@ccoalm/ccl-skills 0.6.2 → 0.8.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.
- package/README.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-unverified-cli-flag.sh +309 -0
- package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_unverified_cli_flag.sh +483 -0
- package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +10 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +16 -17
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/client-routing.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +195 -7
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/timeout-auth-and-capabilities.md +3 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/claude_review.sh +13 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/codex_review.sh +9 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/kimi_review.sh +9 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/normalize_review_timeout.sh +22 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/opencode_review.sh +9 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +1540 -129
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_claude_review_probe.sh +8 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +76 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +1858 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_update_review_plan_intent.sh +789 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/update_review_plan_intent.py +513 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/data-platform-architecture.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/event-driven-architecture.md +14 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +5 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/SKILL.md +2 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +13 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/SKILL.md +64 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/agents/openai.yaml +4 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +72 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/runtime-and-project-contract.md +58 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/source-map.md +41 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/verification-diagnostics-and-security.md +63 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +25 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/source-register.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +16 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/secret-and-config-management.md +7 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/retry-timeout-circuit-breaker.md +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +8 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/design-routing-and-readiness.md +10 -14
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/verify-developer-experience.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/SKILL.md +135 -86
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/behavioral-aesthetic-logic.md +66 -80
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/delivery-contract.md +275 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-execution-checklist.md +88 -214
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-impl-naming-and-versioning.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-intake-and-acceptance.md +10 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +4 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/external-ui-ux-quality-benchmarks.md +112 -95
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/frontend-code-evidence-map.md +30 -21
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/interaction-design-patterns.md +22 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/layout-recipes-and-screenshot-acceptance.md +20 -17
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-project-token-consistency.md +7 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/multi-stack-strategy.md +14 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/operational-processing-workflows.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-lifecycle-acceptance-and-iteration.md +9 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/product-surface-patterns.md +3 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/source-map.md +37 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +7 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +8 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-design-development.md +16 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/visual-craft.md +4 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/SKILL.md +5 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/architecture-playbook.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/audit-history-architecture.md +31 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/data-platform-architecture.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/event-driven-architecture.md +7 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/notification-architecture.md +28 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/packaging-runtime-readiness.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/replay-comparison-architecture.md +28 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/workflow-state-architecture.md +39 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +10 -7
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/ai-service-wiring-patterns.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/audit-history-patterns.md +29 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/background-job-patterns.md +16 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/batch-and-artifact-patterns.md +25 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/notification-patterns.md +40 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/public-api-security-patterns.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/replay-comparison-patterns.md +30 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +48 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/testing-and-quality-patterns.md +10 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +4 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/coverage-exhaustion-traps.md +45 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +142 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +21 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +11 -9
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/firing-point-placement.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/parallel-stack-references-pattern.md +5 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/r0-leakage-audit.md +102 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +69 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +10 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/uiux-judgment-extraction.md +6 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +4 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +93 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-parallel-stack-parity.sh +119 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +22 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb +49 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/obligation-ledger.py +2748 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb +20 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/shared_git_surface_gate.py +1142 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_parallel_stack_parity.sh +183 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +19 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_skill_catalog.sh +41 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ci_checkout_ref_binding.sh +120 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_entrypoint_domain_scan_terms.sh +82 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +336 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_self_adjudication.sh +82 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger.sh +1416 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_obligation_ledger_repo_audit.sh +57 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_wiring.sh +141 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_pointer_integrity.sh +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_shared_git_surface_gate.sh +1696 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_delivery_contract.sh +2117 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_uiux_loading_budget.sh +316 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +1176 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_skill_cross_refs.sh +31 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate-skill.sh +9 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +980 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/terminal-cli-dev/SKILL.md +9 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +11 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/client-runtime-test-matrices.md +10 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/fitness-functions.md +16 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/scenario-testing.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +16 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +5 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/delivery-face-closeout.md +16 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/self-benchmark-baseline.md +37 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +7 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/complex-workspace-patterns.md +1 -1
- package/dist/assets/release.json +275 -105
- 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"
|