@ccoalm/ccl-skills 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +2 -2
  2. package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +8 -7
  3. package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +6 -1
  4. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +16 -17
  5. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/client-routing.md +1 -1
  6. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/manual-invocation-and-prompts.md +6 -0
  7. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +195 -7
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/timeout-auth-and-capabilities.md +3 -3
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/claude_review.sh +13 -5
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/codex_review.sh +9 -3
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/kimi_review.sh +9 -3
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/normalize_review_timeout.sh +22 -0
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/opencode_review.sh +9 -3
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +1540 -129
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_claude_review_probe.sh +8 -3
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +76 -1
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +1858 -3
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_update_review_plan_intent.sh +789 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/update_review_plan_intent.py +513 -0
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/defect-diagnosis/SKILL.md +1 -0
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/feature-risk-router/SKILL.md +3 -1
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +1 -1
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +1 -1
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +4 -1
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/references/state-machine-task-patterns.md +2 -0
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/SKILL.md +2 -1
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/inference-capacity-operations.md +24 -0
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/llm-client-gateway.md +1 -1
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/model-prompt-evaluation.md +4 -1
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +11 -10
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/references/contracts-and-state.md +5 -0
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/SKILL.md +64 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/agents/openai.yaml +4 -0
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +73 -0
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/runtime-and-project-contract.md +58 -0
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/source-map.md +41 -0
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/verification-diagnostics-and-security.md +63 -0
  38. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +3 -2
  39. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/metrics-conventions.md +8 -1
  40. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +2 -2
  41. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/canary-and-rollout-strategy.md +16 -2
  42. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +9 -0
  43. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +14 -16
  44. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/code-review-checklist.md +4 -0
  45. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/delivery-lifecycle.md +1 -1
  46. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/design-routing-and-readiness.md +10 -14
  47. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/rd-standards-doc-family-checklist.md +1 -0
  48. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/verify-developer-experience.md +1 -1
  49. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/SKILL.md +135 -86
  50. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/behavioral-aesthetic-logic.md +66 -80
  51. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/delivery-contract.md +275 -0
  52. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-execution-checklist.md +88 -214
  53. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-impl-naming-and-versioning.md +2 -2
  54. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-intake-and-acceptance.md +10 -8
  55. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +6 -5
  56. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/external-ui-ux-quality-benchmarks.md +112 -95
  57. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/frontend-code-evidence-map.md +30 -21
  58. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/interaction-design-patterns.md +22 -3
  59. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/layout-recipes-and-screenshot-acceptance.md +20 -17
  60. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-project-token-consistency.md +7 -9
  61. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-stack-strategy.md +14 -10
  62. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/operational-processing-workflows.md +2 -0
  63. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +3 -3
  64. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-lifecycle-acceptance-and-iteration.md +9 -6
  65. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-surface-patterns.md +3 -0
  66. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/source-map.md +37 -10
  67. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +8 -1
  68. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +16 -5
  69. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-design-development.md +16 -5
  70. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/visual-craft.md +4 -2
  71. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +1 -1
  72. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +4 -1
  73. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +2 -0
  74. package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +1 -1
  75. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +8 -8
  76. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md +4 -0
  77. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +104 -5
  78. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +11 -9
  79. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/r0-leakage-audit.md +102 -0
  80. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +103 -0
  81. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +20 -0
  82. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/uiux-judgment-extraction.md +6 -6
  83. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +4 -3
  84. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +69 -2
  85. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +22 -0
  86. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb +49 -4
  87. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/obligation-ledger.py +2748 -0
  88. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb +20 -5
  89. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/shared_git_surface_gate.py +1142 -0
  90. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +17 -0
  91. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh +41 -4
  92. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ci_checkout_ref_binding.sh +120 -0
  93. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_entrypoint_domain_scan_terms.sh +82 -8
  94. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +336 -0
  95. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_self_adjudication.sh +82 -10
  96. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger.sh +1416 -0
  97. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger_repo_audit.sh +57 -0
  98. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_wiring.sh +141 -4
  99. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_pointer_integrity.sh +3 -1
  100. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_shared_git_surface_gate.sh +1696 -0
  101. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_delivery_contract.sh +2117 -0
  102. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_loading_budget.sh +316 -0
  103. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +1176 -0
  104. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_skill_cross_refs.sh +31 -1
  105. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate-skill.sh +9 -4
  106. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +980 -0
  107. package/dist/assets/marketplace/plugins/ccl-skills/skills/terminal-cli-dev/SKILL.md +8 -6
  108. package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/classical-test-design-techniques.md +1 -1
  109. package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/tc-review-and-prioritization.md +1 -1
  110. package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/update-lifecycle.md +2 -0
  111. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +16 -15
  112. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/ci-fixtures-and-flake-control.md +5 -1
  113. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/client-runtime-test-matrices.md +10 -2
  114. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/e2e-real-flow-testing.md +2 -2
  115. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/integration-contract-testing.md +10 -0
  116. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +2 -2
  117. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-topology-and-commands.md +1 -1
  118. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +2 -1
  119. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/annotation-driven-revision.md +9 -0
  120. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/figure-and-table-craft.md +8 -2
  121. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +7 -5
  122. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/complex-workspace-patterns.md +1 -1
  123. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/react-architecture.md +3 -0
  124. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-quality-release.md +37 -4
  125. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-ui-quality.md +10 -1
  126. package/dist/assets/release.json +215 -105
  127. package/package.json +1 -1
@@ -0,0 +1,1416 @@
1
+ #!/usr/bin/env bash
2
+ # Killing-mutation contract for the obligation ledger generator/auditor.
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
6
+ TOOL="$SCRIPT_DIR/obligation-ledger.py"
7
+ TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/obligation-ledger-test.XXXXXX")"
8
+ trap 'rm -rf "$TMP_ROOT"' EXIT
9
+
10
+ FIXTURE="$TMP_ROOT/fixture"
11
+ mkdir -p "$FIXTURE/skills/source" "$FIXTURE/skills/destination" \
12
+ "$FIXTURE/skills/extra" "$FIXTURE/skills/matrix" \
13
+ "$FIXTURE/skills/meta/references" "$FIXTURE/specs"
14
+ git -C "$FIXTURE" init -q
15
+ git -C "$FIXTURE" config user.name "Obligation Fixture"
16
+ git -C "$FIXTURE" config user.email "fixture@example.invalid"
17
+
18
+ cat >"$FIXTURE/skills/source/SKILL.md" <<'EOF'
19
+ # Source
20
+ ## Rules
21
+ - The owner MUST retain the release token before final approval.
22
+ - Every reviewer MUST reject a build with a missing audit record.
23
+ - Every client MUST preserve the table-bound release record.
24
+ - Every maintainer MUST preserve the immutable release record.
25
+ - The release record MUST preserve these clauses:owner context must remain visible, every client must expose recovery, and final approval must block unsafe retry.
26
+ - The final skill MUST stay generic.
27
+ - Use only provenance sources.
28
+ - Shared i18n packages remain in scope.
29
+ - For mobile/H5, safe-area behavior MUST be covered.
30
+ - A component library is only rendering vocabulary, not design judgment.
31
+ - User-facing copy uses plain language.
32
+ - Supported actions include post, reply, block, and mute.
33
+ - Remove unused first-screen dead area.
34
+ - Do not use Error 500 as primary copy.
35
+ - Sequence the message as (1) what happened, (2) why, and (3) next action.
36
+ - The request MUST finish within 500 ms.
37
+ - The release MUST remain `pending` until verification.
38
+ - The guide lives at `references/pending-state.md`.
39
+ - Final approval MUST block unsafe retry.
40
+ - Loading MUST keep the geometry of final content.
41
+ - The user MUST confirm the destructive action.
42
+ - The latest design MUST remain accessible.
43
+ - The final artifact MUST retain its audit trail.
44
+ - The failure rate MUST stay below 5%.
45
+ - The retry rate MUST stay under 5%.
46
+ EOF
47
+ cat >"$FIXTURE/skills/destination/SKILL.md" <<'EOF'
48
+ # Destination
49
+ ## Current
50
+ - Existing stable contract remains unchanged for the fixture.
51
+ EOF
52
+ cat >"$FIXTURE/skills/extra/SKILL.md" <<'EOF'
53
+ # Extra
54
+ ## Stable
55
+ - Every client MUST preserve the unchanged comparison-domain contract.
56
+ EOF
57
+ cat >"$FIXTURE/skills/matrix/SKILL.md" <<'EOF'
58
+ # Matrix
59
+ ## Contract table
60
+ | Rule \| identity | Boundary `A|B` |
61
+ | --- | --- |
62
+ | Every owner MUST keep the stable table audit. | Stable boundary. |
63
+ EOF
64
+ cat >"$FIXTURE/skills/meta/references/source-register.md" <<'EOF'
65
+ # Source register
66
+ ## Provenance only
67
+ - External references are evidence inputs and are never the sole normative carrier.
68
+ EOF
69
+ git -C "$FIXTURE" add skills
70
+ git -C "$FIXTURE" commit -qm base
71
+ BASE="$(git -C "$FIXTURE" rev-parse HEAD)"
72
+
73
+ cat >"$FIXTURE/skills/source/SKILL.md" <<'EOF'
74
+ # Source
75
+ ## Rules
76
+ - The release protocol now delegates token retention to the delivery owner.
77
+ - Review policy now delegates missing-audit rejection to the gate owner.
78
+ - The release protocol now delegates the table-bound record to a delivery client.
79
+ - Genericity now follows the reusable skill contract.
80
+ - Provenance selection now follows the source boundary.
81
+ - Localization package scope now follows the rendered-content boundary.
82
+ - Mobile-web safe-area coverage now follows the adaptation matrix.
83
+ - Component-library authority now follows the design-judgment boundary.
84
+ - Visible interface copy now follows the content standard.
85
+ - Moderation actions now follow the interaction vocabulary.
86
+ - Above-the-fold density now follows the visual-craft rule.
87
+ - Raw server-status copy now follows the error-copy rule.
88
+ - Error-message order now follows the recovery-copy pattern.
89
+ - Request timing now follows the runtime threshold.
90
+ - Release status now follows verification state.
91
+ - The pending-state guide path now follows the documentation index.
92
+ - Unsafe retry now follows the approval gate.
93
+ - Loading geometry now follows the terminal-content layout rule.
94
+ - Destructive confirmation now follows the acting-user contract.
95
+ - Current-design accessibility now follows the design baseline.
96
+ - Artifact audit retention now follows the delivery baseline.
97
+ - Failure-rate direction now follows the quality threshold.
98
+ - Retry-rate direction now follows the reliability threshold.
99
+ EOF
100
+ cat >"$FIXTURE/skills/destination/SKILL.md" <<'EOF'
101
+ # Destination
102
+ ## Current
103
+ - Existing stable contract remains unchanged for the fixture.
104
+ ## Delivery contract
105
+ - Legacy first-screen note remains in destination.
106
+ - The delivery owner MUST retain the release token before final approval.
107
+ - Every gate reviewer MUST reject a build with a missing audit record.
108
+ - Every maintainer MUST preserve the immutable release record.
109
+ - The release record MUST preserve these clauses:owner context must remain visible.
110
+ - Every client must expose recovery.
111
+ - Final approval must block unsafe retry.
112
+ - Owner context MUST remain visible.
113
+ - The resulting skill MUST stay generic.
114
+ - None of the non-provenance sources may be used.
115
+ - Shared localization packages remain in scope.
116
+ - For mobile web, safe-area behavior MUST be covered.
117
+ - A component library is rendering vocabulary, not a substitute for design judgment.
118
+ - Visible interface copy uses plain language.
119
+ - Supported actions include posting, replying, blocking, and muting.
120
+ - Remove unused above-the-fold dead area.
121
+ - Do not use raw server status as primary copy.
122
+ - State what happened, why it happened, and the next action.
123
+ - The runtime request MUST complete within 500 ms.
124
+ - The release MUST stay `pending` until verification.
125
+ - Use the guide at `references/pending-state.md`.
126
+ - Final approval MUST block unsafe retry.
127
+ - Loading MUST preserve the geometry of final content.
128
+ - The user MUST confirm the irreversible action.
129
+ - The latest design MUST remain accessible.
130
+ - The final artifact MUST retain its audit trail.
131
+ - The failure rate MUST stay below 5%.
132
+ - The retry rate MUST stay under 5%.
133
+ EOF
134
+ # Same sentence under a different path/chain is legal: the carrier identity is
135
+ # the manifest's exact (path, chain, text) tuple, not a corpus-wide text ban.
136
+ cat >"$FIXTURE/skills/extra/SKILL.md" <<'EOF'
137
+ # Extra
138
+ ## Stable
139
+ - Every client MUST preserve the unchanged comparison-domain contract.
140
+ - The delivery owner MUST retain the release token before final approval.
141
+ EOF
142
+ cat >"$FIXTURE/skills/matrix/SKILL.md" <<'EOF'
143
+ # Matrix
144
+ ## Contract table
145
+ | Rule \| identity | Boundary `A|B` |
146
+ | --- | --- |
147
+ | Every owner MUST keep the stable table audit. | Stable boundary. |
148
+ | Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |
149
+ EOF
150
+ cat >"$FIXTURE/specs/mapping.jsonl" <<'EOF'
151
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":1,"reason":"left-a-rewritten-line","before_text":"The owner MUST retain the release token before final approval.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"strengthened","carrier_path":"skills/destination/SKILL.md","carrier_text":"The delivery owner MUST retain the release token before final approval.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"recency","before":["before","final"],"after":["before","final"],"same_immediate_host":true},{"kind":"actor","before":["owner"],"after":["owner"],"same_immediate_host":true}],"review_note":"Old Rules host loses token retention; new Delivery contract host gains a stricter named delivery owner."}
152
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":2,"reason":"left-a-rewritten-line","before_text":"Every reviewer MUST reject a build with a missing audit record.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"strengthened","carrier_path":"skills/destination/SKILL.md","carrier_text":"Every gate reviewer MUST reject a build with a missing audit record.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"scope","before":["Every"],"after":["Every"],"same_immediate_host":true},{"kind":"actor","before":["reviewer"],"after":["reviewer"],"same_immediate_host":true},{"kind":"consequence","before":["reject"],"after":["reject"],"same_immediate_host":true}],"review_note":"Old Rules host loses audit rejection; new Delivery contract host gains a gate-specific reviewer."}
153
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":3,"reason":"left-a-rewritten-line","before_text":"Every client MUST preserve the table-bound release record.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"strengthened","carrier_path":"skills/matrix/SKILL.md","carrier_text":"Every delivery client MUST preserve the table-bound release record.","carrier_chain":["Matrix","Contract table","table:Rule \\| identity | Boundary `A|B`","column:Rule \\| identity"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"scope","before":["Every"],"after":["Every"],"same_immediate_host":true},{"kind":"actor","before":["client"],"after":["client"],"same_immediate_host":true}],"review_note":"Old Rules host loses the table-bound record; new Contract table Rule column gains the delivery-client contract."}
154
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":4,"reason":"left-a-rewritten-line","before_text":"Every maintainer MUST preserve the immutable release record.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Every maintainer MUST preserve the immutable release record.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":false,"qualifiers":[],"review_note":"The exact obligation moves verbatim from old Rules to the Delivery contract host."}
155
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":5,"reason":"left-a-rewritten-line","before_text":"The release record MUST preserve these clauses:owner context must remain visible, every client must expose recovery, and final approval must block unsafe retry.","before_chain":["Source","Rules"],"disposition":"subsumed","effect":"preserved","carrier_path":null,"carrier_text":null,"carrier_chain":null,"carrier_bundle":[{"carrier_path":"skills/destination/SKILL.md","carrier_text":"The release record MUST preserve these clauses:owner context must remain visible.","carrier_chain":["Destination","Delivery contract"],"covers":["c1"],"clause_qualifiers":[{"clause_id":"c1","qualifiers":[{"kind":"modality","before":["MUST","must"],"after":["MUST","must"],"same_immediate_host":true},{"kind":"actor","before":["owner"],"after":["owner"],"same_immediate_host":true}]}]},{"carrier_path":"skills/destination/SKILL.md","carrier_text":"Every client must expose recovery.","carrier_chain":["Destination","Delivery contract"],"covers":["c2"],"clause_qualifiers":[{"clause_id":"c2","qualifiers":[{"kind":"modality","before":["must"],"after":["must"],"same_immediate_host":true},{"kind":"scope","before":["every"],"after":["Every"],"same_immediate_host":true},{"kind":"actor","before":["client"],"after":["client"],"same_immediate_host":true}]}]},{"carrier_path":"skills/destination/SKILL.md","carrier_text":"Final approval must block unsafe retry.","carrier_chain":["Destination","Delivery contract"],"covers":["c3"],"clause_qualifiers":[{"clause_id":"c3","qualifiers":[{"kind":"modality","before":["must"],"after":["must"],"same_immediate_host":true},{"kind":"recency","before":["final"],"after":["Final"],"same_immediate_host":true},{"kind":"consequence","before":["block"],"after":["block"],"same_immediate_host":true}]}]}],"compound_clauses":[{"id":"c1","text":"The release record MUST preserve these clauses: owner context must remain visible"},{"id":"c2","text":"every client must expose recovery"},{"id":"c3","text":"final approval must block unsafe retry."}],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST","must"],"after":["MUST","must"],"same_immediate_host":true},{"kind":"recency","before":["final"],"after":["Final"],"same_immediate_host":true},{"kind":"scope","before":["every"],"after":["Every"],"same_immediate_host":true},{"kind":"actor","before":["owner","client"],"after":["owner","client"],"same_immediate_host":true},{"kind":"consequence","before":["block"],"after":["block"],"same_immediate_host":true}],"review_note":"Manual clause coverage: c1 preserves owner context, c2 preserves client recovery, and c3 preserves the final unsafe-retry block."}
156
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":6,"reason":"left-a-rewritten-line","before_text":"The final skill MUST stay generic.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"strengthened","carrier_path":"skills/destination/SKILL.md","carrier_text":"The resulting skill MUST stay generic.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true}],"qualifier_relations":[{"kind":"recency","term":"final","occurrence":1,"source_excerpt":"final skill","resolution":"carrier-applies-unconditionally","same_immediate_host":true}],"review_note":"Manual review recorded for the exact final-to-resulting rewrite."}
157
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":7,"reason":"left-a-rewritten-line","before_text":"Use only provenance sources.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"None of the non-provenance sources may be used.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"scope","before":["only"],"after":["None"],"same_immediate_host":true}],"review_note":"The exclusive provenance-source boundary is preserved by the negative universal form."}
158
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":8,"reason":"left-a-rewritten-line","before_text":"Shared i18n packages remain in scope.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Shared localization packages remain in scope.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[],"review_note":"The localization-package scope is rephrased without treating identifier digits as a threshold."}
159
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":9,"reason":"left-a-rewritten-line","before_text":"For mobile/H5, safe-area behavior MUST be covered.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"For mobile web, safe-area behavior MUST be covered.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true}],"review_note":"The mobile-web safe-area obligation is preserved without treating the H5 identifier as a threshold."}
160
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":10,"reason":"left-a-rewritten-line","before_text":"A component library is only rendering vocabulary, not design judgment.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"A component library is rendering vocabulary, not a substitute for design judgment.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"scope","before":["only"],"after":["not a substitute"],"same_immediate_host":true}],"review_note":"The exclusive rendering-vocabulary boundary is preserved by the explicit not-a-substitute expression."}
161
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":11,"reason":"left-a-rewritten-line","before_text":"User-facing copy uses plain language.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Visible interface copy uses plain language.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[],"review_note":"User-facing is an audience modifier here, not an acting-role qualifier."}
162
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":12,"reason":"left-a-rewritten-line","before_text":"Supported actions include post, reply, block, and mute.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Supported actions include posting, replying, blocking, and muting.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[],"review_note":"Block is an enumerated product action noun here, not a completion consequence."}
163
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":13,"reason":"left-a-rewritten-line","before_text":"Remove unused first-screen dead area.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Remove unused above-the-fold dead area.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[],"review_note":"First-screen is a visual-region compound here, not a temporal ordering qualifier."}
164
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":14,"reason":"left-a-rewritten-line","before_text":"Do not use Error 500 as primary copy.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Do not use raw server status as primary copy.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["Do not"],"after":["Do not"],"same_immediate_host":true}],"review_note":"The error-copy prohibition is preserved without treating a status code as a threshold."}
165
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":15,"reason":"left-a-rewritten-line","before_text":"Sequence the message as (1) what happened, (2) why, and (3) next action.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"State what happened, why it happened, and the next action.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[],"review_note":"Parenthesized list ordinals are structural markers, not numeric thresholds."}
166
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":16,"reason":"left-a-rewritten-line","before_text":"The request MUST finish within 500 ms.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The runtime request MUST complete within 500 ms.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"threshold","before":["within","500 ms"],"after":["within","500 ms"],"same_immediate_host":true}],"review_note":"The real bounded runtime threshold is preserved with its unit."}
167
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":17,"reason":"left-a-rewritten-line","before_text":"The release MUST remain `pending` until verification.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The release MUST stay `pending` until verification.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"consequence","before":["pending"],"after":["pending"],"same_immediate_host":true}],"review_note":"The true pending terminal status remains explicit."}
168
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":18,"reason":"left-a-rewritten-line","before_text":"The guide lives at `references/pending-state.md`.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Use the guide at `references/pending-state.md`.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[],"review_note":"Pending inside a path identifier is not a terminal-status qualifier."}
169
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":19,"reason":"left-a-rewritten-line","before_text":"Final approval MUST block unsafe retry.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Final approval MUST block unsafe retry.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":false,"qualifiers":[],"review_note":"The true final approval blocker is preserved verbatim under the delivery contract."}
170
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":20,"reason":"left-a-rewritten-line","before_text":"Loading MUST keep the geometry of final content.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"Loading MUST preserve the geometry of final content.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"recency","before":["final"],"after":["final"],"same_immediate_host":true}],"review_note":"Final content is a true terminal-state comparison and remains explicit."}
171
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":21,"reason":"left-a-rewritten-line","before_text":"The user MUST confirm the destructive action.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The user MUST confirm the irreversible action.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":true,"qualifiers":[{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":true},{"kind":"actor","before":["user"],"after":["user"],"same_immediate_host":true}],"review_note":"The acting user and mandatory confirmation remain explicit."}
172
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":22,"reason":"left-a-rewritten-line","before_text":"The latest design MUST remain accessible.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The latest design MUST remain accessible.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":false,"qualifiers":[],"review_note":"The latest-design recency constraint moves verbatim."}
173
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":23,"reason":"left-a-rewritten-line","before_text":"The final artifact MUST retain its audit trail.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The final artifact MUST retain its audit trail.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":false,"qualifiers":[],"review_note":"The final-artifact terminal constraint moves verbatim."}
174
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":24,"reason":"left-a-rewritten-line","before_text":"The failure rate MUST stay below 5%.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The failure rate MUST stay below 5%.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":false,"qualifiers":[],"review_note":"The lower failure-rate bound moves verbatim."}
175
+ {"schema_version":2,"source_path":"skills/source/SKILL.md","source_ordinal":25,"reason":"left-a-rewritten-line","before_text":"The retry rate MUST stay under 5%.","before_chain":["Source","Rules"],"disposition":"rehosted","effect":"preserved","carrier_path":"skills/destination/SKILL.md","carrier_text":"The retry rate MUST stay under 5%.","carrier_chain":["Destination","Delivery contract"],"manual_reviewed":false,"qualifiers":[],"review_note":"The lower retry-rate bound moves verbatim."}
176
+ EOF
177
+
178
+ # Schema 3 makes semantic review evidence explicit. Rephrased scalar carriers
179
+ # need one reviewed decision; each bundle clause needs its own reviewed decision.
180
+ python3 - "$FIXTURE/specs/mapping.jsonl" <<'PY'
181
+ import hashlib
182
+ import json
183
+ import sys
184
+ from pathlib import Path
185
+
186
+ path = Path(sys.argv[1])
187
+ rows = [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line]
188
+ for row in rows:
189
+ row["schema_version"] = 3
190
+ row["semantic_review"] = None
191
+ row["semantic_rationale"] = None
192
+ bundle = row.get("carrier_bundle")
193
+ if bundle:
194
+ for member in bundle:
195
+ for proof in member["clause_qualifiers"]:
196
+ proof["semantic_review"] = "reviewed"
197
+ proof["semantic_rationale"] = (
198
+ f"Reviewed clause {proof['clause_id']} against its one assigned carrier."
199
+ )
200
+ elif row.get("carrier_text") is not None and row["before_text"] != row["carrier_text"]:
201
+ row["semantic_review"] = "reviewed"
202
+ row["semantic_rationale"] = "Reviewed the non-verbatim scalar carrier against the source obligation."
203
+
204
+ def digest(path, chain, text):
205
+ payload = json.dumps(
206
+ [path, chain, text], ensure_ascii=False, separators=(",", ":")
207
+ ).encode("utf-8")
208
+ return hashlib.sha256(payload).hexdigest()
209
+
210
+ def member(text, bridges):
211
+ path = "skills/destination/SKILL.md"
212
+ chain = ["Destination", "Delivery contract"]
213
+ return {
214
+ "carrier_path": path,
215
+ "carrier_text": text,
216
+ "carrier_chain": chain,
217
+ "carrier_sha256": digest(path, chain, text),
218
+ "bridge_terms": bridges,
219
+ }
220
+
221
+ def partition_row(row, spans):
222
+ cursor = 0
223
+ parts = []
224
+ for index, spec in enumerate(spans, 1):
225
+ text = spec[1]
226
+ start = cursor
227
+ end = start + len(text)
228
+ cursor = end
229
+ if spec[0] == "retired":
230
+ parts.append({
231
+ "id": f"p{index}", "status": "retired",
232
+ "source_start": start, "source_end": end, "source_text": text,
233
+ "authority": "Independent fixture review of the obsolete source-specific literal.",
234
+ "scope": "Only this exact retired span in this source obligation.",
235
+ "reason": "The fixed source-specific literal is invalid under the current generic contract.",
236
+ "semantic_review": "reviewed",
237
+ "semantic_rationale": "Reviewed this exact span as retired while adjacent behavior survives.",
238
+ })
239
+ else:
240
+ carrier_text, bridges, qualifiers = spec[2], spec[3], spec[4]
241
+ part_effect = spec[5] if len(spec) > 5 else "preserved"
242
+ parts.append({
243
+ "id": f"p{index}", "status": "survives",
244
+ "source_start": start, "source_end": end, "source_text": text,
245
+ "effect": part_effect, "carriers": [member(carrier_text, bridges)],
246
+ "qualifiers": qualifiers,
247
+ "qualifier_resolutions": [],
248
+ "semantic_review": "reviewed",
249
+ "semantic_rationale": "Reviewed this exact live span against the exact current carrier.",
250
+ })
251
+ assert cursor == len(row["before_text"]), (cursor, row["before_text"])
252
+ return {
253
+ "schema_version": 4,
254
+ "source_path": row["source_path"],
255
+ "source_ordinal": row["source_ordinal"],
256
+ "reason": row["reason"],
257
+ "before_text": row["before_text"],
258
+ "before_chain": row["before_chain"],
259
+ "disposition": "partial-retirement",
260
+ "effect": "retired",
261
+ "parts": parts,
262
+ "manual_reviewed": True,
263
+ "semantic_review": "reviewed",
264
+ "semantic_rationale": "Reviewed the complete exact-span partition and its live/dead boundary.",
265
+ "review_note": "Schema4 fixture partition with exact carriers and authority-scoped retirement.",
266
+ }
267
+
268
+ rows[12] = partition_row(rows[12], [
269
+ ("survives", "Remove unused ", "Remove unused above-the-fold dead area.", ["Remove", "unused"], []),
270
+ ("retired", "first-screen"),
271
+ ("survives", " dead area.", "Remove unused above-the-fold dead area.", ["dead area"], []),
272
+ ])
273
+ rows[13] = partition_row(rows[13], [
274
+ (
275
+ "survives", "Do not use ", "Do not use raw server status as primary copy.",
276
+ ["Do not", "use"],
277
+ [{"kind":"modality","before":["Do not"],"after":["Do not"],"same_immediate_host":True}],
278
+ "strengthened",
279
+ ),
280
+ ("retired", "Error 500"),
281
+ (
282
+ "survives", " as primary copy.", "Do not use raw server status as primary copy.",
283
+ ["primary copy"], [],
284
+ ),
285
+ ])
286
+ path.write_text(
287
+ "".join(json.dumps(row, separators=(",", ":")) + "\n" for row in rows),
288
+ encoding="utf-8",
289
+ )
290
+ PY
291
+
292
+ python3 - "$TOOL" <<'PY'
293
+ import importlib.util
294
+ import sys
295
+ from pathlib import Path
296
+
297
+ tool = Path(sys.argv[1])
298
+ spec = importlib.util.spec_from_file_location("obligation_ledger_under_test", tool)
299
+ module = importlib.util.module_from_spec(spec)
300
+ sys.modules[spec.name] = module
301
+ spec.loader.exec_module(module)
302
+
303
+ cases = [
304
+ ("only-is-exclusive-scope", "Use only provenance sources.", {"scope": ["only"]}),
305
+ ("only-chinese-is-exclusive-scope", "该来源仅用于证据。", {"scope": ["仅"]}),
306
+ ("not-only-is-additive", "This is not only a layout concern.", {}),
307
+ ("not-markdown-only-is-additive", "This is not **only** a layout concern.", {}),
308
+ ("chinese-not-only-is-additive", "这不仅是布局问题。", {}),
309
+ ("chinese-not-only-emphatic-is-additive", "这不仅仅是布局问题。", {}),
310
+ ("exclusive-cross-expression", "A library is not a substitute for design judgment.", {"scope": ["not a substitute"]}),
311
+ ("i18n-is-identifier", "Shared i18n packages remain in scope.", {}),
312
+ ("h5-is-identifier", "For mobile/H5, safe-area behavior MUST be covered.", {"modality": ["MUST"]}),
313
+ ("user-facing-is-modifier", "User-facing copy uses plain language.", {}),
314
+ ("user-copy-is-modifier", "User copy uses plain language.", {}),
315
+ ("user-needs-is-noun-modifier", "User needs guide prioritization.", {}),
316
+ ("users-possessive-needs-is-noun", "Users' needs guide prioritization.", {}),
317
+ ("user-experience-research-is-modifier", "User experience research informs the design.", {}),
318
+ ("user-research-is-noun-modifier", "User research informs the design.", {}),
319
+ ("chinese-user-recipient", "让用户知道下一步。", {}),
320
+ ("chinese-user-needs-is-noun", "用户需求影响设计方向。", {}),
321
+ ("chinese-possessive-user-needs-is-noun", "用户的需求影响设计方向。", {}),
322
+ ("real-user-actor", "The user MUST confirm.", {"modality": ["MUST"], "actor": ["user"]}),
323
+ ("real-users-actor", "Users MUST confirm.", {"modality": ["MUST"], "actor": ["Users"]}),
324
+ ("agent-contract-is-modifier", "Inspect agent-contract files before use.", {"recency": ["before"]}),
325
+ ("reviewer-check-is-modifier", "Reviewer checks remain enumerable.", {}),
326
+ ("real-user-needs-to-actor", "The user needs to confirm.", {"actor": ["user"]}),
327
+ ("real-chinese-user-actor", "用户必须确认。", {"modality": ["必须"], "actor": ["用户"]}),
328
+ ("real-chinese-user-actor-after-let", "让用户确认操作。", {"actor": ["用户"]}),
329
+ ("chinese-test-is-verb", "必须测试错误状态。", {"modality": ["必须"]}),
330
+ ("chinese-tester-is-actor", "测试人员必须确认结果。", {"modality": ["必须"], "actor": ["测试人员"]}),
331
+ ("block-is-action-noun", "Actions include post, reply, block, and mute.", {}),
332
+ ("code-block-is-noun", "Render the sample in a code block.", {}),
333
+ ("code-blocks-is-noun", "Render samples in code blocks.", {}),
334
+ ("block-is-consequence", "The gate MUST block completion.", {"modality": ["MUST"], "consequence": ["block"]}),
335
+ ("blocks-is-consequence", "The gate blocks completion.", {"consequence": ["blocks"]}),
336
+ ("blocked-is-consequence", "The release is blocked.", {"consequence": ["blocked"]}),
337
+ ("reviewer-block-is-consequence", "A reviewer can directly block the change.", {"actor": ["reviewer"], "consequence": ["block"]}),
338
+ ("pass-fail-is-label", "Do not use a pass/fail summary.", {"modality": ["Do not"]}),
339
+ ("fail-is-consequence", "The gate MUST fail closed.", {"modality": ["MUST"], "consequence": ["fail"]}),
340
+ ("failed-is-consequence", "The verification failed.", {"consequence": ["failed"]}),
341
+ ("red-text-is-color", "Use red text for the error.", {}),
342
+ ("red-colored-border-is-color", "Use a red-colored border for the error.", {}),
343
+ ("red-first-is-baseline-label", "Record a RED-first focused assertion.", {"recency": ["first"]}),
344
+ ("red-baseline-is-label", "The RED baseline names its provenance.", {}),
345
+ ("red-gate-is-consequence", "The gate is red.", {"consequence": ["red"]}),
346
+ ("first-screen-is-region", "Remove unused first-screen dead area.", {}),
347
+ ("final-skill-remains-reviewed-recency", "The final skill MUST stay generic.", {"modality": ["MUST"], "recency": ["final"]}),
348
+ ("final-content-remains-terminal", "Keep the geometry of final content.", {"recency": ["final"]}),
349
+ ("error-status-is-not-threshold", "Do not use Error 500 as primary copy.", {"modality": ["Do not"]}),
350
+ ("http-status-is-not-threshold", "HTTP 500 is not primary copy.", {}),
351
+ ("numbered-list-is-not-threshold", "Use (1) what happened, (2) why, and (3) next action.", {}),
352
+ ("identifier-path-is-not-terminal", "Read `references/pending-state.md`.", {}),
353
+ ("pending-is-terminal", "The release MUST remain `pending`.", {"modality": ["MUST"], "consequence": ["pending"]}),
354
+ ("within-unit-is-threshold", "Finish within 500 ms.", {"threshold": ["within", "500 ms"]}),
355
+ ("within-compact-unit-is-threshold", "Finish within 500ms.", {"threshold": ["within", "500ms"]}),
356
+ ("within-word-unit-is-threshold", "Finish within five seconds.", {"threshold": ["within"]}),
357
+ ("within-product-is-not-threshold", "Keep tokens aligned within one product.", {}),
358
+ ("percent-is-threshold", "Keep failures below 5%.", {"threshold": ["below", "5%"]}),
359
+ ("under-percent-is-threshold", "Keep failures under 5%.", {"threshold": ["under", "5%"]}),
360
+ ("above-percent-is-threshold", "Keep success above 5%.", {"threshold": ["above", "5%"]}),
361
+ ("over-percent-is-threshold", "Keep success over 5%.", {"threshold": ["over", "5%"]}),
362
+ ("under-review-is-not-threshold", "The proposal is under review.", {}),
363
+ ("over-time-is-not-threshold", "Improve the design over time.", {}),
364
+ ("per-the-is-cross-reference", "Handle it per the classes above.", {}),
365
+ ("above-the-fold-is-not-threshold", "Remove unused above-the-fold area.", {}),
366
+ ("comparator-is-threshold", "Keep at least 3 samples.", {"threshold": ["at least"]}),
367
+ ("dimension-is-threshold", "Use a 44×44 dp target.", {"threshold": ["44×44 dp"]}),
368
+ ("ratio-is-threshold", "Keep contrast at 4.5:1.", {"threshold": ["4.5:1"]}),
369
+ ("inline-code-number-is-literal", "Use `500ms` as the example literal.", {}),
370
+ ("path-number-is-literal", "Read references/500ms.md for details.", {}),
371
+ ("parenthesized-path-number-is-literal", "Read (references/500ms.md) for details.", {}),
372
+ ("quoted-percent-is-literal", "The sample literal is \"5%\".", {}),
373
+ ]
374
+
375
+ failures = []
376
+ for name, text, expected in cases:
377
+ actual = module.qualifier_terms(text)
378
+ if actual != expected:
379
+ failures.append(f"{name}: expected={expected!r} actual={actual!r}")
380
+
381
+ bundle_cases = [
382
+ (
383
+ "bundle-clause-action-is-local",
384
+ "Preserve the audit record.",
385
+ "Expose the audit record.",
386
+ [],
387
+ "BUNDLE_CLAUSE_ACTION_MISSING",
388
+ ),
389
+ (
390
+ "bundle-clause-actor-is-local",
391
+ "Every client MUST expose recovery.",
392
+ "Every owner MUST expose recovery.",
393
+ [
394
+ {"kind": "modality", "before": ["MUST"], "after": ["MUST"], "same_immediate_host": True},
395
+ {"kind": "scope", "before": ["Every"], "after": ["Every"], "same_immediate_host": True},
396
+ {"kind": "actor", "before": ["client"], "after": ["owner"], "same_immediate_host": True},
397
+ ],
398
+ "BUNDLE_CLAUSE_ACTOR_MISSING",
399
+ ),
400
+ (
401
+ "bundle-clause-modality-is-local",
402
+ "The client MUST expose recovery.",
403
+ "The client may expose recovery.",
404
+ [
405
+ {"kind": "modality", "before": ["MUST"], "after": ["may"], "same_immediate_host": True},
406
+ {"kind": "actor", "before": ["client"], "after": ["client"], "same_immediate_host": True},
407
+ ],
408
+ "BUNDLE_CLAUSE_MODALITY_MISSING",
409
+ ),
410
+ (
411
+ "bundle-clause-consequence-is-local",
412
+ "The release remains pending.",
413
+ "The release remains queued.",
414
+ [
415
+ {"kind": "consequence", "before": ["pending"], "after": [], "same_immediate_host": True},
416
+ ],
417
+ "BUNDLE_CLAUSE_CONSEQUENCE_MISSING",
418
+ ),
419
+ ]
420
+ for name, before, after, claimed, expected_code in bundle_cases:
421
+ try:
422
+ module.validate_bundle_clause_proof(before, after, claimed, name)
423
+ except module.AuditError as exc:
424
+ if exc.code != expected_code:
425
+ failures.append(f"{name}: expected_code={expected_code} actual_code={exc.code}")
426
+ else:
427
+ failures.append(f"{name}: expected_code={expected_code} but proof passed")
428
+
429
+ threshold_direction_cases = [
430
+ ("below-to-above-is-reversed", "Keep failures below 5%.", "Keep failures above 5%."),
431
+ ("under-to-over-is-reversed", "Keep failures under 5%.", "Keep failures over 5%."),
432
+ ]
433
+ for name, before, after in threshold_direction_cases:
434
+ try:
435
+ module.ensure_qualifier_strength(before, after, name)
436
+ except module.AuditError as exc:
437
+ if exc.code != "BUNDLE_QUALIFIER_REVERSED":
438
+ failures.append(
439
+ f"{name}: expected_code=BUNDLE_QUALIFIER_REVERSED actual_code={exc.code}"
440
+ )
441
+ else:
442
+ failures.append(f"{name}: reversed threshold direction passed")
443
+ if module.HARD_MODALITY.search("Use only provenance sources."):
444
+ failures.append("only-is-hard-modality: unexpected HARD_MODALITY match")
445
+ if module.HARD_MODALITY.search("该来源仅用于证据。"):
446
+ failures.append("only-chinese-is-hard-modality: unexpected HARD_MODALITY match")
447
+
448
+ def resolution_probe(name, before, after, resolution, chain=("Delivery Contract",)):
449
+ carrier = module.Carrier("skills/destination/SKILL.md", after, chain, 1, 1)
450
+ try:
451
+ module.ensure_partition_qualifier_strength(
452
+ before, after, name, [resolution], [carrier]
453
+ )
454
+ except module.AuditError as exc:
455
+ failures.append(f"{name}: unexpected {exc.code}: {exc.detail}")
456
+
457
+ resolution_probe(
458
+ "implicit-hard-normative",
459
+ "The baseline must name its provenance type.",
460
+ "Every slice records a falsifiable baseline.",
461
+ {"kind":"modality","before":"must","resolution":"implicit-normative"},
462
+ )
463
+ resolution_probe(
464
+ "per-to-each",
465
+ "Handle per class.",
466
+ "Classify each difference.",
467
+ {"kind":"scope","before":"per","resolution":"universal-member"},
468
+ )
469
+ resolution_probe(
470
+ "actor-number",
471
+ "Users know the result.",
472
+ "The user knows the result.",
473
+ {"kind":"actor","before":"Users","resolution":"singular-plural"},
474
+ )
475
+ resolution_probe(
476
+ "square-dimension",
477
+ "Use a 44pt target.",
478
+ "Use a 44×44pt target.",
479
+ {"kind":"threshold","before":"44pt","resolution":"square-dimension"},
480
+ )
481
+ resolution_probe(
482
+ "precondition-completion-boundary",
483
+ "Run the recorded consumer check before closing on API evidence.",
484
+ "A value may leave this contract only after consumer proof establishes the universe and every member is checked.",
485
+ {"kind":"recency","before":"before","resolution":"precondition-completion-boundary"},
486
+ )
487
+ resolution_probe(
488
+ "invalid-closure-hard-prohibition",
489
+ "Backend-only closure without the recorded consumer check is invalid.",
490
+ "A value is outside this contract only after the consumer universe is checked or proves it cannot affect any client.",
491
+ {"kind":"consequence","before":"invalid","resolution":"hard-prohibition"},
492
+ )
493
+ try:
494
+ module.ensure_partition_qualifier_strength(
495
+ "The baseline must name its provenance type.",
496
+ "The baseline should name its provenance type.",
497
+ "soft-is-not-hard",
498
+ [{"kind":"modality","before":"must","resolution":"implicit-normative"}],
499
+ [module.Carrier("skills/destination/SKILL.md", "The baseline should name its provenance type.", ("Delivery Contract",), 1, 1)],
500
+ )
501
+ failures.append("soft-is-not-hard: weakened modality passed")
502
+ except module.AuditError as exc:
503
+ if exc.code != "PART_QUALIFIER_RESOLUTION_INVALID":
504
+ failures.append(f"soft-is-not-hard: wrong error {exc.code}")
505
+ for name, before, after, resolution in [
506
+ (
507
+ "after-without-checked-proof-is-not-precondition",
508
+ "Run the recorded consumer check before closing on API evidence.",
509
+ "The change may close after a repository search.",
510
+ {"kind":"recency","before":"before","resolution":"precondition-completion-boundary"},
511
+ ),
512
+ (
513
+ "advisory-close-is-not-invalid-prohibition",
514
+ "Backend-only closure without the recorded consumer check is invalid.",
515
+ "Only a backend value may leave the contract.",
516
+ {"kind":"consequence","before":"invalid","resolution":"hard-prohibition"},
517
+ ),
518
+ ]:
519
+ try:
520
+ module.ensure_partition_qualifier_strength(
521
+ before,
522
+ after,
523
+ name,
524
+ [resolution],
525
+ [module.Carrier("skills/destination/SKILL.md", after, ("Delivery Contract",), 1, 1)],
526
+ )
527
+ failures.append(f"{name}: invalid semantic relation passed")
528
+ except module.AuditError as exc:
529
+ if exc.code != "PART_QUALIFIER_RESOLUTION_INVALID":
530
+ failures.append(f"{name}: wrong error {exc.code}")
531
+ if failures:
532
+ print("FAIL qualifier tokenizer context contract", file=sys.stderr)
533
+ print("\n".join(failures), file=sys.stderr)
534
+ raise SystemExit(1)
535
+ print(
536
+ "PASS qualifier tokenizer/context, threshold direction, and clause lexical guards "
537
+ f"({len(cases)} tokenizer cases, {len(threshold_direction_cases)} direction probes, "
538
+ f"{len(bundle_cases)} clause probes)"
539
+ )
540
+ PY
541
+
542
+ python3 "$TOOL" render --repo "$FIXTURE" --base "$BASE" \
543
+ --mapping "$FIXTURE/specs/mapping.jsonl" --output "$FIXTURE/specs/ledger.md"
544
+ grep -Fq 'The sibling `obligation-mapping.jsonl` is the canonical proof source' \
545
+ "$FIXTURE/specs/ledger.md"
546
+ grep -Fq '[c1]' "$FIXTURE/specs/ledger.md"
547
+ grep -Fq 'relations=1' "$FIXTURE/specs/ledger.md"
548
+ grep -Fq 'proof_mode=exact-mechanical' "$FIXTURE/specs/ledger.md"
549
+ # Mixed partial-retirement rows close row-level as retired; part-level counts
550
+ # must keep both dimensions visible (rows 13/14: 3 preserved + 1 strengthened
551
+ # surviving parts, 2 retired parts).
552
+ grep -Fq 'survived-preserved=3, survived-strengthened=1, retired=2' \
553
+ "$FIXTURE/specs/ledger.md"
554
+ grep -Fq 'proof_mode=reviewed-semantic' "$FIXTURE/specs/ledger.md"
555
+ grep -Fq 'validates review evidence presence and shape, not the truth' \
556
+ "$FIXTURE/specs/ledger.md"
557
+ if grep -Fq 'The owner MUST retain the release token before final approval.' \
558
+ "$FIXTURE/specs/ledger.md"; then
559
+ echo "FAIL: compact ledger duplicated canonical before/carrier text" >&2
560
+ exit 1
561
+ fi
562
+ audit_output="$(python3 "$TOOL" audit --repo "$FIXTURE" --base "$BASE" \
563
+ --mapping "$FIXTURE/specs/mapping.jsonl" --ledger "$FIXTURE/specs/ledger.md" 2>&1)"
564
+ printf '%s\n' "$audit_output" | grep -Fq 'exact_mechanical='
565
+ printf '%s\n' "$audit_output" | grep -Fq 'reviewed_semantic='
566
+ git -C "$FIXTURE" add .
567
+ git -C "$FIXTURE" commit -qm candidate
568
+
569
+ # A pinned --head freezes the comparison domain at the landing commit: a
570
+ # later, unrelated obligation rewrite must not add rows to a frozen ledger
571
+ # (unpinned it does — that asymmetry is what this case pins).
572
+ CANDIDATE_HEAD="$(git -C "$FIXTURE" rev-parse HEAD)"
573
+ python3 "$TOOL" render --repo "$FIXTURE" --base "$BASE" --head "$CANDIDATE_HEAD" \
574
+ --mapping "$FIXTURE/specs/mapping.jsonl" --output "$FIXTURE/specs/ledger-pinned.md" 2>/dev/null
575
+ python3 - "$FIXTURE/skills/destination/SKILL.md" <<'PY'
576
+ import sys
577
+ from pathlib import Path
578
+
579
+ path = Path(sys.argv[1])
580
+ text = path.read_text(encoding="utf-8")
581
+ updated = text.replace(
582
+ "- Existing stable contract remains unchanged for the fixture.",
583
+ "- Existing stable contract now waits for the fixture owner.",
584
+ )
585
+ assert updated != text
586
+ path.write_text(updated, encoding="utf-8")
587
+ PY
588
+ pinned_audit="$(python3 "$TOOL" audit --repo "$FIXTURE" --base "$BASE" --head "$CANDIDATE_HEAD" \
589
+ --mapping "$FIXTURE/specs/mapping.jsonl" --ledger "$FIXTURE/specs/ledger-pinned.md" 2>&1)" \
590
+ || { printf 'FAIL pinned-head audit went red on a post-head rewrite:\n%s\n' "$pinned_audit" >&2; exit 1; }
591
+ printf '%s\n' "$pinned_audit" | grep -Fq 'audit_ok' \
592
+ || { printf 'FAIL pinned-head audit missing audit_ok:\n%s\n' "$pinned_audit" >&2; exit 1; }
593
+ if unpinned_audit="$(python3 "$TOOL" audit --repo "$FIXTURE" --base "$BASE" \
594
+ --mapping "$FIXTURE/specs/mapping.jsonl" --ledger "$FIXTURE/specs/ledger.md" 2>&1)"; then
595
+ printf 'FAIL unpinned audit must demand a row for the post-head rewrite:\n%s\n' "$unpinned_audit" >&2
596
+ exit 1
597
+ fi
598
+ printf '%s\n' "$unpinned_audit" | grep -Fq 'ROW_SET_MISMATCH' \
599
+ || { printf 'FAIL unpinned audit failed for the wrong reason:\n%s\n' "$unpinned_audit" >&2; exit 1; }
600
+ git -C "$FIXTURE" checkout -q -- skills/destination/SKILL.md
601
+ echo "PASS pinned-head domain freeze"
602
+
603
+ mutate_jsonl() {
604
+ local file="$1" expression="$2"
605
+ python3 - "$file" "$expression" <<'PY'
606
+ import json
607
+ import sys
608
+ from pathlib import Path
609
+
610
+ path = Path(sys.argv[1])
611
+ expression = sys.argv[2]
612
+ rows = [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line]
613
+ exec(expression, {"rows": rows})
614
+ path.write_text("".join(json.dumps(row, separators=(",", ":")) + "\n" for row in rows), encoding="utf-8")
615
+ PY
616
+ }
617
+
618
+ mutation_error_matches() {
619
+ local expected_code="$1" expected_target="$2" output="$3"
620
+ printf '%s\n' "$output" | grep -q "^ERROR $expected_code:" && \
621
+ printf '%s\n' "$output" | grep -Fq "$expected_target"
622
+ }
623
+
624
+ if mutation_error_matches \
625
+ QUALIFIER_REVERSED 'skills/source/SKILL.md#24' \
626
+ 'ERROR QUALIFIER_REVERSED: skills/source/SKILL.md#25: threshold'; then
627
+ echo "FAIL mutation target attribution: unrelated same-code target was accepted" >&2
628
+ exit 1
629
+ fi
630
+ echo "PASS mutation target attribution rejects unrelated same-code failures"
631
+
632
+ run_mutant() {
633
+ local name="$1" expected_code="$2" expected_target="$3" expected_delta="$4"
634
+ shift 4
635
+ local case_dir="$TMP_ROOT/$name" control_output output status actual_delta
636
+ git clone -q "$FIXTURE" "$case_dir"
637
+ control_output="$(python3 "$TOOL" audit --repo "$case_dir" --base "$BASE" \
638
+ --mapping "$case_dir/specs/mapping.jsonl" --ledger "$case_dir/specs/ledger.md" 2>&1)" || {
639
+ echo "FAIL $name: unmutated control was not green: $control_output" >&2
640
+ exit 1
641
+ }
642
+ "$@" "$case_dir"
643
+ if git -C "$case_dir" diff --quiet; then
644
+ echo "FAIL $name: mutation precondition produced no tracked delta" >&2
645
+ exit 1
646
+ fi
647
+ actual_delta="$(git -C "$case_dir" diff --name-only | LC_ALL=C sort | paste -sd, -)"
648
+ if [ "$actual_delta" != "$expected_delta" ]; then
649
+ echo "FAIL $name: expected exact delta $expected_delta, got $actual_delta" >&2
650
+ exit 1
651
+ fi
652
+ if printf '%s\n' "$actual_delta" | grep -Fq 'specs/mapping.jsonl' && \
653
+ printf '%s\n' "$expected_target" | grep -q '^skills/source/.*#[0-9]'; then
654
+ local changed_rows expected_row
655
+ expected_row="${expected_target%%/member-*}"
656
+ expected_row="${expected_row%%/part-*}"
657
+ changed_rows="$(python3 - "$case_dir" <<'PY'
658
+ import json
659
+ import subprocess
660
+ import sys
661
+ from pathlib import Path
662
+
663
+ repo = Path(sys.argv[1])
664
+ before = subprocess.run(
665
+ ["git", "-C", str(repo), "show", "HEAD:specs/mapping.jsonl"],
666
+ check=True,
667
+ capture_output=True,
668
+ text=True,
669
+ ).stdout
670
+ after = (repo / "specs/mapping.jsonl").read_text(encoding="utf-8")
671
+
672
+ def keyed(raw):
673
+ rows = [json.loads(line) for line in raw.splitlines() if line]
674
+ return {
675
+ f"{row['source_path']}#{row['source_ordinal']}": row
676
+ for row in rows
677
+ }
678
+
679
+ old = keyed(before)
680
+ new = keyed(after)
681
+ print(",".join(sorted(key for key in old.keys() | new.keys() if old.get(key) != new.get(key))))
682
+ PY
683
+ )"
684
+ if [ "$changed_rows" != "$expected_row" ]; then
685
+ echo "FAIL $name: expected single mapping-row delta $expected_row, got $changed_rows" >&2
686
+ exit 1
687
+ fi
688
+ fi
689
+ set +e
690
+ output="$(python3 "$TOOL" audit --repo "$case_dir" --base "$BASE" \
691
+ --mapping "$case_dir/specs/mapping.jsonl" --ledger "$case_dir/specs/ledger.md" 2>&1)"
692
+ status=$?
693
+ set -e
694
+ if [ "$status" -eq 0 ]; then
695
+ echo "FAIL $name: mutant survived" >&2
696
+ exit 1
697
+ fi
698
+ if ! mutation_error_matches "$expected_code" "$expected_target" "$output"; then
699
+ echo "FAIL $name: expected first code $expected_code at $expected_target, got: $output" >&2
700
+ exit 1
701
+ fi
702
+ echo "PASS $name -> first_error=$expected_code target=$expected_target delta=$actual_delta"
703
+ }
704
+
705
+ mutation_missing_row() {
706
+ sed -i.bak '2d' "$1/specs/mapping.jsonl"
707
+ rm "$1/specs/mapping.jsonl.bak"
708
+ }
709
+
710
+ mutation_duplicate_carrier() {
711
+ mutate_jsonl "$1/specs/mapping.jsonl" \
712
+ 'rows[0]["carriers"] = [{"path": rows[0]["carrier_path"], "text": rows[0]["carrier_text"], "chain": rows[0]["carrier_chain"]}, {"path": "skills/extra/SKILL.md", "text": "Every client MUST preserve the unchanged comparison-domain contract.", "chain": ["Extra", "Stable"]}]'
713
+ }
714
+
715
+ mutation_weaken_modality() {
716
+ sed -i.bak 's/delivery owner MUST retain/delivery owner may retain/' "$1/skills/destination/SKILL.md"
717
+ rm "$1/skills/destination/SKILL.md.bak"
718
+ mutate_jsonl "$1/specs/mapping.jsonl" \
719
+ 'rows[0]["carrier_text"] = rows[0]["carrier_text"].replace("MUST", "may"); rows[0]["qualifiers"][0]["after"] = ["may"]'
720
+ }
721
+
722
+ mutation_wrong_parent() {
723
+ sed -i.bak 's/## Delivery contract/## Advisory delivery notes/' "$1/skills/destination/SKILL.md"
724
+ rm "$1/skills/destination/SKILL.md.bak"
725
+ }
726
+
727
+ mutation_reverse_recency() {
728
+ sed -i.bak 's/before final approval/after initial approval/' "$1/skills/destination/SKILL.md"
729
+ rm "$1/skills/destination/SKILL.md.bak"
730
+ mutate_jsonl "$1/specs/mapping.jsonl" \
731
+ 'rows[0]["carrier_text"] = rows[0]["carrier_text"].replace("before final", "after initial"); rows[0]["qualifiers"][1]["after"] = ["after", "initial"]'
732
+ }
733
+
734
+ mutation_stale_locator() {
735
+ sed -E -i.bak 's#skills/destination/SKILL.md:[0-9]+-[0-9]+#skills/destination/SKILL.md:999-999#g' "$1/specs/ledger.md"
736
+ rm "$1/specs/ledger.md.bak"
737
+ }
738
+
739
+ mutation_invalid_status() {
740
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[0]["disposition"] = "superseded"'
741
+ }
742
+
743
+ mutation_retired_preserved() {
744
+ mutate_jsonl "$1/specs/mapping.jsonl" \
745
+ 'rows[0]["disposition"] = "retired-dead"; rows[0]["effect"] = "preserved"; rows[0]["carrier_path"] = None; rows[0]["carrier_text"] = None; rows[0]["carrier_chain"] = None; rows[0]["qualifiers"] = []; rows[0]["review_note"] = "authority: fixture authority; scope: this obsolete row"'
746
+ }
747
+
748
+ mutation_retired_dead_strengthened() {
749
+ mutate_jsonl "$1/specs/mapping.jsonl" \
750
+ 'rows[0]["disposition"] = "retired-dead"; rows[0]["effect"] = "strengthened"; rows[0]["carrier_path"] = None; rows[0]["carrier_text"] = None; rows[0]["carrier_chain"] = None; rows[0]["qualifiers"] = []; rows[0]["manual_reviewed"] = True; rows[0]["semantic_review"] = "reviewed"; rows[0]["semantic_rationale"] = "Fixture retirement rationale."; rows[0]["review_note"] = "authority: fixture authority; scope: this obsolete row"'
751
+ }
752
+
753
+ mutation_partition_retired_mislabel() {
754
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["effect"] = "strengthened"'
755
+ }
756
+
757
+ mutation_retired_effect_on_live_row() {
758
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[3]["effect"] = "retired"'
759
+ }
760
+
761
+ mutation_partition_unreviewed() {
762
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["manual_reviewed"] = False'
763
+ }
764
+
765
+ mutation_unreviewed_strengthening() {
766
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[3]["effect"] = "strengthened"; rows[3]["manual_reviewed"] = False'
767
+ }
768
+
769
+ mutation_provenance_only() {
770
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[0]["carrier_path"] = "skills/meta/references/source-register.md"'
771
+ }
772
+
773
+ mutation_new_domain_file() {
774
+ sed -i.bak 's/unchanged comparison-domain contract/rewritten comparison-domain contract/' "$1/skills/extra/SKILL.md"
775
+ rm "$1/skills/extra/SKILL.md.bak"
776
+ }
777
+
778
+ mutation_lost_baseline_table_cell() {
779
+ sed -i.bak 's/stable table audit/rewritten table audit/' "$1/skills/matrix/SKILL.md"
780
+ rm "$1/skills/matrix/SKILL.md.bak"
781
+ }
782
+
783
+ mutation_table_carrier_to_fence() {
784
+ python3 - "$1/skills/matrix/SKILL.md" <<'PY'
785
+ from pathlib import Path
786
+ import sys
787
+ path = Path(sys.argv[1])
788
+ text = path.read_text()
789
+ text = text.replace('| Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |\n', '')
790
+ text += '\n```text\nEvery delivery client MUST preserve the table-bound release record.\n```\n'
791
+ path.write_text(text)
792
+ PY
793
+ }
794
+
795
+ mutation_table_carrier_to_comment() {
796
+ python3 - "$1/skills/matrix/SKILL.md" <<'PY'
797
+ from pathlib import Path
798
+ import sys
799
+ path = Path(sys.argv[1])
800
+ text = path.read_text()
801
+ text = text.replace('| Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |', '<!-- Every delivery client MUST preserve the table-bound release record. -->')
802
+ path.write_text(text)
803
+ PY
804
+ }
805
+
806
+ mutation_table_carrier_to_header() {
807
+ python3 - "$1/skills/matrix/SKILL.md" <<'PY'
808
+ from pathlib import Path
809
+ import sys
810
+ path = Path(sys.argv[1])
811
+ text = path.read_text()
812
+ text = text.replace('| Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |\n', '')
813
+ text += '\n| Every delivery client MUST preserve the table-bound release record. | Other |\n| --- | --- |\n'
814
+ path.write_text(text)
815
+ PY
816
+ }
817
+
818
+ mutation_table_inside_long_fence() {
819
+ python3 - "$1/skills/matrix/SKILL.md" <<'PY'
820
+ from pathlib import Path
821
+ import sys
822
+ path = Path(sys.argv[1])
823
+ text = path.read_text().replace('| Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |\n', '')
824
+ text += '\n````markdown\n```text\n| Rule | Boundary |\n| --- | --- |\n| Every delivery client MUST preserve the table-bound release record. | inert |\n```\n````\n'
825
+ path.write_text(text)
826
+ PY
827
+ }
828
+
829
+ mutation_table_inside_mismatched_fence() {
830
+ python3 - "$1/skills/matrix/SKILL.md" <<'PY'
831
+ from pathlib import Path
832
+ import sys
833
+ path = Path(sys.argv[1])
834
+ text = path.read_text().replace('| Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |\n', '')
835
+ text += '\n```markdown\n~~~\n| Rule | Boundary |\n| --- | --- |\n| Every delivery client MUST preserve the table-bound release record. | inert |\n~~~\n```\n'
836
+ path.write_text(text)
837
+ PY
838
+ }
839
+
840
+ mutation_table_after_nonblank_fence_suffix() {
841
+ python3 - "$1/skills/matrix/SKILL.md" <<'PY'
842
+ from pathlib import Path
843
+ import sys
844
+ path = Path(sys.argv[1])
845
+ text = path.read_text().replace('| Every delivery client MUST preserve the table-bound release record. | Runtime-visible contract. |\n', '')
846
+ text += '\n```markdown\n```not-a-closer\n| Rule | Boundary |\n| --- | --- |\n| Every delivery client MUST preserve the table-bound release record. | inert |\n```\n'
847
+ path.write_text(text)
848
+ PY
849
+ }
850
+
851
+ mutation_inline_code_only_table_cell() {
852
+ sed -i.bak 's/| Every delivery client MUST preserve the table-bound release record. |/| `Every delivery client MUST preserve the table-bound release record.` |/' "$1/skills/matrix/SKILL.md"
853
+ rm "$1/skills/matrix/SKILL.md.bak"
854
+ mutate_jsonl "$1/specs/mapping.jsonl" \
855
+ 'rows[2]["carrier_text"] = "`Every delivery client MUST preserve the table-bound release record.`"'
856
+ }
857
+
858
+
859
+ mutation_bundle_noncompound() {
860
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[3]["carrier_path"] = None; rows[3]["carrier_text"] = None; rows[3]["carrier_chain"] = None; rows[3]["carrier_bundle"] = rows[4]["carrier_bundle"]; rows[3]["compound_clauses"] = rows[4]["compound_clauses"]; rows[3]["disposition"] = "subsumed"; rows[3]["manual_reviewed"] = True; rows[3]["qualifiers"] = rows[4]["qualifiers"]'
861
+ }
862
+
863
+ mutation_bundle_size_one() {
864
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"] = rows[4]["carrier_bundle"][:1]'
865
+ }
866
+
867
+ mutation_scalar_and_bundle() {
868
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_path"] = "skills/destination/SKILL.md"'
869
+ }
870
+
871
+ mutation_duplicate_bundle_member() {
872
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"].append(rows[4]["carrier_bundle"][0].copy())'
873
+ }
874
+
875
+ mutation_empty_bundle_covers() {
876
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"][0]["covers"] = []'
877
+ }
878
+
879
+ mutation_uncovered_bundle_clause() {
880
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"] = rows[4]["carrier_bundle"][:2]'
881
+ }
882
+
883
+ mutation_duplicate_clause_coverage() {
884
+ python3 - "$1/skills/destination/SKILL.md" <<'PY'
885
+ from pathlib import Path
886
+ import sys
887
+
888
+ path = Path(sys.argv[1])
889
+ path.write_text(
890
+ path.read_text()
891
+ + "- The release record MUST preserve these clauses:owner context must remain fully visible.\n"
892
+ )
893
+ PY
894
+ mutate_jsonl "$1/specs/mapping.jsonl" 'm = rows[4]["carrier_bundle"][2]; m["carrier_text"] = "The release record MUST preserve these clauses:owner context must remain fully visible."; m["covers"] = ["c1"]; m["clause_qualifiers"] = [{"clause_id":"c1","qualifiers":[{"kind":"modality","before":["MUST","must"],"after":["MUST","must"],"same_immediate_host":True},{"kind":"actor","before":["owner"],"after":["owner"],"same_immediate_host":True}],"semantic_review":"reviewed","semantic_rationale":"Reviewed duplicate-coverage mutant clause."}]'
895
+ }
896
+
897
+ mutation_stale_bundle_member() {
898
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"][0]["carrier_text"] += " stale"'
899
+ }
900
+
901
+ mutation_bundle_actor_wrong_member() {
902
+ sed -i.bak 's/The release record MUST preserve these clauses:owner context must remain visible\./Every owner MUST preserve these clauses:context must remain visible./' "$1/skills/destination/SKILL.md"
903
+ rm "$1/skills/destination/SKILL.md.bak"
904
+ mutate_jsonl "$1/specs/mapping.jsonl" 'a = rows[4]["carrier_bundle"][0]; b = rows[4]["carrier_bundle"][1]; a["carrier_text"] = "Every owner MUST preserve these clauses:context must remain visible."; a["covers"] = ["c2"]; a["clause_qualifiers"] = [{"clause_id":"c2","qualifiers":[{"kind":"modality","before":["must"],"after":["MUST","must"],"same_immediate_host":True},{"kind":"scope","before":["every"],"after":["Every"],"same_immediate_host":True},{"kind":"actor","before":["client"],"after":["owner"],"same_immediate_host":True}],"semantic_review":"reviewed","semantic_rationale":"Reviewed wrong-member mutant clause c2."}]; b["covers"] = ["c1"]; b["clause_qualifiers"] = [{"clause_id":"c1","qualifiers":[{"kind":"modality","before":["MUST","must"],"after":["must"],"same_immediate_host":True},{"kind":"actor","before":["owner"],"after":["client"],"same_immediate_host":True}],"semantic_review":"reviewed","semantic_rationale":"Reviewed wrong-member mutant clause c1."}]'
905
+ }
906
+
907
+ mutation_bundle_recency_reversed() {
908
+ sed -i.bak 's/Final approval must block unsafe retry\./Initial approval must block unsafe retry./' "$1/skills/destination/SKILL.md"
909
+ rm "$1/skills/destination/SKILL.md.bak"
910
+ mutate_jsonl "$1/specs/mapping.jsonl" 'm = rows[4]["carrier_bundle"][2]; m["carrier_text"] = "Initial approval must block unsafe retry."; m["clause_qualifiers"][0]["qualifiers"][1]["after"] = ["Initial"]; rows[4]["qualifiers"][1]["after"] = ["Initial"]'
911
+ }
912
+
913
+ mutation_bundle_must_to_may() {
914
+ sed -i.bak 's/Every client must expose recovery\./Every client may expose recovery./' "$1/skills/destination/SKILL.md"
915
+ rm "$1/skills/destination/SKILL.md.bak"
916
+ mutate_jsonl "$1/specs/mapping.jsonl" 'm = rows[4]["carrier_bundle"][1]; m["carrier_text"] = "Every client may expose recovery."; m["clause_qualifiers"][0]["qualifiers"][0]["after"] = ["may"]'
917
+ }
918
+
919
+ mutation_bundle_swap_clause_actions() {
920
+ sed -i.bak \
921
+ -e 's/The release record MUST preserve these clauses:owner context must remain visible\./The release record MUST expose these clauses:owner context must remain visible./' \
922
+ -e 's/Every client must expose recovery\./Every client must preserve recovery./' \
923
+ "$1/skills/destination/SKILL.md"
924
+ rm "$1/skills/destination/SKILL.md.bak"
925
+ mutate_jsonl "$1/specs/mapping.jsonl" 'b = rows[4]["carrier_bundle"]; b[0]["carrier_text"] = "The release record MUST expose these clauses:owner context must remain visible."; b[1]["carrier_text"] = "Every client must preserve recovery."'
926
+ }
927
+
928
+ mutation_bundle_multi_clause_carrier() {
929
+ python3 - "$1/skills/destination/SKILL.md" <<'PY'
930
+ from pathlib import Path
931
+ import sys
932
+
933
+ path = Path(sys.argv[1])
934
+ text = path.read_text()
935
+ text = text.replace(
936
+ "- The release record MUST preserve these clauses:owner context must remain visible.\n"
937
+ "- Every client must expose recovery.\n",
938
+ "- The release record MUST preserve these clauses:owner context must remain visible; "
939
+ "Every client must expose recovery.\n",
940
+ )
941
+ path.write_text(text)
942
+ PY
943
+ mutate_jsonl "$1/specs/mapping.jsonl" 'b = rows[4]["carrier_bundle"]; b[0]["carrier_text"] = "The release record MUST preserve these clauses:owner context must remain visible; Every client must expose recovery."; b[0]["covers"] = ["c1", "c2"]; b[0]["clause_qualifiers"] = [{"clause_id":"c1","qualifiers":[{"kind":"modality","before":["MUST","must"],"after":["MUST","must"],"same_immediate_host":True},{"kind":"actor","before":["owner"],"after":["owner","client"],"same_immediate_host":True}],"semantic_review":"reviewed","semantic_rationale":"Reviewed compound-member mutant clause c1."},{"clause_id":"c2","qualifiers":[{"kind":"modality","before":["must"],"after":["MUST","must"],"same_immediate_host":True},{"kind":"scope","before":["every"],"after":["Every"],"same_immediate_host":True},{"kind":"actor","before":["client"],"after":["owner","client"],"same_immediate_host":True}],"semantic_review":"reviewed","semantic_rationale":"Reviewed compound-member mutant clause c2."}]; del b[1]'
944
+ }
945
+
946
+ mutation_bundle_compound_member_single_cover() {
947
+ python3 - "$1/skills/destination/SKILL.md" <<'PY'
948
+ from pathlib import Path
949
+ import sys
950
+
951
+ path = Path(sys.argv[1])
952
+ text = path.read_text()
953
+ text += (
954
+ "- The release record MUST preserve these clauses:owner context must remain visible; "
955
+ "Every client must expose recovery.\n"
956
+ )
957
+ path.write_text(text)
958
+ PY
959
+ mutate_jsonl "$1/specs/mapping.jsonl" 'm = rows[4]["carrier_bundle"][0]; m["carrier_text"] = "The release record MUST preserve these clauses:owner context must remain visible; Every client must expose recovery."; m["clause_qualifiers"][0]["qualifiers"] = [{"kind":"modality","before":["MUST","must"],"after":["MUST","must"],"same_immediate_host":True},{"kind":"actor","before":["owner"],"after":["owner","client"],"same_immediate_host":True}]'
960
+ }
961
+
962
+ mutation_only_to_any() {
963
+ sed -i.bak 's/None of the non-provenance sources may be used\./Any non-provenance source may be used./' "$1/skills/destination/SKILL.md"
964
+ rm "$1/skills/destination/SKILL.md.bak"
965
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[6]["carrier_text"] = "Any non-provenance source may be used."; rows[6]["qualifiers"][0]["after"] = ["Any"]'
966
+ }
967
+
968
+ mutation_every_to_any() {
969
+ sed -i.bak 's/Every gate reviewer MUST reject/Any gate reviewer MUST reject/' "$1/skills/destination/SKILL.md"
970
+ rm "$1/skills/destination/SKILL.md.bak"
971
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[1]["carrier_text"] = rows[1]["carrier_text"].replace("Every", "Any"); rows[1]["qualifiers"][1]["after"] = ["Any"]'
972
+ }
973
+
974
+ mutation_remove_exclusive_expression() {
975
+ sed -i.bak 's/not a substitute for design judgment/a substitute for design judgment/' "$1/skills/destination/SKILL.md"
976
+ rm "$1/skills/destination/SKILL.md.bak"
977
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[9]["carrier_text"] = rows[9]["carrier_text"].replace("not a substitute", "a substitute"); rows[9]["qualifiers"] = []'
978
+ }
979
+
980
+ mutation_drop_true_threshold() {
981
+ sed -i.bak 's/MUST complete within 500 ms/MUST complete eventually/' "$1/skills/destination/SKILL.md"
982
+ rm "$1/skills/destination/SKILL.md.bak"
983
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[15]["carrier_text"] = rows[15]["carrier_text"].replace("within 500 ms", "eventually"); rows[15]["qualifiers"] = rows[15]["qualifiers"][:1]'
984
+ }
985
+
986
+ mutation_drop_pending_status() {
987
+ sed -i.bak 's/MUST stay `pending`/MUST stay queued/' "$1/skills/destination/SKILL.md"
988
+ rm "$1/skills/destination/SKILL.md.bak"
989
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[16]["carrier_text"] = rows[16]["carrier_text"].replace("`pending`", "queued"); rows[16]["qualifiers"] = rows[16]["qualifiers"][:1]'
990
+ }
991
+
992
+ mutation_drop_true_blocker() {
993
+ sed -i.bak 's/Final approval MUST block unsafe retry\./Final approval MUST allow unsafe retry./' "$1/skills/destination/SKILL.md"
994
+ rm "$1/skills/destination/SKILL.md.bak"
995
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[18]; r["carrier_text"] = "Final approval MUST allow unsafe retry."; r["manual_reviewed"] = True; r["semantic_review"] = "reviewed"; r["semantic_rationale"] = "Reviewed the blocker-removal mutant."; r["qualifiers"] = [{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":True},{"kind":"recency","before":["Final"],"after":["Final"],"same_immediate_host":True}]'
996
+ }
997
+
998
+ mutation_drop_true_actor() {
999
+ sed -i.bak 's/The user MUST confirm the irreversible action\./The workflow MUST confirm the irreversible action./' "$1/skills/destination/SKILL.md"
1000
+ rm "$1/skills/destination/SKILL.md.bak"
1001
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[20]; r["carrier_text"] = "The workflow MUST confirm the irreversible action."; r["qualifiers"] = r["qualifiers"][:1]'
1002
+ }
1003
+
1004
+ mutation_reverse_below_threshold() {
1005
+ sed -i.bak 's/The failure rate MUST stay below 5%\./The failure rate MUST stay above 5%./' "$1/skills/destination/SKILL.md"
1006
+ rm "$1/skills/destination/SKILL.md.bak"
1007
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[23]; r["carrier_text"] = "The failure rate MUST stay above 5%."; r["manual_reviewed"] = True; r["semantic_review"] = "reviewed"; r["semantic_rationale"] = "Reviewed the changed threshold direction."; r["qualifiers"] = [{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":True},{"kind":"threshold","before":["below","5%"],"after":["above","5%"],"same_immediate_host":True}]'
1008
+ }
1009
+
1010
+ mutation_reverse_under_threshold() {
1011
+ sed -i.bak 's/The retry rate MUST stay under 5%\./The retry rate MUST stay over 5%./' "$1/skills/destination/SKILL.md"
1012
+ rm "$1/skills/destination/SKILL.md.bak"
1013
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[24]; r["carrier_text"] = "The retry rate MUST stay over 5%."; r["manual_reviewed"] = True; r["semantic_review"] = "reviewed"; r["semantic_rationale"] = "Reviewed the changed threshold direction."; r["qualifiers"] = [{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":True},{"kind":"threshold","before":["under","5%"],"after":["over","5%"],"same_immediate_host":True}]'
1014
+ }
1015
+
1016
+ mutation_scalar_semantic_review_missing() {
1017
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[0]["semantic_review"] = None'
1018
+ }
1019
+
1020
+ mutation_scalar_semantic_rationale_missing() {
1021
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[0]["semantic_rationale"] = ""'
1022
+ }
1023
+
1024
+ mutation_bundle_clause_semantic_review_missing() {
1025
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"][0]["clause_qualifiers"][0]["semantic_review"] = None'
1026
+ }
1027
+
1028
+ mutation_bundle_clause_semantic_rationale_missing() {
1029
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[4]["carrier_bundle"][0]["clause_qualifiers"][0]["semantic_rationale"] = ""'
1030
+ }
1031
+
1032
+ mutation_relation_on_modality() {
1033
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[5]["qualifier_relations"][0]; r["kind"] = "modality"; r["term"] = "MUST"; r["source_excerpt"] = "MUST"'
1034
+ }
1035
+
1036
+ mutation_relation_on_scope() {
1037
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[6]; r["effect"] = "strengthened"; r["qualifier_relations"] = [{"kind":"scope","term":"only","occurrence":1,"source_excerpt":"only provenance","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: attempted scope suppression."'
1038
+ }
1039
+
1040
+ mutation_relation_on_consequence() {
1041
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[16]; r["effect"] = "strengthened"; r["qualifier_relations"] = [{"kind":"consequence","term":"pending","occurrence":1,"source_excerpt":"`pending`","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: attempted terminal suppression."'
1042
+ }
1043
+
1044
+ mutation_relation_on_threshold() {
1045
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[15]; r["effect"] = "strengthened"; r["qualifier_relations"] = [{"kind":"threshold","term":"within","occurrence":1,"source_excerpt":"within 500 ms","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: attempted threshold suppression."'
1046
+ }
1047
+
1048
+ mutation_relation_stale_occurrence() {
1049
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["qualifier_relations"][0]["occurrence"] = 2'
1050
+ }
1051
+
1052
+ mutation_relation_stale_excerpt() {
1053
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["qualifier_relations"][0]["source_excerpt"] = "final artifact"'
1054
+ }
1055
+
1056
+ mutation_duplicate_relation() {
1057
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["qualifier_relations"].append(rows[5]["qualifier_relations"][0].copy())'
1058
+ }
1059
+
1060
+ mutation_relation_without_strengthening() {
1061
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["effect"] = "preserved"'
1062
+ }
1063
+
1064
+ mutation_relation_without_review() {
1065
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["manual_reviewed"] = False'
1066
+ }
1067
+
1068
+ mutation_relation_without_review_note() {
1069
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["review_note"] = ""'
1070
+ }
1071
+
1072
+ mutation_relation_without_closed_final_rewrite() {
1073
+ sed -i.bak 's/The resulting skill MUST stay generic\./The skill MUST stay generic./' "$1/skills/destination/SKILL.md"
1074
+ rm "$1/skills/destination/SKILL.md.bak"
1075
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[5]["carrier_text"] = "The skill MUST stay generic."'
1076
+ }
1077
+
1078
+ mutation_relation_on_latest_design() {
1079
+ sed -i.bak 's/The latest design MUST remain accessible\./The design MUST remain accessible./' "$1/skills/destination/SKILL.md"
1080
+ rm "$1/skills/destination/SKILL.md.bak"
1081
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[21]; r["effect"] = "strengthened"; r["carrier_text"] = "The design MUST remain accessible."; r["manual_reviewed"] = True; r["semantic_review"] = "reviewed"; r["semantic_rationale"] = "Reviewed the attempted latest-design rewrite."; r["qualifiers"] = [{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":True}]; r["qualifier_relations"] = [{"kind":"recency","term":"latest","occurrence":1,"source_excerpt":"latest design","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: reviewer claims latest is non-temporal."'
1082
+ }
1083
+
1084
+ mutation_relation_on_final_artifact() {
1085
+ sed -i.bak 's/The final artifact MUST retain its audit trail\./The artifact MUST retain its audit trail./' "$1/skills/destination/SKILL.md"
1086
+ rm "$1/skills/destination/SKILL.md.bak"
1087
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[22]; r["effect"] = "strengthened"; r["carrier_text"] = "The artifact MUST retain its audit trail."; r["manual_reviewed"] = True; r["semantic_review"] = "reviewed"; r["semantic_rationale"] = "Reviewed the attempted final-artifact rewrite."; r["qualifiers"] = [{"kind":"modality","before":["MUST"],"after":["MUST"],"same_immediate_host":True}]; r["qualifier_relations"] = [{"kind":"recency","term":"final","occurrence":1,"source_excerpt":"final artifact","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: reviewer claims artifact finality is implicit."'
1088
+ }
1089
+
1090
+ mutation_relation_on_ordered_final() {
1091
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[0]; r["qualifier_relations"] = [{"kind":"recency","term":"final","occurrence":1,"source_excerpt":"final approval","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: attempted final-approval suppression."'
1092
+ }
1093
+
1094
+ mutation_relation_on_final_content() {
1095
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[19]; r["effect"] = "strengthened"; r["qualifier_relations"] = [{"kind":"recency","term":"final","occurrence":1,"source_excerpt":"final content","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: attempted terminal-content suppression."'
1096
+ }
1097
+
1098
+ mutation_relation_on_bundle() {
1099
+ mutate_jsonl "$1/specs/mapping.jsonl" 'r = rows[4]; r["effect"] = "strengthened"; r["qualifier_relations"] = [{"kind":"recency","term":"final","occurrence":1,"source_excerpt":"final approval","resolution":"carrier-applies-unconditionally","same_immediate_host":True}]; r["review_note"] = "Qualifier relation: attempted bundle suppression."'
1100
+ }
1101
+
1102
+ mutation_schema4_missing_part() {
1103
+ mutate_jsonl "$1/specs/mapping.jsonl" 'del rows[12]["parts"][1]'
1104
+ }
1105
+
1106
+ mutation_schema4_overlap() {
1107
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][1]["source_start"] -= 1'
1108
+ }
1109
+
1110
+ mutation_schema4_source_text() {
1111
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][0]["source_text"] = "Remove used "'
1112
+ }
1113
+
1114
+ mutation_schema4_before_text() {
1115
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["before_text"] += " stale"'
1116
+ }
1117
+
1118
+ mutation_schema4_empty_carrier() {
1119
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][0]["carriers"] = []'
1120
+ }
1121
+
1122
+ mutation_schema4_generic_neighbor() {
1123
+ python3 - "$1/specs/mapping.jsonl" <<'PY'
1124
+ import hashlib
1125
+ import json
1126
+ import sys
1127
+ from pathlib import Path
1128
+
1129
+ path = Path(sys.argv[1])
1130
+ rows = [json.loads(line) for line in path.read_text().splitlines() if line]
1131
+ member = rows[12]["parts"][0]["carriers"][0]
1132
+ member["carrier_text"] = "Existing stable contract remains unchanged for the fixture."
1133
+ member["carrier_chain"] = ["Destination", "Current"]
1134
+ member["bridge_terms"] = ["stable"]
1135
+ payload = json.dumps(
1136
+ [member["carrier_path"], member["carrier_chain"], member["carrier_text"]],
1137
+ ensure_ascii=False,
1138
+ separators=(",", ":"),
1139
+ ).encode()
1140
+ member["carrier_sha256"] = hashlib.sha256(payload).hexdigest()
1141
+ path.write_text("".join(json.dumps(row, separators=(",", ":")) + "\n" for row in rows))
1142
+ PY
1143
+ }
1144
+
1145
+ mutation_schema4_retired_only_bridge() {
1146
+ python3 - "$1/specs/mapping.jsonl" <<'PY'
1147
+ import hashlib
1148
+ import json
1149
+ import sys
1150
+ from pathlib import Path
1151
+
1152
+ path = Path(sys.argv[1])
1153
+ rows = [json.loads(line) for line in path.read_text().splitlines() if line]
1154
+ member = rows[12]["parts"][0]["carriers"][0]
1155
+ member["carrier_text"] = "Legacy first-screen note remains in destination."
1156
+ member["carrier_chain"] = ["Destination", "Delivery contract"]
1157
+ member["bridge_terms"] = ["first-screen"]
1158
+ payload = json.dumps(
1159
+ [member["carrier_path"], member["carrier_chain"], member["carrier_text"]],
1160
+ ensure_ascii=False,
1161
+ separators=(",", ":"),
1162
+ ).encode()
1163
+ member["carrier_sha256"] = hashlib.sha256(payload).hexdigest()
1164
+ path.write_text("".join(json.dumps(row, separators=(",", ":")) + "\n" for row in rows))
1165
+ PY
1166
+ }
1167
+
1168
+ mutation_schema4_hash() {
1169
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][0]["carriers"][0]["carrier_sha256"] = "0" * 64'
1170
+ }
1171
+
1172
+ mutation_schema4_authority() {
1173
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][1]["authority"] = ""'
1174
+ }
1175
+
1176
+ mutation_schema4_scope() {
1177
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][1]["scope"] = ""'
1178
+ }
1179
+
1180
+ mutation_schema4_reason() {
1181
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][1]["reason"] = ""'
1182
+ }
1183
+
1184
+ mutation_schema4_qualifier() {
1185
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[13]["parts"][0]["qualifiers"] = []'
1186
+ }
1187
+
1188
+ mutation_schema4_duplicate_carrier() {
1189
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["parts"][0]["carriers"].append(rows[12]["parts"][0]["carriers"][0].copy())'
1190
+ }
1191
+
1192
+ mutation_schema4_wrong_disposition() {
1193
+ mutate_jsonl "$1/specs/mapping.jsonl" 'rows[12]["disposition"] = "partitioned"'
1194
+ }
1195
+
1196
+ DELTA_MAPPING='specs/mapping.jsonl'
1197
+ DELTA_DEST='skills/destination/SKILL.md'
1198
+ DELTA_DEST_MAPPING='skills/destination/SKILL.md,specs/mapping.jsonl'
1199
+ DELTA_MATRIX='skills/matrix/SKILL.md'
1200
+ DELTA_MATRIX_MAPPING='skills/matrix/SKILL.md,specs/mapping.jsonl'
1201
+
1202
+ run_mutant missing_row ROW_SET_MISMATCH 'skills/source/SKILL.md#2' "$DELTA_MAPPING" mutation_missing_row
1203
+ run_mutant duplicate_carrier MULTIPLE_CARRIERS 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_duplicate_carrier
1204
+ run_mutant must_to_may QUALIFIER_WEAKENED 'skills/source/SKILL.md#1' "$DELTA_DEST_MAPPING" mutation_weaken_modality
1205
+ run_mutant wrong_parent CARRIER_CHAIN_MISMATCH 'skills/source/SKILL.md#1' "$DELTA_DEST" mutation_wrong_parent
1206
+ run_mutant recency_direction_reversal QUALIFIER_REVERSED 'skills/source/SKILL.md#1' "$DELTA_DEST_MAPPING" mutation_reverse_recency
1207
+ run_mutant stale_locator STALE_LEDGER 'specs/ledger.md' 'specs/ledger.md' mutation_stale_locator
1208
+ run_mutant invalid_status INVALID_DISPOSITION 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_invalid_status
1209
+ run_mutant retired_dead_preserved RETIRED_EFFECT_INVALID 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_retired_preserved
1210
+ run_mutant retired_dead_strengthened RETIRED_EFFECT_INVALID 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_retired_dead_strengthened
1211
+ run_mutant partition_retired_mislabel PARTITION_EFFECT_MISMATCH 'skills/source/SKILL.md#13' "$DELTA_MAPPING" mutation_partition_retired_mislabel
1212
+ run_mutant retired_effect_on_live_row RETIRED_EFFECT_INVALID 'skills/source/SKILL.md#4' "$DELTA_MAPPING" mutation_retired_effect_on_live_row
1213
+ run_mutant partition_unreviewed MANUAL_REVIEW_REQUIRED 'skills/source/SKILL.md#13' "$DELTA_MAPPING" mutation_partition_unreviewed
1214
+ run_mutant unreviewed_verbatim_strengthening STRENGTHENED_REVIEW_REQUIRED 'skills/source/SKILL.md#4' "$DELTA_MAPPING" mutation_unreviewed_strengthening
1215
+ run_mutant provenance_only PROVENANCE_ONLY_CARRIER 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_provenance_only
1216
+ run_mutant comparison_domain_new_file ROW_SET_MISMATCH 'skills/extra/SKILL.md#1' 'skills/extra/SKILL.md' mutation_new_domain_file
1217
+ run_mutant lost_baseline_table_cell ROW_SET_MISMATCH 'skills/matrix/SKILL.md#1' "$DELTA_MATRIX" mutation_lost_baseline_table_cell
1218
+ run_mutant table_carrier_moved_to_fence CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX" mutation_table_carrier_to_fence
1219
+ run_mutant table_carrier_moved_to_comment CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX" mutation_table_carrier_to_comment
1220
+ run_mutant table_carrier_moved_to_header CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX" mutation_table_carrier_to_header
1221
+ run_mutant table_inside_long_outer_fence CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX" mutation_table_inside_long_fence
1222
+ run_mutant table_inside_mismatched_fence CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX" mutation_table_inside_mismatched_fence
1223
+ run_mutant table_after_nonblank_fence_suffix CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX" mutation_table_after_nonblank_fence_suffix
1224
+ run_mutant inline_code_only_table_cell CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#3' "$DELTA_MATRIX_MAPPING" mutation_inline_code_only_table_cell
1225
+
1226
+ run_mutant bundle_noncompound BUNDLE_SOURCE_NOT_COMPOUND 'skills/source/SKILL.md#4' "$DELTA_MAPPING" mutation_bundle_noncompound
1227
+ run_mutant bundle_size_one BUNDLE_SIZE 'skills/source/SKILL.md#5' "$DELTA_MAPPING" mutation_bundle_size_one
1228
+ run_mutant scalar_and_bundle CARRIER_SHAPE_CONFLICT 'skills/source/SKILL.md#5' "$DELTA_MAPPING" mutation_scalar_and_bundle
1229
+ run_mutant duplicate_bundle_member BUNDLE_DUPLICATE_MEMBER 'skills/source/SKILL.md#5/member-4' "$DELTA_MAPPING" mutation_duplicate_bundle_member
1230
+ run_mutant empty_bundle_covers BUNDLE_MEMBER_EMPTY 'skills/source/SKILL.md#5/member-1' "$DELTA_MAPPING" mutation_empty_bundle_covers
1231
+ run_mutant uncovered_bundle_clause BUNDLE_CLAUSE_UNCOVERED 'skills/source/SKILL.md#5' "$DELTA_MAPPING" mutation_uncovered_bundle_clause
1232
+ run_mutant duplicate_clause_coverage BUNDLE_CLAUSE_DUPLICATE 'skills/source/SKILL.md#5' "$DELTA_DEST_MAPPING" mutation_duplicate_clause_coverage
1233
+ run_mutant stale_bundle_member CARRIER_COMPOSITE_NOT_UNIQUE 'skills/source/SKILL.md#5/member-1' "$DELTA_MAPPING" mutation_stale_bundle_member
1234
+ run_mutant bundle_actor_wrong_member BUNDLE_CLAUSE_ACTOR_MISSING 'skills/source/SKILL.md#5/member-1/c2' "$DELTA_DEST_MAPPING" mutation_bundle_actor_wrong_member
1235
+ run_mutant bundle_must_to_may BUNDLE_CLAUSE_MODALITY_MISSING 'skills/source/SKILL.md#5/member-2/c2' "$DELTA_DEST_MAPPING" mutation_bundle_must_to_may
1236
+ run_mutant bundle_recency_reversed BUNDLE_QUALIFIER_REVERSED 'skills/source/SKILL.md#5/member-3/c3' "$DELTA_DEST_MAPPING" mutation_bundle_recency_reversed
1237
+ run_mutant bundle_sibling_action_borrow BUNDLE_CLAUSE_ACTION_MISSING 'skills/source/SKILL.md#5/member-1/c1' "$DELTA_DEST_MAPPING" mutation_bundle_swap_clause_actions
1238
+ run_mutant bundle_multi_clause_carrier BUNDLE_MEMBER_MULTI_CLAUSE 'skills/source/SKILL.md#5/member-1' "$DELTA_DEST_MAPPING" mutation_bundle_multi_clause_carrier
1239
+ run_mutant bundle_compound_member_single_cover BUNDLE_MEMBER_COMPOUND 'skills/source/SKILL.md#5/member-1' "$DELTA_DEST_MAPPING" mutation_bundle_compound_member_single_cover
1240
+
1241
+ run_mutant only_to_any QUALIFIER_REVERSED 'skills/source/SKILL.md#7' "$DELTA_DEST_MAPPING" mutation_only_to_any
1242
+ run_mutant every_to_any QUALIFIER_REVERSED 'skills/source/SKILL.md#2' "$DELTA_DEST_MAPPING" mutation_every_to_any
1243
+ run_mutant exclusive_expression_removed QUALIFIER_CLASS_MISMATCH 'skills/source/SKILL.md#10' "$DELTA_DEST_MAPPING" mutation_remove_exclusive_expression
1244
+ run_mutant true_threshold_dropped QUALIFIER_CLASS_MISMATCH 'skills/source/SKILL.md#16' "$DELTA_DEST_MAPPING" mutation_drop_true_threshold
1245
+ run_mutant pending_status_dropped QUALIFIER_CLASS_MISMATCH 'skills/source/SKILL.md#17' "$DELTA_DEST_MAPPING" mutation_drop_pending_status
1246
+ run_mutant true_blocker_dropped QUALIFIER_CLASS_MISMATCH 'skills/source/SKILL.md#19' "$DELTA_DEST_MAPPING" mutation_drop_true_blocker
1247
+ run_mutant true_actor_dropped QUALIFIER_CLASS_MISMATCH 'skills/source/SKILL.md#21' "$DELTA_DEST_MAPPING" mutation_drop_true_actor
1248
+ run_mutant below_to_above QUALIFIER_REVERSED 'skills/source/SKILL.md#24' "$DELTA_DEST_MAPPING" mutation_reverse_below_threshold
1249
+ run_mutant under_to_over QUALIFIER_REVERSED 'skills/source/SKILL.md#25' "$DELTA_DEST_MAPPING" mutation_reverse_under_threshold
1250
+ run_mutant scalar_semantic_review_missing SEMANTIC_REVIEW_REQUIRED 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_scalar_semantic_review_missing
1251
+ run_mutant scalar_semantic_rationale_missing SEMANTIC_RATIONALE_REQUIRED 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_scalar_semantic_rationale_missing
1252
+ run_mutant bundle_clause_semantic_review_missing SEMANTIC_REVIEW_REQUIRED 'skills/source/SKILL.md#5/member-1/c1' "$DELTA_MAPPING" mutation_bundle_clause_semantic_review_missing
1253
+ run_mutant bundle_clause_semantic_rationale_missing SEMANTIC_RATIONALE_REQUIRED 'skills/source/SKILL.md#5/member-1/c1' "$DELTA_MAPPING" mutation_bundle_clause_semantic_rationale_missing
1254
+ run_mutant relation_on_modality QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_relation_on_modality
1255
+ run_mutant relation_on_scope QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#7' "$DELTA_MAPPING" mutation_relation_on_scope
1256
+ run_mutant relation_on_consequence QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#17' "$DELTA_MAPPING" mutation_relation_on_consequence
1257
+ run_mutant relation_on_threshold QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#16' "$DELTA_MAPPING" mutation_relation_on_threshold
1258
+ run_mutant relation_stale_occurrence QUALIFIER_RELATION_INVALID 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_relation_stale_occurrence
1259
+ run_mutant relation_stale_excerpt QUALIFIER_RELATION_INVALID 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_relation_stale_excerpt
1260
+ run_mutant duplicate_relation QUALIFIER_RELATION_INVALID 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_duplicate_relation
1261
+ run_mutant relation_without_strengthening QUALIFIER_RELATION_REQUIRES_STRENGTHENED 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_relation_without_strengthening
1262
+ run_mutant relation_without_review STRENGTHENED_REVIEW_REQUIRED 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_relation_without_review
1263
+ run_mutant relation_without_review_note REVIEW_NOTE_MISSING 'skills/source/SKILL.md#6' "$DELTA_MAPPING" mutation_relation_without_review_note
1264
+ run_mutant relation_without_closed_final_rewrite QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#6' "$DELTA_DEST_MAPPING" mutation_relation_without_closed_final_rewrite
1265
+ run_mutant relation_on_latest_design QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#22' "$DELTA_DEST_MAPPING" mutation_relation_on_latest_design
1266
+ run_mutant relation_on_final_artifact QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#23' "$DELTA_DEST_MAPPING" mutation_relation_on_final_artifact
1267
+ run_mutant relation_on_ordered_final QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#1' "$DELTA_MAPPING" mutation_relation_on_ordered_final
1268
+ run_mutant relation_on_final_content QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#20' "$DELTA_MAPPING" mutation_relation_on_final_content
1269
+ run_mutant relation_on_bundle QUALIFIER_RELATION_FORBIDDEN 'skills/source/SKILL.md#5' "$DELTA_MAPPING" mutation_relation_on_bundle
1270
+
1271
+ run_mutant schema4_missing_part PARTITION_GAP_OR_OVERLAP 'skills/source/SKILL.md#13/part-2' "$DELTA_MAPPING" mutation_schema4_missing_part
1272
+ run_mutant schema4_overlap PARTITION_GAP_OR_OVERLAP 'skills/source/SKILL.md#13/part-2' "$DELTA_MAPPING" mutation_schema4_overlap
1273
+ run_mutant schema4_source_text PART_SOURCE_MISMATCH 'skills/source/SKILL.md#13/part-1' "$DELTA_MAPPING" mutation_schema4_source_text
1274
+ run_mutant schema4_before_text ROW_SOURCE_MISMATCH 'skills/source/SKILL.md#13' "$DELTA_MAPPING" mutation_schema4_before_text
1275
+ run_mutant schema4_empty_carrier PART_CARRIER_REQUIRED 'skills/source/SKILL.md#13/part-1' "$DELTA_MAPPING" mutation_schema4_empty_carrier
1276
+ run_mutant schema4_generic_neighbor PART_CARRIER_BRIDGE_MISSING 'skills/source/SKILL.md#13/part-1/member-1' "$DELTA_MAPPING" mutation_schema4_generic_neighbor
1277
+ run_mutant schema4_retired_only_bridge PART_CARRIER_BRIDGE_MISSING 'skills/source/SKILL.md#13/part-1/member-1' "$DELTA_MAPPING" mutation_schema4_retired_only_bridge
1278
+ run_mutant schema4_hash PART_CARRIER_HASH_MISMATCH 'skills/source/SKILL.md#13/part-1/member-1' "$DELTA_MAPPING" mutation_schema4_hash
1279
+ run_mutant schema4_authority PART_RETIREMENT_PROOF_MISSING 'skills/source/SKILL.md#13/part-2: authority' "$DELTA_MAPPING" mutation_schema4_authority
1280
+ run_mutant schema4_scope PART_RETIREMENT_PROOF_MISSING 'skills/source/SKILL.md#13/part-2: scope' "$DELTA_MAPPING" mutation_schema4_scope
1281
+ run_mutant schema4_reason PART_RETIREMENT_PROOF_MISSING 'skills/source/SKILL.md#13/part-2: reason' "$DELTA_MAPPING" mutation_schema4_reason
1282
+ run_mutant schema4_qualifier BUNDLE_QUALIFIER_CLASS_MISMATCH 'skills/source/SKILL.md#14/part-1' "$DELTA_MAPPING" mutation_schema4_qualifier
1283
+ run_mutant schema4_duplicate_carrier PART_CARRIER_DUPLICATE 'skills/source/SKILL.md#13/part-1/member-2' "$DELTA_MAPPING" mutation_schema4_duplicate_carrier
1284
+ run_mutant schema4_wrong_disposition PARTITION_DISPOSITION_MISMATCH 'skills/source/SKILL.md#13' "$DELTA_MAPPING" mutation_schema4_wrong_disposition
1285
+
1286
+ # --- Parser-boundary regressions: every case below reproduces a defect that
1287
+ # shipped (or was found in review) in the CommonMark-facing parsers, so a
1288
+ # revert of any one fix turns this leg red without needing a fixture repo.
1289
+ python3 - "$TOOL" <<'PY'
1290
+ import importlib.util
1291
+ import sys
1292
+ from pathlib import Path
1293
+
1294
+ tool = Path(sys.argv[1])
1295
+ spec = importlib.util.spec_from_file_location("obligation_ledger_parser_test", tool)
1296
+ module = importlib.util.module_from_spec(spec)
1297
+ sys.modules[spec.name] = module
1298
+ spec.loader.exec_module(module)
1299
+
1300
+ failures = []
1301
+
1302
+ def check(name, condition, detail=""):
1303
+ if not condition:
1304
+ failures.append(f"{name}: {detail}")
1305
+
1306
+ # table_cells: a backtick run of a different length inside an open code span
1307
+ # stays literal (CommonMark pairing), so the row still parses into cells.
1308
+ cells = module.table_cells("| verify ``see `x` marker`` in each build | applies |")
1309
+ check("table-unequal-backtick-runs", cells is not None and len(cells) == 2,
1310
+ f"cells={cells}")
1311
+
1312
+ # table_cells: a cell ending in a backslash must not swallow the synthetic
1313
+ # boundary pipe; the final cell is still emitted.
1314
+ cells = module.table_cells("| c | a\\|")
1315
+ check("table-trailing-backslash", cells is not None and len(cells) == 2,
1316
+ f"cells={cells}")
1317
+
1318
+ # table_cells: an unmatched opener is literal, not an open span to line end.
1319
+ cells = module.table_cells("| lone `tick | second |")
1320
+ check("table-unmatched-opener", cells is not None and len(cells) == 2,
1321
+ f"cells={cells}")
1322
+
1323
+ # mask_inert_markdown: a 4-space-indented paragraph continuation stays visible
1324
+ # (indented code cannot interrupt a paragraph).
1325
+ masked = module.mask_inert_markdown(
1326
+ "verify the complete regression\n suite before any merge.\n")
1327
+ check("mask-paragraph-continuation", "suite before any merge" in masked, masked)
1328
+
1329
+ # mask_inert_markdown: indented code after a heading, a closed fence, or a
1330
+ # blank line IS code and must be blanked.
1331
+ for name, source in (
1332
+ ("mask-code-after-heading", "# heading\n hidden-indent-code\n"),
1333
+ ("mask-code-after-fence", "```\nfenced\n```\n hidden-indent-code\n"),
1334
+ ("mask-code-after-blank", "prose\n\n hidden-indent-code\n"),
1335
+ # A comment-only line is blanked to whitespace and therefore behaves as a
1336
+ # blank line, so the following indented chunk is code.
1337
+ ("mask-code-after-comment-line", "<!-- note -->\n hidden-indent-code\n"),
1338
+ # A Setext underline closes the open paragraph as a heading, so the
1339
+ # indented chunk after it is code (both underline forms).
1340
+ ("mask-code-after-setext-eq", "Title\n===\n hidden-indent-code\n"),
1341
+ ("mask-code-after-setext-dash", "Title\n--\n hidden-indent-code\n"),
1342
+ # Inside a list item the same rules apply relative to the content column:
1343
+ # a Setext underline closes the item's paragraph, and content-column + 4
1344
+ # indentation after it (or after a blank) is code.
1345
+ ("mask-list-code-after-setext", "- Title\n ===\n hidden-indent-code\n"),
1346
+ ("mask-list-code-after-blank", "- item\n\n hidden-indent-code\n"),
1347
+ # The container's own first content line classifies paragraph state: a
1348
+ # heading or thematic break in a list item or block quote does not open
1349
+ # a paragraph, so the indented chunk after it is code.
1350
+ ("mask-list-code-after-item-heading", "- # Heading\n hidden-indent-code\n"),
1351
+ ("mask-list-code-after-item-break", "- ***\n hidden-indent-code\n"),
1352
+ ("mask-code-after-quote-heading", "> # Heading\n hidden-indent-code\n"),
1353
+ # Quote-internal indented code measures from the quote marker.
1354
+ ("mask-quote-internal-code", "> # Heading\n> hidden-indent-code\n"),
1355
+ ):
1356
+ check(name, "hidden-indent-code" not in module.mask_inert_markdown(source),
1357
+ module.mask_inert_markdown(source))
1358
+
1359
+ # mask_inert_markdown: indented list continuations remain visible.
1360
+ masked = module.mask_inert_markdown("- item\n list-continuation\n")
1361
+ check("mask-list-continuation", "list-continuation" in masked, masked)
1362
+
1363
+ # mask_inert_markdown: a trailing comment on a prose line does not close the
1364
+ # paragraph, so the indented next line is a lazy continuation, not code.
1365
+ masked = module.mask_inert_markdown(
1366
+ "prose text <!-- note -->\n lazy-continuation\n")
1367
+ check("mask-comment-inline-continuation", "lazy-continuation" in masked, masked)
1368
+
1369
+ # mask_inert_markdown: quoted prose opens a paragraph, so the indented next
1370
+ # line is a lazy continuation of the quote, not code.
1371
+ masked = module.mask_inert_markdown("> quoted prose\n lazy-continuation\n")
1372
+ check("mask-quote-lazy-continuation", "lazy-continuation" in masked, masked)
1373
+
1374
+ # section_body_ranges: HTML comments and fenced code are not visible section
1375
+ # body text, so they can never carry an obligation.
1376
+ sections = module.section_body_ranges(
1377
+ "## Rules\nvisible rule text.\n<!-- hidden-comment -->\n"
1378
+ "```\nhidden-fence\n```\nmore visible text.\n")
1379
+ check("section-visible-only",
1380
+ len(sections) == 1
1381
+ and "visible rule text" in sections[0][0].text
1382
+ and "hidden-comment" not in sections[0][0].text
1383
+ and "hidden-fence" not in sections[0][0].text,
1384
+ f"sections={[s[0].text for s in sections]}")
1385
+
1386
+ # compound_clauses: delimiters inside inline code are literal content.
1387
+ check("compound-code-semicolon",
1388
+ module.compound_clauses("Run `make check; make lint` and record the result.") == [],
1389
+ "semicolon inside a code span was treated as clause structure")
1390
+
1391
+ # compound_clauses: an action word that exists only inside inline code must not
1392
+ # qualify the colon prefix as actionable.
1393
+ check("compound-code-action-prefix",
1394
+ module.compound_clauses(
1395
+ "Requirement `must`: validate input, record output, and test rollback paths."
1396
+ ) == [],
1397
+ "code-span action word qualified the colon prefix")
1398
+
1399
+ # compound_clauses controls: real compounds still split.
1400
+ clauses = module.compound_clauses(
1401
+ "The release record MUST preserve these clauses: owner context must remain "
1402
+ "visible, every client must expose recovery, and final approval must block "
1403
+ "unsafe retry.")
1404
+ check("compound-colon-control", len(clauses) == 3, f"clauses={clauses}")
1405
+ clauses = module.compound_clauses(
1406
+ "Keep the audit record immutable; keep the release token retained.")
1407
+ check("compound-semicolon-control", len(clauses) == 2, f"clauses={clauses}")
1408
+
1409
+ if failures:
1410
+ for failure in failures:
1411
+ print(f"FAIL parser-boundary {failure}", file=sys.stderr)
1412
+ raise SystemExit(1)
1413
+ PY
1414
+ echo "PASS parser-boundary regressions"
1415
+
1416
+ echo "test_obligation_ledger_ok"