@ccoalm/ccl-skills 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +8 -7
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +6 -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/manual-invocation-and-prompts.md +6 -0
- 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/defect-diagnosis/SKILL.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/feature-risk-router/SKILL.md +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +4 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/references/state-machine-task-patterns.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/SKILL.md +2 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/inference-capacity-operations.md +24 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/llm-client-gateway.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/model-prompt-evaluation.md +4 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +11 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/references/contracts-and-state.md +5 -0
- 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 +73 -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 +3 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/metrics-conventions.md +8 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/canary-and-rollout-strategy.md +16 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +9 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +14 -16
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/code-review-checklist.md +4 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/delivery-lifecycle.md +1 -1
- 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/rd-standards-doc-family-checklist.md +1 -0
- 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 +6 -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 +3 -3
- 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 +8 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +16 -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/references/multi-tenant-isolation.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +4 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +8 -8
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md +4 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +104 -5
- 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/r0-leakage-audit.md +102 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +103 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +20 -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 +69 -2
- 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_regressions.sh +17 -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 +8 -6
- package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/classical-test-design-techniques.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/tc-review-and-prioritization.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/update-lifecycle.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +16 -15
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/ci-fixtures-and-flake-control.md +5 -1
- 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/e2e-real-flow-testing.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/integration-contract-testing.md +10 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-topology-and-commands.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +2 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/annotation-driven-revision.md +9 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/figure-and-table-craft.md +8 -2
- 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/marketplace/plugins/ccl-skills/skills/web-react-dev/references/react-architecture.md +3 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-quality-release.md +37 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-ui-quality.md +10 -1
- package/dist/assets/release.json +215 -105
- package/package.json +1 -1
package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh
CHANGED
|
@@ -205,7 +205,7 @@ PYTHONPATH="$WORK/harness/scripts" python3 - "$WORK" <<'PY'
|
|
|
205
205
|
from pathlib import Path
|
|
206
206
|
import sys
|
|
207
207
|
|
|
208
|
-
from review_gate import candidate_paths_from_packet, derive_owner_selection
|
|
208
|
+
from review_gate import candidate_paths_from_packet, decode_git_c_path, derive_owner_selection
|
|
209
209
|
|
|
210
210
|
registry = Path(sys.argv[1])
|
|
211
211
|
packet = (
|
|
@@ -216,10 +216,23 @@ packet = (
|
|
|
216
216
|
b'--- a/skills/fake-owner/SKILL.md\n'
|
|
217
217
|
b'+++ b/skills/fake-owner/SKILL.md\n'
|
|
218
218
|
b'diff --git "a/services/caf\\303\\251.py" "b/services/caf\\303\\251.py"\n'
|
|
219
|
+
+ (
|
|
220
|
+
'diff --git "a/services/caf\N{LATIN SMALL LETTER E WITH ACUTE} file.py" '
|
|
221
|
+
'"b/services/caf\N{LATIN SMALL LETTER E WITH ACUTE} file.py"\n'
|
|
222
|
+
).encode()
|
|
219
223
|
)
|
|
220
224
|
paths = candidate_paths_from_packet(packet)
|
|
221
225
|
assert "services/my file.py" in paths, paths
|
|
222
226
|
assert "services/caf\N{LATIN SMALL LETTER E WITH ACUTE}.py" in paths, paths
|
|
227
|
+
assert "services/caf\N{LATIN SMALL LETTER E WITH ACUTE} file.py" in paths, paths
|
|
228
|
+
assert decode_git_c_path(
|
|
229
|
+
'"caf\N{LATIN SMALL LETTER E WITH ACUTE} file\\\\name.md"', strict_utf8=True
|
|
230
|
+
) == "caf\N{LATIN SMALL LETTER E WITH ACUTE} file\\name.md"
|
|
231
|
+
assert decode_git_c_path('"caf\\303\\251.md"', strict_utf8=True) == "caf\N{LATIN SMALL LETTER E WITH ACUTE}.md"
|
|
232
|
+
assert decode_git_c_path('"python\\x2descape.md"', strict_utf8=True) is None
|
|
233
|
+
assert decode_git_c_path('"bad\\377.md"', strict_utf8=True) is None
|
|
234
|
+
assert decode_git_c_path('"bad\\400.md"', strict_utf8=True) is None
|
|
235
|
+
assert decode_git_c_path('"bad\\777.md"', strict_utf8=True) is None
|
|
223
236
|
assert "skills/fake-owner/SKILL.md" not in paths, paths
|
|
224
237
|
assert not any(
|
|
225
238
|
row["skill"] == "README.md"
|
|
@@ -556,6 +569,71 @@ if [ "$?" -ne 0 ]; then
|
|
|
556
569
|
exit 1
|
|
557
570
|
fi
|
|
558
571
|
|
|
572
|
+
PYTHONPATH="$WORK/harness/scripts" python3 - "$WORK" <<'PY'
|
|
573
|
+
from pathlib import Path
|
|
574
|
+
import sys
|
|
575
|
+
import unicodedata
|
|
576
|
+
|
|
577
|
+
from review_gate import (
|
|
578
|
+
_validate_untracked_path_text,
|
|
579
|
+
GateError,
|
|
580
|
+
candidate_paths_from_packet,
|
|
581
|
+
derive_owner_selection,
|
|
582
|
+
render_untracked_file,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
registry = Path(sys.argv[1])
|
|
586
|
+
probe = registry / "untracked-path-probe"
|
|
587
|
+
probe.write_text("probe\n", encoding="utf-8")
|
|
588
|
+
metadata = probe.stat()
|
|
589
|
+
probe.unlink()
|
|
590
|
+
|
|
591
|
+
relative_paths = (
|
|
592
|
+
"skills/testing-strategy/references/café.py",
|
|
593
|
+
"skills/testing-strategy/references/emoji-🚀.py",
|
|
594
|
+
"skills/testing-strategy/references/space name.py",
|
|
595
|
+
'skills/testing-strategy/references/quote"name.py',
|
|
596
|
+
"skills/testing-strategy/references/back\\slash.py",
|
|
597
|
+
)
|
|
598
|
+
for relative in relative_paths:
|
|
599
|
+
packet = render_untracked_file(relative, b"print('ok')\n", metadata)
|
|
600
|
+
paths = candidate_paths_from_packet(packet)
|
|
601
|
+
assert paths == [relative], (relative, paths, packet)
|
|
602
|
+
|
|
603
|
+
for codepoint in range(sys.maxunicode + 1):
|
|
604
|
+
character = chr(codepoint)
|
|
605
|
+
if unicodedata.category(character) not in {"Cc", "Zl", "Zp"}:
|
|
606
|
+
continue
|
|
607
|
+
try:
|
|
608
|
+
_validate_untracked_path_text(f"before{character}after.py")
|
|
609
|
+
except GateError as exc:
|
|
610
|
+
assert "control-character" in exc.reason, (codepoint, exc.reason)
|
|
611
|
+
else:
|
|
612
|
+
raise AssertionError(f"transport control U+{codepoint:04X} was accepted")
|
|
613
|
+
|
|
614
|
+
owners = {
|
|
615
|
+
row["skill"]
|
|
616
|
+
for row in derive_owner_selection([relative_paths[0]], registry)
|
|
617
|
+
}
|
|
618
|
+
assert {"python-service-dev", "testing-strategy"}.issubset(owners), owners
|
|
619
|
+
|
|
620
|
+
try:
|
|
621
|
+
render_untracked_file(
|
|
622
|
+
"skills/testing-strategy/references/non-utf8-\udcff.py",
|
|
623
|
+
b"print('ok')\n",
|
|
624
|
+
metadata,
|
|
625
|
+
)
|
|
626
|
+
except GateError as exc:
|
|
627
|
+
assert exc.reason_code == "invalid_input", exc.reason_code
|
|
628
|
+
assert "non-UTF-8 untracked path" in exc.reason, exc.reason
|
|
629
|
+
else:
|
|
630
|
+
raise AssertionError("surrogate-bearing untracked path did not fail closed")
|
|
631
|
+
PY
|
|
632
|
+
if [ "$?" -ne 0 ]; then
|
|
633
|
+
printf 'FAIL - untracked path rendering round-trips Unicode, quoting, and owner routing\n' >&2
|
|
634
|
+
exit 1
|
|
635
|
+
fi
|
|
636
|
+
|
|
559
637
|
cat >"$WORK/harness/scripts/claude_review.sh" <<'CLAUDE_STUB'
|
|
560
638
|
#!/usr/bin/env bash
|
|
561
639
|
set -u
|
|
@@ -614,6 +692,10 @@ print(json.dumps([
|
|
|
614
692
|
),
|
|
615
693
|
}
|
|
616
694
|
for item in profile["required_concerns"]
|
|
695
|
+
if not (
|
|
696
|
+
sys.argv[2] == "missing_wording_boundary"
|
|
697
|
+
and item["id"] == "wording_only_boundary"
|
|
698
|
+
)
|
|
617
699
|
], separators=(",", ":")))
|
|
618
700
|
PY
|
|
619
701
|
)"
|
|
@@ -621,6 +703,7 @@ native_skill_binding="not_requested"
|
|
|
621
703
|
[ -z "$review_skills" ] || native_skill_binding="established"
|
|
622
704
|
case "$behavior" in
|
|
623
705
|
passed) printf '{"mode":"%s","native_skill_binding":"%s","concern_results":%s,"findings":[]}\n' "$mode" "$native_skill_binding" "$concern_results"; exit 0 ;;
|
|
706
|
+
missing_wording_boundary) printf '{"mode":"%s","native_skill_binding":"%s","concern_results":%s,"findings":[]}\n' "$mode" "$native_skill_binding" "$concern_results"; exit 0 ;;
|
|
624
707
|
missing_binding) printf '{"mode":"%s","concern_results":%s,"findings":[]}\n' "$mode" "$concern_results"; exit 0 ;;
|
|
625
708
|
passed_slow)
|
|
626
709
|
sleep 5
|
|
@@ -1300,6 +1383,38 @@ check "a non-regular entrypoint in an explicit owner package is terminal" \
|
|
|
1300
1383
|
rmdir "$WORK/testing-strategy/SKILL.md"
|
|
1301
1384
|
mv "$WORK/testing-strategy/SKILL.md.real" "$WORK/testing-strategy/SKILL.md"
|
|
1302
1385
|
|
|
1386
|
+
python3 - "$WORK/harness/scripts/oversized-runtime.py" <<'PY'
|
|
1387
|
+
import os
|
|
1388
|
+
import sys
|
|
1389
|
+
|
|
1390
|
+
path = sys.argv[1]
|
|
1391
|
+
open(path, "wb").close()
|
|
1392
|
+
os.truncate(path, 1_048_577)
|
|
1393
|
+
PY
|
|
1394
|
+
reset_case passed unavailable unavailable
|
|
1395
|
+
out="$(run_gate)"; rc=$?
|
|
1396
|
+
check "an oversized controller-runtime file fails before provider execution" \
|
|
1397
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=local_tool_failure'
|
|
1398
|
+
rm -f "$WORK/harness/scripts/oversized-runtime.py"
|
|
1399
|
+
|
|
1400
|
+
mkdir -p "$WORK/harness/scripts/runtime-aggregate"
|
|
1401
|
+
python3 - "$WORK/harness/scripts/runtime-aggregate" <<'PY'
|
|
1402
|
+
import os
|
|
1403
|
+
import sys
|
|
1404
|
+
from pathlib import Path
|
|
1405
|
+
|
|
1406
|
+
root = Path(sys.argv[1])
|
|
1407
|
+
for index in range(9):
|
|
1408
|
+
path = root / f"part-{index}.py"
|
|
1409
|
+
path.touch()
|
|
1410
|
+
os.truncate(path, 1_000_000)
|
|
1411
|
+
PY
|
|
1412
|
+
reset_case passed unavailable unavailable
|
|
1413
|
+
out="$(run_gate)"; rc=$?
|
|
1414
|
+
check "an oversized controller-runtime aggregate fails before provider execution" \
|
|
1415
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=local_tool_failure'
|
|
1416
|
+
rm -rf "$WORK/harness/scripts/runtime-aggregate"
|
|
1417
|
+
|
|
1303
1418
|
reset_case passed unavailable unavailable
|
|
1304
1419
|
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
1305
1420
|
--mode review --cwd "$WORK/repo" --diff-file "$WORK/large-diff.patch" \
|
|
@@ -1321,6 +1436,279 @@ out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.s
|
|
|
1321
1436
|
check "a valid plan near the documented 32 KB input limit can render its profile" \
|
|
1322
1437
|
'[ "$(wc -c <"$WORK/near-limit-plan.json")" -le 32000 ] && [ "$rc" = 0 ] && [ "$(cat "$WORK/state/client_sequence")" = claude ]'
|
|
1323
1438
|
|
|
1439
|
+
ln -s "$WORK/review-plan.json" "$WORK/symlink-review-plan.json"
|
|
1440
|
+
reset_case passed unavailable unavailable
|
|
1441
|
+
out="$(run_gate --review-plan-file "$WORK/symlink-review-plan.json")"; rc=$?
|
|
1442
|
+
check "a symlinked review plan fails before provider execution" \
|
|
1443
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input'
|
|
1444
|
+
unlink "$WORK/symlink-review-plan.json"
|
|
1445
|
+
|
|
1446
|
+
cp "$WORK/review-plan.json" "$WORK/hardlink-review-plan.json"
|
|
1447
|
+
ln "$WORK/hardlink-review-plan.json" "$WORK/hardlink-review-plan-alias.json"
|
|
1448
|
+
reset_case passed unavailable unavailable
|
|
1449
|
+
out="$(run_gate --review-plan-file "$WORK/hardlink-review-plan.json")"; rc=$?
|
|
1450
|
+
check "a hardlinked review plan fails before provider execution" \
|
|
1451
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input'
|
|
1452
|
+
unlink "$WORK/hardlink-review-plan-alias.json"
|
|
1453
|
+
unlink "$WORK/hardlink-review-plan.json"
|
|
1454
|
+
|
|
1455
|
+
mkfifo "$WORK/fifo-review-plan.json"
|
|
1456
|
+
reset_case passed unavailable unavailable
|
|
1457
|
+
fifo_probe="$(python3 - \
|
|
1458
|
+
"$WORK/harness/scripts/review_gate.py" \
|
|
1459
|
+
"$WORK/repo" \
|
|
1460
|
+
"$WORK/diff.patch" \
|
|
1461
|
+
"$WORK/fifo-review-plan.json" \
|
|
1462
|
+
"$WORK/state" <<'PY'
|
|
1463
|
+
import json
|
|
1464
|
+
import os
|
|
1465
|
+
import subprocess
|
|
1466
|
+
import sys
|
|
1467
|
+
|
|
1468
|
+
gate, cwd, diff, plan, state = sys.argv[1:]
|
|
1469
|
+
environment = os.environ.copy()
|
|
1470
|
+
environment["REVIEW_GATE_TEST_STATE"] = state
|
|
1471
|
+
try:
|
|
1472
|
+
completed = subprocess.run(
|
|
1473
|
+
[
|
|
1474
|
+
sys.executable,
|
|
1475
|
+
gate,
|
|
1476
|
+
"--mode", "review",
|
|
1477
|
+
"--cwd", cwd,
|
|
1478
|
+
"--diff-file", diff,
|
|
1479
|
+
"--implementer-family", "openai",
|
|
1480
|
+
"--review-plan-file", plan,
|
|
1481
|
+
],
|
|
1482
|
+
text=True,
|
|
1483
|
+
stdout=subprocess.PIPE,
|
|
1484
|
+
stderr=subprocess.PIPE,
|
|
1485
|
+
timeout=1,
|
|
1486
|
+
env=environment,
|
|
1487
|
+
check=False,
|
|
1488
|
+
)
|
|
1489
|
+
except subprocess.TimeoutExpired as exc:
|
|
1490
|
+
raise AssertionError("FIFO review-plan open blocked") from exc
|
|
1491
|
+
assert completed.returncode == 2, (completed.returncode, completed.stdout, completed.stderr)
|
|
1492
|
+
payload = json.loads(completed.stdout)
|
|
1493
|
+
assert payload["reason_code"] == "invalid_input", payload
|
|
1494
|
+
assert "Traceback" not in completed.stderr, completed.stderr
|
|
1495
|
+
print("ok")
|
|
1496
|
+
PY
|
|
1497
|
+
)"
|
|
1498
|
+
fifo_probe_rc=$?
|
|
1499
|
+
check "a FIFO review plan fails quickly before provider execution" \
|
|
1500
|
+
'[ "$fifo_probe_rc" = 0 ] && [ "$fifo_probe" = ok ] && [ ! -e "$WORK/state/client_sequence" ]'
|
|
1501
|
+
|
|
1502
|
+
python3 - "$WORK/review-plan.json" "$WORK/surrogate-review-plan.json" <<'PY'
|
|
1503
|
+
import json
|
|
1504
|
+
import sys
|
|
1505
|
+
from pathlib import Path
|
|
1506
|
+
|
|
1507
|
+
source, target = map(Path, sys.argv[1:])
|
|
1508
|
+
plan = json.loads(source.read_text(encoding="utf-8"))
|
|
1509
|
+
plan["intent"] = "Reject this escaped lone surrogate: \ud800"
|
|
1510
|
+
target.write_text(json.dumps(plan, ensure_ascii=True), encoding="utf-8")
|
|
1511
|
+
PY
|
|
1512
|
+
reset_case passed unavailable unavailable
|
|
1513
|
+
out="$(run_gate --review-plan-file "$WORK/surrogate-review-plan.json" 2>&1)"; rc=$?
|
|
1514
|
+
check "an escaped lone surrogate is a stable rc2 before provider execution" \
|
|
1515
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input completion_gated=true'
|
|
1516
|
+
|
|
1517
|
+
python3 - "$WORK/oversized-review-plan.json" <<'PY'
|
|
1518
|
+
import sys
|
|
1519
|
+
from pathlib import Path
|
|
1520
|
+
|
|
1521
|
+
Path(sys.argv[1]).write_bytes(b"x" * 32001)
|
|
1522
|
+
PY
|
|
1523
|
+
reset_case passed unavailable unavailable
|
|
1524
|
+
out="$(run_gate --review-plan-file "$WORK/oversized-review-plan.json")"; rc=$?
|
|
1525
|
+
check "an oversized review plan fails before provider execution" \
|
|
1526
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input'
|
|
1527
|
+
|
|
1528
|
+
# The path checks for diff/prior/completion inputs must bind the bytes to one
|
|
1529
|
+
# already-open descriptor. Simulate the old check-then-open race by replacing a
|
|
1530
|
+
# regular path with a symlink immediately after Path.is_symlink() reports clean.
|
|
1531
|
+
# A secure open-once implementation never calls that pathname predicate, so it
|
|
1532
|
+
# either reads the original inode or rejects the input; it can never consume the
|
|
1533
|
+
# alternate bytes behind the replacement link.
|
|
1534
|
+
file_input_race_probe="$(PYTHONPATH="$WORK/harness/scripts" python3 - "$WORK" <<'PY' 2>&1
|
|
1535
|
+
import contextlib
|
|
1536
|
+
import ast
|
|
1537
|
+
import hashlib
|
|
1538
|
+
import json
|
|
1539
|
+
import os
|
|
1540
|
+
from pathlib import Path
|
|
1541
|
+
from types import SimpleNamespace
|
|
1542
|
+
import sys
|
|
1543
|
+
import time
|
|
1544
|
+
|
|
1545
|
+
import review_gate
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
@contextlib.contextmanager
|
|
1549
|
+
def replace_after_symlink_check(source: Path, alternate: Path):
|
|
1550
|
+
original_is_symlink = Path.is_symlink
|
|
1551
|
+
backup = source.with_name(source.name + ".before-link-swap")
|
|
1552
|
+
swapped = False
|
|
1553
|
+
|
|
1554
|
+
def injecting_is_symlink(path: Path) -> bool:
|
|
1555
|
+
nonlocal swapped
|
|
1556
|
+
result = original_is_symlink(path)
|
|
1557
|
+
if path == source and not swapped:
|
|
1558
|
+
source.rename(backup)
|
|
1559
|
+
source.symlink_to(alternate)
|
|
1560
|
+
swapped = True
|
|
1561
|
+
return result
|
|
1562
|
+
|
|
1563
|
+
Path.is_symlink = injecting_is_symlink
|
|
1564
|
+
try:
|
|
1565
|
+
yield
|
|
1566
|
+
finally:
|
|
1567
|
+
Path.is_symlink = original_is_symlink
|
|
1568
|
+
if swapped:
|
|
1569
|
+
source.unlink()
|
|
1570
|
+
backup.rename(source)
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
@contextlib.contextmanager
|
|
1574
|
+
def replace_after_first_fstat(source: Path, alternate: Path):
|
|
1575
|
+
"""Replace the pathname after the reader has already opened its inode."""
|
|
1576
|
+
|
|
1577
|
+
original_fstat = os.fstat
|
|
1578
|
+
original_inode = source.stat().st_ino
|
|
1579
|
+
backup = source.with_name(source.name + ".after-open-link-swap")
|
|
1580
|
+
swapped = False
|
|
1581
|
+
|
|
1582
|
+
def injecting_fstat(fd: int):
|
|
1583
|
+
nonlocal swapped
|
|
1584
|
+
metadata = original_fstat(fd)
|
|
1585
|
+
if metadata.st_ino == original_inode and not swapped:
|
|
1586
|
+
source.rename(backup)
|
|
1587
|
+
source.symlink_to(alternate)
|
|
1588
|
+
swapped = True
|
|
1589
|
+
return metadata
|
|
1590
|
+
|
|
1591
|
+
os.fstat = injecting_fstat
|
|
1592
|
+
try:
|
|
1593
|
+
yield
|
|
1594
|
+
finally:
|
|
1595
|
+
os.fstat = original_fstat
|
|
1596
|
+
if swapped:
|
|
1597
|
+
source.unlink()
|
|
1598
|
+
backup.rename(source)
|
|
1599
|
+
|
|
1600
|
+
|
|
1601
|
+
root = Path(sys.argv[1]) / "file-input-race"
|
|
1602
|
+
root.mkdir()
|
|
1603
|
+
|
|
1604
|
+
original_diff = (
|
|
1605
|
+
b"diff --git a/safe b/safe\n--- a/safe\n+++ b/safe\n"
|
|
1606
|
+
b"@@ -1 +1 @@\n-old\n+safe\n"
|
|
1607
|
+
)
|
|
1608
|
+
alternate_diff = (
|
|
1609
|
+
b"diff --git a/other b/other\n--- a/other\n+++ b/other\n"
|
|
1610
|
+
b"@@ -1 +1 @@\n-old\n+alternate\n"
|
|
1611
|
+
)
|
|
1612
|
+
diff_source = root / "candidate.patch"
|
|
1613
|
+
diff_alternate = root / "alternate.patch"
|
|
1614
|
+
diff_source.write_bytes(original_diff)
|
|
1615
|
+
diff_alternate.write_bytes(alternate_diff)
|
|
1616
|
+
with replace_after_symlink_check(diff_source, diff_alternate):
|
|
1617
|
+
packet_path, digest, _, _ = review_gate.freeze_packet(
|
|
1618
|
+
SimpleNamespace(
|
|
1619
|
+
cwd=str(root), diff_file=str(diff_source), base=None, paths=[]
|
|
1620
|
+
),
|
|
1621
|
+
time.monotonic() + 5,
|
|
1622
|
+
)
|
|
1623
|
+
try:
|
|
1624
|
+
assert packet_path.read_bytes() == original_diff
|
|
1625
|
+
assert digest == hashlib.sha256(original_diff).hexdigest()
|
|
1626
|
+
finally:
|
|
1627
|
+
packet_path.unlink()
|
|
1628
|
+
|
|
1629
|
+
# Both --prior-review-result-file and --completion-review-result-file consume
|
|
1630
|
+
# this loader. Exercise two independent path replacements so neither call site
|
|
1631
|
+
# can regress to check-then-open behavior under a later refactor.
|
|
1632
|
+
for label in ("prior", "completion"):
|
|
1633
|
+
source = root / f"{label}.json"
|
|
1634
|
+
alternate = root / f"{label}-alternate.json"
|
|
1635
|
+
original_bytes = json.dumps({"source": label}).encode()
|
|
1636
|
+
alternate.write_text(json.dumps({"source": "alternate"}), encoding="utf-8")
|
|
1637
|
+
source.write_bytes(original_bytes)
|
|
1638
|
+
with replace_after_symlink_check(source, alternate):
|
|
1639
|
+
payload, digest = review_gate._load_prior_review_result(str(source), 1)
|
|
1640
|
+
assert payload == {"source": label}
|
|
1641
|
+
assert digest == hashlib.sha256(original_bytes).hexdigest()
|
|
1642
|
+
|
|
1643
|
+
# Opening once prevents following an attacker-selected replacement, but the
|
|
1644
|
+
# gate also needs to reject that the pathname stopped naming the opened inode.
|
|
1645
|
+
# Otherwise a successful result can be attributed to bytes that are no longer
|
|
1646
|
+
# the candidate at the point the freeze returns.
|
|
1647
|
+
swap_source = root / "opened-then-swapped.patch"
|
|
1648
|
+
swap_alternate = root / "opened-then-swapped-alternate.patch"
|
|
1649
|
+
swap_source.write_bytes(original_diff)
|
|
1650
|
+
swap_alternate.write_bytes(alternate_diff)
|
|
1651
|
+
with replace_after_first_fstat(swap_source, swap_alternate):
|
|
1652
|
+
try:
|
|
1653
|
+
review_gate.freeze_packet(
|
|
1654
|
+
SimpleNamespace(
|
|
1655
|
+
cwd=str(root), diff_file=str(swap_source), base=None, paths=[]
|
|
1656
|
+
),
|
|
1657
|
+
time.monotonic() + 5,
|
|
1658
|
+
)
|
|
1659
|
+
except review_gate.GateError as exc:
|
|
1660
|
+
assert "changed" in exc.reason, exc.reason
|
|
1661
|
+
else:
|
|
1662
|
+
raise AssertionError("a pathname replaced after open was accepted")
|
|
1663
|
+
|
|
1664
|
+
# Frozen packet/profile verification and owner/controller hashing used to
|
|
1665
|
+
# reopen by pathname as well. Keep a structural assertion beside the live race
|
|
1666
|
+
# probes so a new Path.read_bytes() call cannot silently recreate that class.
|
|
1667
|
+
tree = ast.parse(Path(review_gate.__file__).read_text(encoding="utf-8"))
|
|
1668
|
+
path_reads = [
|
|
1669
|
+
node
|
|
1670
|
+
for node in ast.walk(tree)
|
|
1671
|
+
if isinstance(node, ast.Call)
|
|
1672
|
+
and isinstance(node.func, ast.Attribute)
|
|
1673
|
+
and node.func.attr in {"read_bytes", "read_text"}
|
|
1674
|
+
]
|
|
1675
|
+
assert not path_reads, [(node.func.attr, node.lineno) for node in path_reads]
|
|
1676
|
+
|
|
1677
|
+
# Resource bounds are checked from fstat before reading. A sparse oversized
|
|
1678
|
+
# owner file must fail closed instead of allocating its declared size, and an
|
|
1679
|
+
# individually valid set must still respect the package aggregate ceiling.
|
|
1680
|
+
oversized_skill = root / "oversized-skill"
|
|
1681
|
+
oversized_skill.mkdir()
|
|
1682
|
+
(oversized_skill / "SKILL.md").touch()
|
|
1683
|
+
os.truncate(oversized_skill / "SKILL.md", 1_048_577)
|
|
1684
|
+
try:
|
|
1685
|
+
review_gate._hash_skill_package(oversized_skill, "oversized-skill")
|
|
1686
|
+
except review_gate.GateError as exc:
|
|
1687
|
+
assert exc.reason_code == "local_tool_failure", exc.reason_code
|
|
1688
|
+
else:
|
|
1689
|
+
raise AssertionError("an oversized selected-skill file was hashed")
|
|
1690
|
+
|
|
1691
|
+
aggregate_skill = root / "aggregate-skill"
|
|
1692
|
+
(aggregate_skill / "references").mkdir(parents=True)
|
|
1693
|
+
(aggregate_skill / "SKILL.md").write_text("# aggregate\n", encoding="utf-8")
|
|
1694
|
+
for index in range(9):
|
|
1695
|
+
part = aggregate_skill / "references" / f"part-{index}.md"
|
|
1696
|
+
part.touch()
|
|
1697
|
+
os.truncate(part, 1_000_000)
|
|
1698
|
+
try:
|
|
1699
|
+
review_gate._hash_skill_package(aggregate_skill, "aggregate-skill")
|
|
1700
|
+
except review_gate.GateError as exc:
|
|
1701
|
+
assert exc.reason_code == "local_tool_failure", exc.reason_code
|
|
1702
|
+
else:
|
|
1703
|
+
raise AssertionError("an oversized selected-skill package was hashed")
|
|
1704
|
+
|
|
1705
|
+
print("open_once_file_inputs_ok")
|
|
1706
|
+
PY
|
|
1707
|
+
)"
|
|
1708
|
+
file_input_race_rc=$?
|
|
1709
|
+
check "diff, prior, and completion inputs are read once from a bounded opened descriptor" \
|
|
1710
|
+
'[ "$file_input_race_rc" = 0 ] && [ "$file_input_race_probe" = open_once_file_inputs_ok ]'
|
|
1711
|
+
|
|
1324
1712
|
reset_case missing_coverage passed unavailable
|
|
1325
1713
|
out="$(run_gate --diff-file "$WORK/secret-diff.patch")"; rc=$?
|
|
1326
1714
|
check "missing coverage cannot widen egress for a secret-bearing diff without approval" \
|
|
@@ -1665,14 +2053,1065 @@ out="$(run_gate --stage explore --risk-tag shared-gate --review-plan-file "$WORK
|
|
|
1665
2053
|
check "high-risk tags raise explore to release depth and default one challenge" \
|
|
1666
2054
|
'[ "$rc" = 0 ] && json_fields "$out" stage=explore stage_source=caller-declared review_depth=release risk_tags_source=caller-declared challenge_budget=1 challenge_rounds_remaining=1 review_chain_tracked=true review_chain_id=high-risk-task autonomous_review_budget=2 autonomous_review_index=1 autonomous_reviews_remaining=1 autonomous_review_allowed=true next_action=run_challenge completion_gated=true risk_tags.0=shared-gate reviewed_concerns.7=high_risk_boundary self_review_gate.required=false self_review_gate.satisfied_triggers.0=before_external_review self_review_gate.satisfied_triggers.1=risk_or_scope_escalation'
|
|
1667
2055
|
|
|
2056
|
+
# Release/high-risk normally requires a challenge. The only single-review
|
|
2057
|
+
# exception is a candidate-bound deterministic wording-only proof whose result
|
|
2058
|
+
# the controller can reproduce from the frozen canonical unified packet.
|
|
2059
|
+
mkdir -p "$WORK/wording-reviewed-repo/skills/code-review/references"
|
|
2060
|
+
printf '%s\n' '# Code Review' \
|
|
2061
|
+
>"$WORK/wording-reviewed-repo/skills/code-review/SKILL.md"
|
|
2062
|
+
for target in \
|
|
2063
|
+
fenced-backtick.md \
|
|
2064
|
+
fenced-tilde.md \
|
|
2065
|
+
fenced-unclosed.md \
|
|
2066
|
+
fenced-list-bullet.md \
|
|
2067
|
+
fenced-list-ordered.md \
|
|
2068
|
+
fenced-list-indented.md \
|
|
2069
|
+
indented-space.md \
|
|
2070
|
+
indented-tab.md \
|
|
2071
|
+
indented-list.md \
|
|
2072
|
+
html-pre.md \
|
|
2073
|
+
html-code.md \
|
|
2074
|
+
inline-code.md \
|
|
2075
|
+
link-destination.md \
|
|
2076
|
+
heading-structure.md \
|
|
2077
|
+
list-structure.md \
|
|
2078
|
+
table-structure.md \
|
|
2079
|
+
after-closed-fence.md \
|
|
2080
|
+
after-closed-list-fence.md \
|
|
2081
|
+
ordinary-list-punctuation.md \
|
|
2082
|
+
numeric-merge.md \
|
|
2083
|
+
fenced-token.md \
|
|
2084
|
+
question-mark.md; do
|
|
2085
|
+
printf '%s\n' '# Existing Markdown fixture' \
|
|
2086
|
+
>"$WORK/wording-reviewed-repo/skills/code-review/references/$target"
|
|
2087
|
+
done
|
|
2088
|
+
mkdir -p "$WORK/repo/skills/code-review/references"
|
|
2089
|
+
printf '%s\n' '# Reviewed Code Review' >"$WORK/repo/skills/code-review/SKILL.md"
|
|
2090
|
+
for target in \
|
|
2091
|
+
punctuation.md \
|
|
2092
|
+
'café file.md' \
|
|
2093
|
+
typo.md \
|
|
2094
|
+
normative.md \
|
|
2095
|
+
zero-width.md \
|
|
2096
|
+
bidi-control.md \
|
|
2097
|
+
emoji-symbol.md \
|
|
2098
|
+
currency-symbol.md \
|
|
2099
|
+
decomposed-boundary.md \
|
|
2100
|
+
zwj-boundary.md; do
|
|
2101
|
+
printf '%s\n' '# Existing Markdown fixture' \
|
|
2102
|
+
>"$WORK/repo/skills/code-review/references/$target"
|
|
2103
|
+
done
|
|
2104
|
+
mkdir -p "$WORK/wording-missing-package-repo"
|
|
2105
|
+
mkdir -p "$WORK/wording-repo-only/skills/repo-only/references"
|
|
2106
|
+
printf '%s\n' '# Repo Only' \
|
|
2107
|
+
>"$WORK/wording-repo-only/skills/repo-only/SKILL.md"
|
|
2108
|
+
printf '%s\n' '# Existing Markdown fixture' \
|
|
2109
|
+
>"$WORK/wording-repo-only/skills/repo-only/references/punctuation.md"
|
|
2110
|
+
mkdir -p "$WORK/wording-linked-package-repo/skills"
|
|
2111
|
+
ln -s "$WORK/wording-reviewed-repo/skills/code-review" \
|
|
2112
|
+
"$WORK/wording-linked-package-repo/skills/code-review"
|
|
2113
|
+
mkdir -p "$WORK/wording-hardlinked-package-repo/skills/code-review"
|
|
2114
|
+
printf '%s\n' '# Hardlinked Code Review' \
|
|
2115
|
+
>"$WORK/wording-hardlinked-package-repo/skills/code-review/SKILL.md"
|
|
2116
|
+
ln "$WORK/wording-hardlinked-package-repo/skills/code-review/SKILL.md" \
|
|
2117
|
+
"$WORK/wording-hardlinked-package-repo/skills/code-review/SKILL.alias"
|
|
2118
|
+
mkdir -p "$WORK/wording-nonregular-package-repo/skills/code-review/SKILL.md"
|
|
2119
|
+
mkdir -p "$WORK/wording-oversized-package-repo/skills/code-review"
|
|
2120
|
+
python3 - "$WORK/wording-oversized-package-repo/skills/code-review/SKILL.md" <<'PY'
|
|
2121
|
+
import os
|
|
2122
|
+
from pathlib import Path
|
|
2123
|
+
import sys
|
|
2124
|
+
|
|
2125
|
+
path = Path(sys.argv[1])
|
|
2126
|
+
path.touch()
|
|
2127
|
+
os.truncate(path, 1_048_577)
|
|
2128
|
+
PY
|
|
2129
|
+
for unsafe_target in missing linked hardlinked nonregular oversized; do
|
|
2130
|
+
mkdir -p \
|
|
2131
|
+
"$WORK/wording-$unsafe_target-target-repo/skills/code-review/references"
|
|
2132
|
+
printf '%s\n' '# Code Review' \
|
|
2133
|
+
>"$WORK/wording-$unsafe_target-target-repo/skills/code-review/SKILL.md"
|
|
2134
|
+
done
|
|
2135
|
+
ln -s "$WORK/repo/skills/code-review/references/punctuation.md" \
|
|
2136
|
+
"$WORK/wording-linked-target-repo/skills/code-review/references/punctuation.md"
|
|
2137
|
+
printf '%s\n' '# Hardlinked Markdown fixture' \
|
|
2138
|
+
>"$WORK/wording-hardlinked-target-repo/skills/code-review/references/punctuation.md"
|
|
2139
|
+
ln "$WORK/wording-hardlinked-target-repo/skills/code-review/references/punctuation.md" \
|
|
2140
|
+
"$WORK/wording-hardlinked-target-repo/skills/code-review/references/punctuation.alias"
|
|
2141
|
+
mkdir \
|
|
2142
|
+
"$WORK/wording-nonregular-target-repo/skills/code-review/references/punctuation.md"
|
|
2143
|
+
python3 - "$WORK/wording-oversized-target-repo/skills/code-review/references/punctuation.md" <<'PY'
|
|
2144
|
+
import os
|
|
2145
|
+
from pathlib import Path
|
|
2146
|
+
import sys
|
|
2147
|
+
|
|
2148
|
+
path = Path(sys.argv[1])
|
|
2149
|
+
path.touch()
|
|
2150
|
+
os.truncate(path, 1_048_577)
|
|
2151
|
+
PY
|
|
2152
|
+
cat >"$WORK/wording-punctuation.patch" <<'DIFF'
|
|
2153
|
+
diff --git a/skills/code-review/references/punctuation.md b/skills/code-review/references/punctuation.md
|
|
2154
|
+
index 1111111..2222222 100644
|
|
2155
|
+
--- a/skills/code-review/references/punctuation.md
|
|
2156
|
+
+++ b/skills/code-review/references/punctuation.md
|
|
2157
|
+
@@ -1 +1 @@
|
|
2158
|
+
-Review remains exact.
|
|
2159
|
+
+Review remains exact!
|
|
2160
|
+
DIFF
|
|
2161
|
+
cat >"$WORK/repo-only-wording.patch" <<'DIFF'
|
|
2162
|
+
diff --git a/skills/repo-only/references/punctuation.md b/skills/repo-only/references/punctuation.md
|
|
2163
|
+
index 1111111..2222222 100644
|
|
2164
|
+
--- a/skills/repo-only/references/punctuation.md
|
|
2165
|
+
+++ b/skills/repo-only/references/punctuation.md
|
|
2166
|
+
@@ -1 +1 @@
|
|
2167
|
+
-Review remains exact.
|
|
2168
|
+
+Review remains exact!
|
|
2169
|
+
DIFF
|
|
2170
|
+
cat >"$WORK/fenced-backtick-wording.patch" <<'DIFF'
|
|
2171
|
+
diff --git a/skills/code-review/references/fenced-backtick.md b/skills/code-review/references/fenced-backtick.md
|
|
2172
|
+
index 1111111..2222222 100644
|
|
2173
|
+
--- a/skills/code-review/references/fenced-backtick.md
|
|
2174
|
+
+++ b/skills/code-review/references/fenced-backtick.md
|
|
2175
|
+
@@ -1,3 +1,3 @@
|
|
2176
|
+
```bash
|
|
2177
|
+
-command;;
|
|
2178
|
+
+command;
|
|
2179
|
+
```
|
|
2180
|
+
DIFF
|
|
2181
|
+
cat >"$WORK/fenced-tilde-wording.patch" <<'DIFF'
|
|
2182
|
+
diff --git a/skills/code-review/references/fenced-tilde.md b/skills/code-review/references/fenced-tilde.md
|
|
2183
|
+
index 1111111..2222222 100644
|
|
2184
|
+
--- a/skills/code-review/references/fenced-tilde.md
|
|
2185
|
+
+++ b/skills/code-review/references/fenced-tilde.md
|
|
2186
|
+
@@ -1,3 +1,3 @@
|
|
2187
|
+
~~~sh
|
|
2188
|
+
-command;;
|
|
2189
|
+
+command;
|
|
2190
|
+
~~~
|
|
2191
|
+
DIFF
|
|
2192
|
+
cat >"$WORK/after-closed-fence-wording.patch" <<'DIFF'
|
|
2193
|
+
diff --git a/skills/code-review/references/after-closed-fence.md b/skills/code-review/references/after-closed-fence.md
|
|
2194
|
+
index 1111111..2222222 100644
|
|
2195
|
+
--- a/skills/code-review/references/after-closed-fence.md
|
|
2196
|
+
+++ b/skills/code-review/references/after-closed-fence.md
|
|
2197
|
+
@@ -1,4 +1,4 @@
|
|
2198
|
+
```shell
|
|
2199
|
+
command stays unchanged
|
|
2200
|
+
```
|
|
2201
|
+
-Review remains exact.
|
|
2202
|
+
+Review remains exact!
|
|
2203
|
+
DIFF
|
|
2204
|
+
cat >"$WORK/fenced-unclosed-wording.patch" <<'DIFF'
|
|
2205
|
+
diff --git a/skills/code-review/references/fenced-unclosed.md b/skills/code-review/references/fenced-unclosed.md
|
|
2206
|
+
index 1111111..2222222 100644
|
|
2207
|
+
--- a/skills/code-review/references/fenced-unclosed.md
|
|
2208
|
+
+++ b/skills/code-review/references/fenced-unclosed.md
|
|
2209
|
+
@@ -1,2 +1,2 @@
|
|
2210
|
+
```bash
|
|
2211
|
+
-command;;
|
|
2212
|
+
+command;
|
|
2213
|
+
DIFF
|
|
2214
|
+
cat >"$WORK/fenced-list-bullet-wording.patch" <<'DIFF'
|
|
2215
|
+
diff --git a/skills/code-review/references/fenced-list-bullet.md b/skills/code-review/references/fenced-list-bullet.md
|
|
2216
|
+
index 1111111..2222222 100644
|
|
2217
|
+
--- a/skills/code-review/references/fenced-list-bullet.md
|
|
2218
|
+
+++ b/skills/code-review/references/fenced-list-bullet.md
|
|
2219
|
+
@@ -1,3 +1,3 @@
|
|
2220
|
+
- ```bash
|
|
2221
|
+
- command;;
|
|
2222
|
+
+ command;
|
|
2223
|
+
```
|
|
2224
|
+
DIFF
|
|
2225
|
+
cat >"$WORK/fenced-list-ordered-wording.patch" <<'DIFF'
|
|
2226
|
+
diff --git a/skills/code-review/references/fenced-list-ordered.md b/skills/code-review/references/fenced-list-ordered.md
|
|
2227
|
+
index 1111111..2222222 100644
|
|
2228
|
+
--- a/skills/code-review/references/fenced-list-ordered.md
|
|
2229
|
+
+++ b/skills/code-review/references/fenced-list-ordered.md
|
|
2230
|
+
@@ -1,3 +1,3 @@
|
|
2231
|
+
10. ~~~sh
|
|
2232
|
+
- command;;
|
|
2233
|
+
+ command;
|
|
2234
|
+
~~~
|
|
2235
|
+
DIFF
|
|
2236
|
+
python3 - "$WORK/fenced-list-indented-wording.patch" <<'PY'
|
|
2237
|
+
from pathlib import Path
|
|
2238
|
+
import sys
|
|
2239
|
+
|
|
2240
|
+
Path(sys.argv[1]).write_text(
|
|
2241
|
+
"diff --git a/skills/code-review/references/fenced-list-indented.md "
|
|
2242
|
+
"b/skills/code-review/references/fenced-list-indented.md\n"
|
|
2243
|
+
"index 1111111..2222222 100644\n"
|
|
2244
|
+
"--- a/skills/code-review/references/fenced-list-indented.md\n"
|
|
2245
|
+
"+++ b/skills/code-review/references/fenced-list-indented.md\n"
|
|
2246
|
+
"@@ -1,5 +1,5 @@\n"
|
|
2247
|
+
" - shell example\n"
|
|
2248
|
+
" \n"
|
|
2249
|
+
" ```sh\n"
|
|
2250
|
+
"- command;;\n"
|
|
2251
|
+
"+ command;\n"
|
|
2252
|
+
" ```\n",
|
|
2253
|
+
encoding="utf-8",
|
|
2254
|
+
)
|
|
2255
|
+
PY
|
|
2256
|
+
cat >"$WORK/indented-space-wording.patch" <<'DIFF'
|
|
2257
|
+
diff --git a/skills/code-review/references/indented-space.md b/skills/code-review/references/indented-space.md
|
|
2258
|
+
index 1111111..2222222 100644
|
|
2259
|
+
--- a/skills/code-review/references/indented-space.md
|
|
2260
|
+
+++ b/skills/code-review/references/indented-space.md
|
|
2261
|
+
@@ -1 +1 @@
|
|
2262
|
+
- command;;
|
|
2263
|
+
+ command;
|
|
2264
|
+
DIFF
|
|
2265
|
+
python3 - "$WORK/indented-tab-wording.patch" <<'PY'
|
|
2266
|
+
from pathlib import Path
|
|
2267
|
+
import sys
|
|
2268
|
+
|
|
2269
|
+
Path(sys.argv[1]).write_text(
|
|
2270
|
+
"diff --git a/skills/code-review/references/indented-tab.md "
|
|
2271
|
+
"b/skills/code-review/references/indented-tab.md\n"
|
|
2272
|
+
"index 1111111..2222222 100644\n"
|
|
2273
|
+
"--- a/skills/code-review/references/indented-tab.md\n"
|
|
2274
|
+
"+++ b/skills/code-review/references/indented-tab.md\n"
|
|
2275
|
+
"@@ -1 +1 @@\n"
|
|
2276
|
+
"-\tcommand;;\n"
|
|
2277
|
+
"+\tcommand;\n",
|
|
2278
|
+
encoding="utf-8",
|
|
2279
|
+
)
|
|
2280
|
+
PY
|
|
2281
|
+
cat >"$WORK/indented-list-wording.patch" <<'DIFF'
|
|
2282
|
+
diff --git a/skills/code-review/references/indented-list.md b/skills/code-review/references/indented-list.md
|
|
2283
|
+
index 1111111..2222222 100644
|
|
2284
|
+
--- a/skills/code-review/references/indented-list.md
|
|
2285
|
+
+++ b/skills/code-review/references/indented-list.md
|
|
2286
|
+
@@ -1,2 +1,2 @@
|
|
2287
|
+
- item
|
|
2288
|
+
- command;;
|
|
2289
|
+
+ command;
|
|
2290
|
+
DIFF
|
|
2291
|
+
cat >"$WORK/html-pre-wording.patch" <<'DIFF'
|
|
2292
|
+
diff --git a/skills/code-review/references/html-pre.md b/skills/code-review/references/html-pre.md
|
|
2293
|
+
index 1111111..2222222 100644
|
|
2294
|
+
--- a/skills/code-review/references/html-pre.md
|
|
2295
|
+
+++ b/skills/code-review/references/html-pre.md
|
|
2296
|
+
@@ -1,3 +1,3 @@
|
|
2297
|
+
<pre>
|
|
2298
|
+
-command;;
|
|
2299
|
+
+command;
|
|
2300
|
+
</pre>
|
|
2301
|
+
DIFF
|
|
2302
|
+
cat >"$WORK/html-code-wording.patch" <<'DIFF'
|
|
2303
|
+
diff --git a/skills/code-review/references/html-code.md b/skills/code-review/references/html-code.md
|
|
2304
|
+
index 1111111..2222222 100644
|
|
2305
|
+
--- a/skills/code-review/references/html-code.md
|
|
2306
|
+
+++ b/skills/code-review/references/html-code.md
|
|
2307
|
+
@@ -1,3 +1,3 @@
|
|
2308
|
+
<code>
|
|
2309
|
+
-command;;
|
|
2310
|
+
+command;
|
|
2311
|
+
</code>
|
|
2312
|
+
DIFF
|
|
2313
|
+
cat >"$WORK/inline-code-wording.patch" <<'DIFF'
|
|
2314
|
+
diff --git a/skills/code-review/references/inline-code.md b/skills/code-review/references/inline-code.md
|
|
2315
|
+
index 1111111..2222222 100644
|
|
2316
|
+
--- a/skills/code-review/references/inline-code.md
|
|
2317
|
+
+++ b/skills/code-review/references/inline-code.md
|
|
2318
|
+
@@ -1 +1 @@
|
|
2319
|
+
-`command;;`
|
|
2320
|
+
+`command;`
|
|
2321
|
+
DIFF
|
|
2322
|
+
cat >"$WORK/link-destination-wording.patch" <<'DIFF'
|
|
2323
|
+
diff --git a/skills/code-review/references/link-destination.md b/skills/code-review/references/link-destination.md
|
|
2324
|
+
index 1111111..2222222 100644
|
|
2325
|
+
--- a/skills/code-review/references/link-destination.md
|
|
2326
|
+
+++ b/skills/code-review/references/link-destination.md
|
|
2327
|
+
@@ -1 +1 @@
|
|
2328
|
+
-[docs](#)
|
|
2329
|
+
+[docs](/)
|
|
2330
|
+
DIFF
|
|
2331
|
+
cat >"$WORK/heading-structure-wording.patch" <<'DIFF'
|
|
2332
|
+
diff --git a/skills/code-review/references/heading-structure.md b/skills/code-review/references/heading-structure.md
|
|
2333
|
+
index 1111111..2222222 100644
|
|
2334
|
+
--- a/skills/code-review/references/heading-structure.md
|
|
2335
|
+
+++ b/skills/code-review/references/heading-structure.md
|
|
2336
|
+
@@ -1 +1 @@
|
|
2337
|
+
-# Review remains exact.
|
|
2338
|
+
+## Review remains exact.
|
|
2339
|
+
DIFF
|
|
2340
|
+
cat >"$WORK/list-structure-wording.patch" <<'DIFF'
|
|
2341
|
+
diff --git a/skills/code-review/references/list-structure.md b/skills/code-review/references/list-structure.md
|
|
2342
|
+
index 1111111..2222222 100644
|
|
2343
|
+
--- a/skills/code-review/references/list-structure.md
|
|
2344
|
+
+++ b/skills/code-review/references/list-structure.md
|
|
2345
|
+
@@ -1 +1 @@
|
|
2346
|
+
-- Review remains exact.
|
|
2347
|
+
+* Review remains exact.
|
|
2348
|
+
DIFF
|
|
2349
|
+
cat >"$WORK/table-structure-wording.patch" <<'DIFF'
|
|
2350
|
+
diff --git a/skills/code-review/references/table-structure.md b/skills/code-review/references/table-structure.md
|
|
2351
|
+
index 1111111..2222222 100644
|
|
2352
|
+
--- a/skills/code-review/references/table-structure.md
|
|
2353
|
+
+++ b/skills/code-review/references/table-structure.md
|
|
2354
|
+
@@ -1 +1 @@
|
|
2355
|
+
-| Review remains exact |
|
|
2356
|
+
+| Review remains exact: |
|
|
2357
|
+
DIFF
|
|
2358
|
+
cat >"$WORK/after-closed-list-fence-wording.patch" <<'DIFF'
|
|
2359
|
+
diff --git a/skills/code-review/references/after-closed-list-fence.md b/skills/code-review/references/after-closed-list-fence.md
|
|
2360
|
+
index 1111111..2222222 100644
|
|
2361
|
+
--- a/skills/code-review/references/after-closed-list-fence.md
|
|
2362
|
+
+++ b/skills/code-review/references/after-closed-list-fence.md
|
|
2363
|
+
@@ -1,4 +1,4 @@
|
|
2364
|
+
- ```shell
|
|
2365
|
+
command stays unchanged
|
|
2366
|
+
```
|
|
2367
|
+
-Review remains exact.
|
|
2368
|
+
+Review remains exact!
|
|
2369
|
+
DIFF
|
|
2370
|
+
cat >"$WORK/ordinary-list-punctuation-wording.patch" <<'DIFF'
|
|
2371
|
+
diff --git a/skills/code-review/references/ordinary-list-punctuation.md b/skills/code-review/references/ordinary-list-punctuation.md
|
|
2372
|
+
index 1111111..2222222 100644
|
|
2373
|
+
--- a/skills/code-review/references/ordinary-list-punctuation.md
|
|
2374
|
+
+++ b/skills/code-review/references/ordinary-list-punctuation.md
|
|
2375
|
+
@@ -1 +1 @@
|
|
2376
|
+
-- Review remains exact.
|
|
2377
|
+
+- Review remains exact!
|
|
2378
|
+
DIFF
|
|
2379
|
+
cat >"$WORK/numeric-merge-wording.patch" <<'DIFF'
|
|
2380
|
+
diff --git a/skills/code-review/references/numeric-merge.md b/skills/code-review/references/numeric-merge.md
|
|
2381
|
+
index 1111111..2222222 100644
|
|
2382
|
+
--- a/skills/code-review/references/numeric-merge.md
|
|
2383
|
+
+++ b/skills/code-review/references/numeric-merge.md
|
|
2384
|
+
@@ -1 +1 @@
|
|
2385
|
+
-Retries wait 5.5 seconds in total.
|
|
2386
|
+
+Retries wait 55 seconds in total.
|
|
2387
|
+
DIFF
|
|
2388
|
+
cat >"$WORK/question-mark-wording.patch" <<'DIFF'
|
|
2389
|
+
diff --git a/skills/code-review/references/question-mark.md b/skills/code-review/references/question-mark.md
|
|
2390
|
+
index 1111111..2222222 100644
|
|
2391
|
+
--- a/skills/code-review/references/question-mark.md
|
|
2392
|
+
+++ b/skills/code-review/references/question-mark.md
|
|
2393
|
+
@@ -1 +1 @@
|
|
2394
|
+
-Agents must reject unsafe input.
|
|
2395
|
+
+Agents must reject unsafe input?
|
|
2396
|
+
DIFF
|
|
2397
|
+
cat >"$WORK/fenced-token-wording.patch" <<'DIFF'
|
|
2398
|
+
diff --git a/skills/code-review/references/fenced-token.md b/skills/code-review/references/fenced-token.md
|
|
2399
|
+
index 1111111..2222222 100644
|
|
2400
|
+
--- a/skills/code-review/references/fenced-token.md
|
|
2401
|
+
+++ b/skills/code-review/references/fenced-token.md
|
|
2402
|
+
@@ -1,3 +1,3 @@
|
|
2403
|
+
```shell
|
|
2404
|
+
-This teh command stays quoted.
|
|
2405
|
+
+This the command stays quoted.
|
|
2406
|
+
```
|
|
2407
|
+
DIFF
|
|
2408
|
+
cat >"$WORK/wording-token.patch" <<'DIFF'
|
|
2409
|
+
diff --git a/skills/code-review/references/typo.md b/skills/code-review/references/typo.md
|
|
2410
|
+
index 1111111..2222222 100644
|
|
2411
|
+
--- a/skills/code-review/references/typo.md
|
|
2412
|
+
+++ b/skills/code-review/references/typo.md
|
|
2413
|
+
@@ -1 +1 @@
|
|
2414
|
+
-This teh rule stays otherwise byte-identical.
|
|
2415
|
+
+This the rule stays otherwise byte-identical.
|
|
2416
|
+
DIFF
|
|
2417
|
+
cat >"$WORK/wording-normative-token.patch" <<'DIFF'
|
|
2418
|
+
diff --git a/skills/code-review/references/normative.md b/skills/code-review/references/normative.md
|
|
2419
|
+
index 1111111..2222222 100644
|
|
2420
|
+
--- a/skills/code-review/references/normative.md
|
|
2421
|
+
+++ b/skills/code-review/references/normative.md
|
|
2422
|
+
@@ -1 +1 @@
|
|
2423
|
+
-Agents must reject unsafe input.
|
|
2424
|
+
+Agents may reject unsafe input.
|
|
2425
|
+
DIFF
|
|
2426
|
+
cat >"$WORK/quoted-unicode-wording.patch" <<'DIFF'
|
|
2427
|
+
diff --git "a/skills/code-review/references/café file.md" "b/skills/code-review/references/café file.md"
|
|
2428
|
+
index 1111111..2222222 100644
|
|
2429
|
+
--- "a/skills/code-review/references/café file.md"
|
|
2430
|
+
+++ "b/skills/code-review/references/café file.md"
|
|
2431
|
+
@@ -1 +1 @@
|
|
2432
|
+
-Review remains exact.
|
|
2433
|
+
+Review remains exact!
|
|
2434
|
+
DIFF
|
|
2435
|
+
cat >"$WORK/non-wording-punctuation.patch" <<'DIFF'
|
|
2436
|
+
diff --git a/skills/demo/scripts/runtime.py b/skills/demo/scripts/runtime.py
|
|
2437
|
+
index 1111111..2222222 100644
|
|
2438
|
+
--- a/skills/demo/scripts/runtime.py
|
|
2439
|
+
+++ b/skills/demo/scripts/runtime.py
|
|
2440
|
+
@@ -1 +1 @@
|
|
2441
|
+
-.
|
|
2442
|
+
+!
|
|
2443
|
+
DIFF
|
|
2444
|
+
cat >"$WORK/multi-skill-wording.patch" <<'DIFF'
|
|
2445
|
+
diff --git a/skills/code-review/references/one.md b/skills/code-review/references/one.md
|
|
2446
|
+
index 1111111..2222222 100644
|
|
2447
|
+
--- a/skills/code-review/references/one.md
|
|
2448
|
+
+++ b/skills/code-review/references/one.md
|
|
2449
|
+
@@ -1 +1,2 @@
|
|
2450
|
+
First package.
|
|
2451
|
+
+***
|
|
2452
|
+
diff --git a/skills/testing-strategy/references/two.md b/skills/testing-strategy/references/two.md
|
|
2453
|
+
index 1111111..2222222 100644
|
|
2454
|
+
--- a/skills/testing-strategy/references/two.md
|
|
2455
|
+
+++ b/skills/testing-strategy/references/two.md
|
|
2456
|
+
@@ -1 +1,2 @@
|
|
2457
|
+
Second package.
|
|
2458
|
+
+***
|
|
2459
|
+
DIFF
|
|
2460
|
+
cat >"$WORK/truncated-context-wording.patch" <<'DIFF'
|
|
2461
|
+
diff --git a/skills/code-review/references/deep.md b/skills/code-review/references/deep.md
|
|
2462
|
+
index 1111111..2222222 100644
|
|
2463
|
+
--- a/skills/code-review/references/deep.md
|
|
2464
|
+
+++ b/skills/code-review/references/deep.md
|
|
2465
|
+
@@ -8 +8 @@
|
|
2466
|
+
-This teh rule is below omitted context.
|
|
2467
|
+
+This the rule is below omitted context.
|
|
2468
|
+
DIFF
|
|
2469
|
+
cat >"$WORK/symlink-mode-wording.patch" <<'DIFF'
|
|
2470
|
+
diff --git a/skills/code-review/references/link.md b/skills/code-review/references/link.md
|
|
2471
|
+
index 1111111..2222222 120000
|
|
2472
|
+
--- a/skills/code-review/references/link.md
|
|
2473
|
+
+++ b/skills/code-review/references/link.md
|
|
2474
|
+
@@ -1 +1 @@
|
|
2475
|
+
-targets/teh
|
|
2476
|
+
+targets/the
|
|
2477
|
+
DIFF
|
|
2478
|
+
cat >"$WORK/frontmatter-shift-insert-wording.patch" <<'DIFF'
|
|
2479
|
+
diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md
|
|
2480
|
+
index 1111111..2222222 100644
|
|
2481
|
+
--- a/skills/code-review/SKILL.md
|
|
2482
|
+
+++ b/skills/code-review/SKILL.md
|
|
2483
|
+
@@ -1,3 +1,4 @@
|
|
2484
|
+
+#
|
|
2485
|
+
---
|
|
2486
|
+
description: Canonical routing metadata.
|
|
2487
|
+
---
|
|
2488
|
+
DIFF
|
|
2489
|
+
cat >"$WORK/frontmatter-shift-delete-wording.patch" <<'DIFF'
|
|
2490
|
+
diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md
|
|
2491
|
+
index 1111111..2222222 100644
|
|
2492
|
+
--- a/skills/code-review/SKILL.md
|
|
2493
|
+
+++ b/skills/code-review/SKILL.md
|
|
2494
|
+
@@ -1,4 +1,3 @@
|
|
2495
|
+
-#
|
|
2496
|
+
---
|
|
2497
|
+
description: Canonical routing metadata.
|
|
2498
|
+
---
|
|
2499
|
+
DIFF
|
|
2500
|
+
cat >"$WORK/no-final-newline-wording.patch" <<'DIFF'
|
|
2501
|
+
diff --git a/skills/code-review/references/no-newline.md b/skills/code-review/references/no-newline.md
|
|
2502
|
+
index 1111111..2222222 100644
|
|
2503
|
+
--- a/skills/code-review/references/no-newline.md
|
|
2504
|
+
+++ b/skills/code-review/references/no-newline.md
|
|
2505
|
+
@@ -1 +1 @@
|
|
2506
|
+
-This teh token has no final newline.
|
|
2507
|
+
|
|
2508
|
+
+This the token has no final newline.
|
|
2509
|
+
|
|
2510
|
+
DIFF
|
|
2511
|
+
cat >"$WORK/invalid-octal-wording.patch" <<'DIFF'
|
|
2512
|
+
diff --git "a/skills/code-review/references/bad\777.md" "b/skills/code-review/references/bad\777.md"
|
|
2513
|
+
index 1111111..2222222 100644
|
|
2514
|
+
--- "a/skills/code-review/references/bad\777.md"
|
|
2515
|
+
+++ "b/skills/code-review/references/bad\777.md"
|
|
2516
|
+
@@ -1 +1,2 @@
|
|
2517
|
+
Plain context.
|
|
2518
|
+
+***
|
|
2519
|
+
DIFF
|
|
2520
|
+
python3 - "$WORK/huge-hunk-number-wording.patch" <<'PY'
|
|
2521
|
+
from pathlib import Path
|
|
2522
|
+
import sys
|
|
2523
|
+
|
|
2524
|
+
Path(sys.argv[1]).write_text(
|
|
2525
|
+
"diff --git a/skills/code-review/references/huge.md b/skills/code-review/references/huge.md\n"
|
|
2526
|
+
"index 1111111..2222222 100644\n"
|
|
2527
|
+
"--- a/skills/code-review/references/huge.md\n"
|
|
2528
|
+
"+++ b/skills/code-review/references/huge.md\n"
|
|
2529
|
+
f"@@ -{'9' * 5001} +1 @@\n"
|
|
2530
|
+
"-*\n"
|
|
2531
|
+
"+!\n",
|
|
2532
|
+
encoding="utf-8",
|
|
2533
|
+
)
|
|
2534
|
+
PY
|
|
2535
|
+
python3 - "$WORK" <<'PY'
|
|
2536
|
+
from pathlib import Path
|
|
2537
|
+
import sys
|
|
2538
|
+
|
|
2539
|
+
root = Path(sys.argv[1])
|
|
2540
|
+
for slug, value in (
|
|
2541
|
+
("zero-width", "\u200b"),
|
|
2542
|
+
("bidi-control", "\u202e"),
|
|
2543
|
+
("emoji-symbol", "\U0001f6a8"),
|
|
2544
|
+
("currency-symbol", "$$"),
|
|
2545
|
+
):
|
|
2546
|
+
(root / f"{slug}-wording.patch").write_text(
|
|
2547
|
+
f"diff --git a/skills/code-review/references/{slug}.md b/skills/code-review/references/{slug}.md\n"
|
|
2548
|
+
"index 1111111..2222222 100644\n"
|
|
2549
|
+
f"--- a/skills/code-review/references/{slug}.md\n"
|
|
2550
|
+
f"+++ b/skills/code-review/references/{slug}.md\n"
|
|
2551
|
+
"@@ -1 +1,2 @@\n"
|
|
2552
|
+
" Plain context.\n"
|
|
2553
|
+
f"+{value}\n",
|
|
2554
|
+
encoding="utf-8",
|
|
2555
|
+
)
|
|
2556
|
+
PY
|
|
2557
|
+
python3 - "$WORK" <<'PY'
|
|
2558
|
+
from pathlib import Path
|
|
2559
|
+
import sys
|
|
2560
|
+
|
|
2561
|
+
root = Path(sys.argv[1])
|
|
2562
|
+
for slug, old_line, new_line in (
|
|
2563
|
+
(
|
|
2564
|
+
"decomposed-boundary",
|
|
2565
|
+
"Context cafe\u0301 token.",
|
|
2566
|
+
"Context coffee\u0301 token.",
|
|
2567
|
+
),
|
|
2568
|
+
(
|
|
2569
|
+
"zwj-boundary",
|
|
2570
|
+
"Context admin\u200distrator token.",
|
|
2571
|
+
"Context root\u200distrator token.",
|
|
2572
|
+
),
|
|
2573
|
+
):
|
|
2574
|
+
(root / f"{slug}-wording.patch").write_text(
|
|
2575
|
+
f"diff --git a/skills/code-review/references/{slug}.md b/skills/code-review/references/{slug}.md\n"
|
|
2576
|
+
"index 1111111..2222222 100644\n"
|
|
2577
|
+
f"--- a/skills/code-review/references/{slug}.md\n"
|
|
2578
|
+
f"+++ b/skills/code-review/references/{slug}.md\n"
|
|
2579
|
+
"@@ -1 +1 @@\n"
|
|
2580
|
+
f"-{old_line}\n"
|
|
2581
|
+
f"+{new_line}\n",
|
|
2582
|
+
encoding="utf-8",
|
|
2583
|
+
)
|
|
2584
|
+
PY
|
|
2585
|
+
|
|
2586
|
+
write_wording_proof() { # <packet> <proof> <kind> [old new count]
|
|
2587
|
+
python3 - "$@" <<'PY'
|
|
2588
|
+
import hashlib
|
|
2589
|
+
import json
|
|
2590
|
+
from pathlib import Path
|
|
2591
|
+
import sys
|
|
2592
|
+
|
|
2593
|
+
packet_path, proof_path, kind, *replacement = sys.argv[1:]
|
|
2594
|
+
packet = Path(packet_path).read_bytes()
|
|
2595
|
+
candidate_sha = hashlib.sha256(packet).hexdigest()
|
|
2596
|
+
if kind == "markdown-punctuation-only":
|
|
2597
|
+
check = {"kind": kind}
|
|
2598
|
+
elif kind == "markdown-token-replacement":
|
|
2599
|
+
old, new, count_text = replacement
|
|
2600
|
+
check = {
|
|
2601
|
+
"kind": kind,
|
|
2602
|
+
"old_token": old,
|
|
2603
|
+
"new_token": new,
|
|
2604
|
+
"expected_count": int(count_text),
|
|
2605
|
+
}
|
|
2606
|
+
else:
|
|
2607
|
+
raise AssertionError(kind)
|
|
2608
|
+
proof = {
|
|
2609
|
+
"schema_version": 1,
|
|
2610
|
+
"candidate_sha256": candidate_sha,
|
|
2611
|
+
"check": check,
|
|
2612
|
+
}
|
|
2613
|
+
Path(proof_path).write_text(
|
|
2614
|
+
json.dumps(proof, sort_keys=True, separators=(",", ":")), encoding="utf-8"
|
|
2615
|
+
)
|
|
2616
|
+
PY
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
write_wording_proof "$WORK/wording-punctuation.patch" \
|
|
2620
|
+
"$WORK/wording-punctuation-proof.json" markdown-punctuation-only
|
|
2621
|
+
write_wording_proof "$WORK/repo-only-wording.patch" \
|
|
2622
|
+
"$WORK/repo-only-wording-proof.json" markdown-punctuation-only
|
|
2623
|
+
for fenced_scope in \
|
|
2624
|
+
fenced-backtick \
|
|
2625
|
+
fenced-tilde \
|
|
2626
|
+
fenced-unclosed \
|
|
2627
|
+
fenced-list-bullet \
|
|
2628
|
+
fenced-list-ordered \
|
|
2629
|
+
fenced-list-indented; do
|
|
2630
|
+
write_wording_proof "$WORK/$fenced_scope-wording.patch" \
|
|
2631
|
+
"$WORK/$fenced_scope-wording-proof.json" markdown-punctuation-only
|
|
2632
|
+
done
|
|
2633
|
+
for rejected_punctuation_scope in \
|
|
2634
|
+
indented-space \
|
|
2635
|
+
indented-tab \
|
|
2636
|
+
indented-list \
|
|
2637
|
+
html-pre \
|
|
2638
|
+
html-code \
|
|
2639
|
+
inline-code \
|
|
2640
|
+
link-destination \
|
|
2641
|
+
heading-structure \
|
|
2642
|
+
list-structure \
|
|
2643
|
+
table-structure \
|
|
2644
|
+
ordinary-list-punctuation \
|
|
2645
|
+
numeric-merge \
|
|
2646
|
+
question-mark; do
|
|
2647
|
+
write_wording_proof "$WORK/$rejected_punctuation_scope-wording.patch" \
|
|
2648
|
+
"$WORK/$rejected_punctuation_scope-wording-proof.json" \
|
|
2649
|
+
markdown-punctuation-only
|
|
2650
|
+
done
|
|
2651
|
+
write_wording_proof "$WORK/fenced-token-wording.patch" \
|
|
2652
|
+
"$WORK/fenced-token-wording-proof.json" markdown-token-replacement teh the 1
|
|
2653
|
+
for eligible_scope in \
|
|
2654
|
+
after-closed-fence \
|
|
2655
|
+
after-closed-list-fence; do
|
|
2656
|
+
write_wording_proof "$WORK/$eligible_scope-wording.patch" \
|
|
2657
|
+
"$WORK/$eligible_scope-wording-proof.json" markdown-punctuation-only
|
|
2658
|
+
done
|
|
2659
|
+
write_wording_proof "$WORK/wording-token.patch" \
|
|
2660
|
+
"$WORK/wording-token-proof.json" markdown-token-replacement teh the 1
|
|
2661
|
+
write_wording_proof "$WORK/wording-normative-token.patch" \
|
|
2662
|
+
"$WORK/wording-normative-token-proof.json" markdown-token-replacement must may 1
|
|
2663
|
+
write_wording_proof "$WORK/quoted-unicode-wording.patch" \
|
|
2664
|
+
"$WORK/quoted-unicode-wording-proof.json" markdown-punctuation-only
|
|
2665
|
+
write_wording_proof "$WORK/non-wording-punctuation.patch" \
|
|
2666
|
+
"$WORK/non-wording-proof.json" markdown-punctuation-only
|
|
2667
|
+
write_wording_proof "$WORK/multi-skill-wording.patch" \
|
|
2668
|
+
"$WORK/multi-skill-wording-proof.json" markdown-punctuation-only
|
|
2669
|
+
write_wording_proof "$WORK/truncated-context-wording.patch" \
|
|
2670
|
+
"$WORK/truncated-context-wording-proof.json" markdown-token-replacement teh the 1
|
|
2671
|
+
write_wording_proof "$WORK/symlink-mode-wording.patch" \
|
|
2672
|
+
"$WORK/symlink-mode-wording-proof.json" markdown-token-replacement teh the 1
|
|
2673
|
+
write_wording_proof "$WORK/frontmatter-shift-insert-wording.patch" \
|
|
2674
|
+
"$WORK/frontmatter-shift-insert-wording-proof.json" markdown-punctuation-only
|
|
2675
|
+
write_wording_proof "$WORK/frontmatter-shift-delete-wording.patch" \
|
|
2676
|
+
"$WORK/frontmatter-shift-delete-wording-proof.json" markdown-punctuation-only
|
|
2677
|
+
write_wording_proof "$WORK/no-final-newline-wording.patch" \
|
|
2678
|
+
"$WORK/no-final-newline-wording-proof.json" markdown-token-replacement teh the 1
|
|
2679
|
+
write_wording_proof "$WORK/invalid-octal-wording.patch" \
|
|
2680
|
+
"$WORK/invalid-octal-wording-proof.json" markdown-punctuation-only
|
|
2681
|
+
write_wording_proof "$WORK/huge-hunk-number-wording.patch" \
|
|
2682
|
+
"$WORK/huge-hunk-number-wording-proof.json" markdown-punctuation-only
|
|
2683
|
+
for rejected_character in zero-width bidi-control emoji-symbol currency-symbol; do
|
|
2684
|
+
write_wording_proof "$WORK/$rejected_character-wording.patch" \
|
|
2685
|
+
"$WORK/$rejected_character-wording-proof.json" markdown-punctuation-only
|
|
2686
|
+
done
|
|
2687
|
+
write_wording_proof "$WORK/decomposed-boundary-wording.patch" \
|
|
2688
|
+
"$WORK/decomposed-boundary-wording-proof.json" markdown-token-replacement cafe coffee 1
|
|
2689
|
+
write_wording_proof "$WORK/zwj-boundary-wording.patch" \
|
|
2690
|
+
"$WORK/zwj-boundary-wording-proof.json" markdown-token-replacement admin root 1
|
|
2691
|
+
|
|
2692
|
+
mkdir -p "$WORK/wording-base-repo/skills/code-review/references"
|
|
2693
|
+
git -C "$WORK/wording-base-repo" init -q
|
|
2694
|
+
git -C "$WORK/wording-base-repo" config user.name 'Review Gate Test'
|
|
2695
|
+
git -C "$WORK/wording-base-repo" config user.email 'review-gate@example.invalid'
|
|
2696
|
+
printf '%s\n' '# Code Review' \
|
|
2697
|
+
>"$WORK/wording-base-repo/skills/code-review/SKILL.md"
|
|
2698
|
+
cat >"$WORK/wording-base-repo/skills/code-review/references/long.md" <<'MARKDOWN'
|
|
2699
|
+
---
|
|
2700
|
+
title: Bounded example
|
|
2701
|
+
---
|
|
2702
|
+
# Review contract
|
|
2703
|
+
|
|
2704
|
+
Context line one.
|
|
2705
|
+
Context line two.
|
|
2706
|
+
Context line three.
|
|
2707
|
+
This teh token is below frontmatter and ordinary context.
|
|
2708
|
+
Context line four.
|
|
2709
|
+
MARKDOWN
|
|
2710
|
+
git -C "$WORK/wording-base-repo" add skills/code-review
|
|
2711
|
+
git -C "$WORK/wording-base-repo" commit -q -m base
|
|
2712
|
+
python3 - "$WORK/wording-base-repo/skills/code-review/references/long.md" <<'PY'
|
|
2713
|
+
from pathlib import Path
|
|
2714
|
+
import sys
|
|
2715
|
+
|
|
2716
|
+
path = Path(sys.argv[1])
|
|
2717
|
+
path.write_text(path.read_text().replace("This teh token", "This the token"))
|
|
2718
|
+
PY
|
|
2719
|
+
git -C "$WORK/wording-base-repo" diff --no-color --no-ext-diff --no-textconv \
|
|
2720
|
+
--unified=1000000 HEAD >"$WORK/wording-base.patch"
|
|
2721
|
+
write_wording_proof "$WORK/wording-base.patch" \
|
|
2722
|
+
"$WORK/wording-base-proof.json" markdown-token-replacement teh the 1
|
|
2723
|
+
|
|
2724
|
+
reset_case passed unavailable unavailable
|
|
2725
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2726
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2727
|
+
--cwd "$WORK/wording-missing-package-repo" \
|
|
2728
|
+
--diff-file "$WORK/wording-punctuation.patch" \
|
|
2729
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2730
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2731
|
+
check "wording proof cannot borrow a missing candidate package from the controller tree" \
|
|
2732
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2733
|
+
|
|
2734
|
+
reset_case passed unavailable unavailable
|
|
2735
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2736
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2737
|
+
--cwd "$WORK/wording-repo-only" \
|
|
2738
|
+
--diff-file "$WORK/repo-only-wording.patch" \
|
|
2739
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2740
|
+
--wording-only-proof-file "$WORK/repo-only-wording-proof.json")"; rc=$?
|
|
2741
|
+
check "wording proof accepts a candidate-only package absent from the controller tree" \
|
|
2742
|
+
'[ "$rc" = 0 ] && json_fields "$out" wording_only_scope.status=passed wording_only_scope.changed_files.0=skills/repo-only/references/punctuation.md'
|
|
2743
|
+
|
|
2744
|
+
for unsafe_package in linked hardlinked nonregular oversized; do
|
|
2745
|
+
reset_case passed unavailable unavailable
|
|
2746
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2747
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2748
|
+
--cwd "$WORK/wording-$unsafe_package-package-repo" \
|
|
2749
|
+
--diff-file "$WORK/wording-punctuation.patch" \
|
|
2750
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2751
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2752
|
+
check "wording proof rejects a $unsafe_package candidate package entrypoint" \
|
|
2753
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2754
|
+
done
|
|
2755
|
+
|
|
2756
|
+
for unsafe_target in missing linked hardlinked nonregular oversized; do
|
|
2757
|
+
reset_case passed unavailable unavailable
|
|
2758
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2759
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2760
|
+
--cwd "$WORK/wording-$unsafe_target-target-repo" \
|
|
2761
|
+
--diff-file "$WORK/wording-punctuation.patch" \
|
|
2762
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2763
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2764
|
+
check "wording proof rejects a $unsafe_target changed Markdown target" \
|
|
2765
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2766
|
+
done
|
|
2767
|
+
|
|
2768
|
+
for fenced_scope in \
|
|
2769
|
+
fenced-backtick \
|
|
2770
|
+
fenced-tilde \
|
|
2771
|
+
fenced-unclosed \
|
|
2772
|
+
fenced-list-bullet \
|
|
2773
|
+
fenced-list-ordered \
|
|
2774
|
+
fenced-list-indented; do
|
|
2775
|
+
reset_case passed unavailable unavailable
|
|
2776
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2777
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2778
|
+
--cwd "$WORK/wording-reviewed-repo" \
|
|
2779
|
+
--diff-file "$WORK/$fenced_scope-wording.patch" \
|
|
2780
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2781
|
+
--wording-only-proof-file "$WORK/$fenced_scope-wording-proof.json")"; rc=$?
|
|
2782
|
+
check "$fenced_scope cannot use the punctuation-only release waiver" \
|
|
2783
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2784
|
+
done
|
|
2785
|
+
|
|
2786
|
+
for rejected_punctuation_scope in \
|
|
2787
|
+
indented-space \
|
|
2788
|
+
indented-tab \
|
|
2789
|
+
indented-list \
|
|
2790
|
+
html-pre \
|
|
2791
|
+
html-code \
|
|
2792
|
+
inline-code \
|
|
2793
|
+
link-destination \
|
|
2794
|
+
heading-structure \
|
|
2795
|
+
list-structure \
|
|
2796
|
+
table-structure \
|
|
2797
|
+
ordinary-list-punctuation \
|
|
2798
|
+
numeric-merge \
|
|
2799
|
+
question-mark; do
|
|
2800
|
+
reset_case passed unavailable unavailable
|
|
2801
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2802
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2803
|
+
--cwd "$WORK/wording-reviewed-repo" \
|
|
2804
|
+
--diff-file "$WORK/$rejected_punctuation_scope-wording.patch" \
|
|
2805
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2806
|
+
--wording-only-proof-file "$WORK/$rejected_punctuation_scope-wording-proof.json")"; rc=$?
|
|
2807
|
+
check "$rejected_punctuation_scope cannot use the punctuation-only release waiver" \
|
|
2808
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2809
|
+
done
|
|
2810
|
+
|
|
2811
|
+
reset_case passed unavailable unavailable
|
|
2812
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2813
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2814
|
+
--cwd "$WORK/wording-reviewed-repo" \
|
|
2815
|
+
--diff-file "$WORK/fenced-token-wording.patch" \
|
|
2816
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2817
|
+
--wording-only-proof-file "$WORK/fenced-token-wording-proof.json")"; rc=$?
|
|
2818
|
+
check "token replacement inside a code container cannot use the wording-only scope" \
|
|
2819
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2820
|
+
|
|
2821
|
+
# An in-tree .gitattributes `-diff` entry must not collapse a changed file to a
|
|
2822
|
+
# binary marker: the frozen packet has to carry the exact changed lines.
|
|
2823
|
+
mkdir -p "$WORK/attr-diff-repo"
|
|
2824
|
+
git -C "$WORK/attr-diff-repo" init -q
|
|
2825
|
+
git -C "$WORK/attr-diff-repo" config user.name 'Review Gate Test'
|
|
2826
|
+
git -C "$WORK/attr-diff-repo" config user.email 'review-gate@example.invalid'
|
|
2827
|
+
printf 'notes.md -diff\n' >"$WORK/attr-diff-repo/.gitattributes"
|
|
2828
|
+
printf 'stable baseline line\n' >"$WORK/attr-diff-repo/notes.md"
|
|
2829
|
+
git -C "$WORK/attr-diff-repo" add .gitattributes notes.md
|
|
2830
|
+
git -C "$WORK/attr-diff-repo" commit -q -m 'baseline'
|
|
2831
|
+
printf 'stable baseline line\nhidden shared-gate rule change\n' >"$WORK/attr-diff-repo/notes.md"
|
|
2832
|
+
reset_case passed unavailable unavailable
|
|
2833
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2834
|
+
--mode review --stage build --challenge-budget 0 \
|
|
2835
|
+
--cwd "$WORK/attr-diff-repo" --base HEAD \
|
|
2836
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json")"; rc=$?
|
|
2837
|
+
check "a -diff attribute cannot hide changed lines from the frozen packet" \
|
|
2838
|
+
'[ "$rc" = 0 ] && grep -q "hidden shared-gate rule change" "$WORK/state/claude_packet" && ! grep -q "Binary files" "$WORK/state/claude_packet"'
|
|
2839
|
+
|
|
2840
|
+
|
|
2841
|
+
for eligible_scope in \
|
|
2842
|
+
after-closed-fence \
|
|
2843
|
+
after-closed-list-fence; do
|
|
2844
|
+
reset_case passed unavailable unavailable
|
|
2845
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2846
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2847
|
+
--cwd "$WORK/wording-reviewed-repo" \
|
|
2848
|
+
--diff-file "$WORK/$eligible_scope-wording.patch" \
|
|
2849
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2850
|
+
--wording-only-proof-file "$WORK/$eligible_scope-wording-proof.json")"; rc=$?
|
|
2851
|
+
check "$eligible_scope remains eligible for punctuation-only proof" \
|
|
2852
|
+
'[ "$rc" = 0 ] && json_fields "$out" wording_only_scope.status=passed wording_only_scope.check_kind=markdown-punctuation-only'
|
|
2853
|
+
done
|
|
2854
|
+
|
|
2855
|
+
reset_case passed unavailable unavailable
|
|
2856
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2857
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2858
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2859
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2860
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2861
|
+
punctuation_proof_hash="$(shasum -a 256 "$WORK/wording-punctuation-proof.json" | awk '{print $1}')"
|
|
2862
|
+
check "release wording-only punctuation scope can take one proof-bound review" \
|
|
2863
|
+
'[ "$rc" = 0 ] && json_fields "$out" challenge_budget=0 review_chain_tracked=false wording_only_scope.status=passed wording_only_scope.check_kind=markdown-punctuation-only wording_only_proof_sha256="$punctuation_proof_hash" reviewed_concerns.7=wording_only_boundary'
|
|
2864
|
+
|
|
2865
|
+
reset_case passed unavailable unavailable
|
|
2866
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2867
|
+
--mode review --stage build --challenge-budget 0 \
|
|
2868
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2869
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2870
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2871
|
+
check "build wording-only review records the same controller-bound proof" \
|
|
2872
|
+
'[ "$rc" = 0 ] && json_fields "$out" review_depth=build challenge_budget=0 wording_only_scope.check_kind=markdown-punctuation-only reviewed_concerns.5=wording_only_boundary'
|
|
2873
|
+
printf '%s\n' "$out" >"$WORK/wording-punctuation-review.json"
|
|
2874
|
+
reset_case passed unavailable unavailable
|
|
2875
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2876
|
+
--mode complete --stage build --challenge-budget 0 \
|
|
2877
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2878
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2879
|
+
--completion-review-result-file "$WORK/wording-punctuation-review.json")"; rc=$?
|
|
2880
|
+
check "a wording-only single review cannot become a complete checkpoint" \
|
|
2881
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=completion_checkpoint_invalid'
|
|
2882
|
+
|
|
2883
|
+
python3 - "$WORK/wording-punctuation-review.json" "$WORK/wording-punctuation-review-without-proof-keys.json" <<'PY'
|
|
2884
|
+
import json
|
|
2885
|
+
import sys
|
|
2886
|
+
from pathlib import Path
|
|
2887
|
+
|
|
2888
|
+
source, target = map(Path, sys.argv[1:])
|
|
2889
|
+
payload = json.loads(source.read_text(encoding="utf-8"))
|
|
2890
|
+
payload.pop("wording_only_proof_sha256")
|
|
2891
|
+
payload.pop("wording_only_scope")
|
|
2892
|
+
target.write_text(json.dumps(payload), encoding="utf-8")
|
|
2893
|
+
PY
|
|
2894
|
+
reset_case passed unavailable unavailable
|
|
2895
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2896
|
+
--mode complete --stage build --challenge-budget 0 \
|
|
2897
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2898
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2899
|
+
--completion-review-result-file "$WORK/wording-punctuation-review-without-proof-keys.json")"; rc=$?
|
|
2900
|
+
check "a receipt cannot hide wording-only proof by dropping controller-owned keys" \
|
|
2901
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=completion_checkpoint_invalid'
|
|
2902
|
+
|
|
2903
|
+
python3 - "$WORK/wording-punctuation-review.json" "$WORK/wording-punctuation-review-with-null-proof.json" <<'PY'
|
|
2904
|
+
import json
|
|
2905
|
+
import sys
|
|
2906
|
+
from pathlib import Path
|
|
2907
|
+
|
|
2908
|
+
source, target = map(Path, sys.argv[1:])
|
|
2909
|
+
payload = json.loads(source.read_text(encoding="utf-8"))
|
|
2910
|
+
payload["wording_only_proof_sha256"] = None
|
|
2911
|
+
payload["wording_only_scope"] = None
|
|
2912
|
+
target.write_text(json.dumps(payload), encoding="utf-8")
|
|
2913
|
+
PY
|
|
2914
|
+
reset_case passed unavailable unavailable
|
|
2915
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2916
|
+
--mode complete --stage build --challenge-budget 0 \
|
|
2917
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2918
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2919
|
+
--completion-review-result-file "$WORK/wording-punctuation-review-with-null-proof.json")"; rc=$?
|
|
2920
|
+
check "a receipt cannot hide wording-only proof by replacing it with null" \
|
|
2921
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=completion_checkpoint_invalid'
|
|
2922
|
+
|
|
2923
|
+
reset_case passed unavailable unavailable
|
|
2924
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2925
|
+
--mode review --stage build --challenge-budget 0 \
|
|
2926
|
+
--cwd "$WORK/repo" --diff-file "$WORK/quoted-unicode-wording.patch" \
|
|
2927
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2928
|
+
--wording-only-proof-file "$WORK/quoted-unicode-wording-proof.json")"; rc=$?
|
|
2929
|
+
check "wording proof binds Git-quoted literal Unicode and space paths" \
|
|
2930
|
+
'[ "$rc" = 0 ] && json_fields "$out" wording_only_scope.changed_files.0="skills/code-review/references/café file.md"'
|
|
2931
|
+
|
|
2932
|
+
reset_case passed unavailable unavailable
|
|
2933
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2934
|
+
--mode review --stage build --challenge-budget 1 \
|
|
2935
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2936
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2937
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2938
|
+
check "wording-only proof cannot open positive autonomous review capacity" \
|
|
2939
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2940
|
+
|
|
2941
|
+
reset_case passed unavailable unavailable
|
|
2942
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2943
|
+
--mode review --stage build --challenge-budget 0 \
|
|
2944
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-token.patch" \
|
|
2945
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2946
|
+
--wording-only-proof-file "$WORK/wording-token-proof.json")"; rc=$?
|
|
2947
|
+
check "build exact typo replacement can take one proof-bound review" \
|
|
2948
|
+
'[ "$rc" = 0 ] && json_fields "$out" review_depth=build challenge_budget=0 wording_only_scope.check_kind=markdown-token-replacement wording_only_scope.old_token=teh wording_only_scope.new_token=the wording_only_scope.expected_count=1 wording_only_scope.replacement_count=1 reviewed_concerns.5=wording_only_boundary'
|
|
2949
|
+
|
|
2950
|
+
reset_case passed unavailable unavailable
|
|
2951
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2952
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2953
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-normative-token.patch" \
|
|
2954
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2955
|
+
--wording-only-proof-file "$WORK/wording-normative-token-proof.json")"; rc=$?
|
|
2956
|
+
check "release token replacement cannot waive its required challenge" \
|
|
2957
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2958
|
+
|
|
2959
|
+
reset_case passed unavailable unavailable
|
|
2960
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2961
|
+
--mode review --stage explore --risk-tag shared-gate --challenge-budget 0 \
|
|
2962
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-normative-token.patch" \
|
|
2963
|
+
--implementer-family openai --review-plan-file "$WORK/high-risk-plan.json" \
|
|
2964
|
+
--wording-only-proof-file "$WORK/wording-normative-token-proof.json")"; rc=$?
|
|
2965
|
+
check "high-risk token replacement cannot waive its required challenge" \
|
|
2966
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2967
|
+
|
|
2968
|
+
reset_case passed unavailable unavailable
|
|
2969
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2970
|
+
--mode review --stage build --challenge-budget 0 \
|
|
2971
|
+
--cwd "$WORK/wording-base-repo" --base HEAD \
|
|
2972
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2973
|
+
--wording-only-proof-file "$WORK/wording-base-proof.json")"; rc=$?
|
|
2974
|
+
check "base-mode build wording proof freezes full context from line one" \
|
|
2975
|
+
'[ "$rc" = 0 ] && json_fields "$out" review_depth=build wording_only_scope.check_kind=markdown-token-replacement wording_only_scope.replacement_count=1 reviewed_concerns.5=wording_only_boundary'
|
|
2976
|
+
|
|
2977
|
+
for rejected_scope in multi-skill-wording truncated-context-wording symlink-mode-wording frontmatter-shift-insert-wording frontmatter-shift-delete-wording no-final-newline-wording invalid-octal-wording huge-hunk-number-wording zero-width-wording bidi-control-wording emoji-symbol-wording currency-symbol-wording decomposed-boundary-wording zwj-boundary-wording; do
|
|
2978
|
+
reset_case passed unavailable unavailable
|
|
2979
|
+
packet="$WORK/$rejected_scope.patch"
|
|
2980
|
+
proof="$WORK/$rejected_scope-proof.json"
|
|
2981
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2982
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2983
|
+
--cwd "$WORK/repo" --diff-file "$packet" \
|
|
2984
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2985
|
+
--wording-only-proof-file "$proof")"; rc=$?
|
|
2986
|
+
check "$rejected_scope cannot waive the release challenge" \
|
|
2987
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
2988
|
+
done
|
|
2989
|
+
|
|
2990
|
+
reset_case missing_wording_boundary unavailable unavailable
|
|
2991
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
2992
|
+
--mode review --stage release --challenge-budget 0 \
|
|
2993
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
2994
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
2995
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
2996
|
+
check "wording-only proof still requires independent semantic-boundary review" \
|
|
2997
|
+
'[ "$rc" = 2 ] && json_fields "$out" status=inconclusive reason_code=invalid_model_output'
|
|
2998
|
+
|
|
2999
|
+
reset_case passed unavailable unavailable
|
|
3000
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
3001
|
+
--mode review --stage release --challenge-budget 0 \
|
|
3002
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
3003
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
3004
|
+
--review-chain-id wording-chain --autonomous-review-index 1 \
|
|
3005
|
+
--wording-only-proof-file "$WORK/wording-punctuation-proof.json")"; rc=$?
|
|
3006
|
+
check "wording-only single review cannot open a tracked review chain" \
|
|
3007
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
3008
|
+
|
|
3009
|
+
python3 - "$WORK/wording-punctuation-proof.json" \
|
|
3010
|
+
"$WORK/stale-wording-proof.json" "$WORK/extra-result-wording-proof.json" \
|
|
3011
|
+
"$WORK/noninteger-schema-wording-proof.json" \
|
|
3012
|
+
"$WORK/extra-check-wording-proof.json" \
|
|
3013
|
+
"$WORK/duplicate-top-wording-proof.json" \
|
|
3014
|
+
"$WORK/duplicate-check-wording-proof.json" <<'PY'
|
|
3015
|
+
import json
|
|
3016
|
+
from pathlib import Path
|
|
3017
|
+
import sys
|
|
3018
|
+
|
|
3019
|
+
source = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
3020
|
+
stale = json.loads(json.dumps(source))
|
|
3021
|
+
stale["candidate_sha256"] = "0" * 64
|
|
3022
|
+
Path(sys.argv[2]).write_text(json.dumps(stale), encoding="utf-8")
|
|
3023
|
+
extra_result = json.loads(json.dumps(source))
|
|
3024
|
+
extra_result["scope_result"] = {"status": "passed"}
|
|
3025
|
+
Path(sys.argv[3]).write_text(json.dumps(extra_result), encoding="utf-8")
|
|
3026
|
+
noninteger = json.loads(json.dumps(source))
|
|
3027
|
+
noninteger["schema_version"] = 1.0
|
|
3028
|
+
Path(sys.argv[4]).write_text(json.dumps(noninteger), encoding="utf-8")
|
|
3029
|
+
extra_check = json.loads(json.dumps(source))
|
|
3030
|
+
extra_check["check"]["result"] = "passed"
|
|
3031
|
+
Path(sys.argv[5]).write_text(json.dumps(extra_check), encoding="utf-8")
|
|
3032
|
+
source_text = json.dumps(source, sort_keys=True, separators=(",", ":"))
|
|
3033
|
+
candidate_field = (
|
|
3034
|
+
'"candidate_sha256":' + json.dumps(source["candidate_sha256"])
|
|
3035
|
+
)
|
|
3036
|
+
Path(sys.argv[6]).write_text(
|
|
3037
|
+
source_text.replace(candidate_field, candidate_field + "," + candidate_field, 1),
|
|
3038
|
+
encoding="utf-8",
|
|
3039
|
+
)
|
|
3040
|
+
kind_field = '"kind":"markdown-punctuation-only"'
|
|
3041
|
+
Path(sys.argv[7]).write_text(
|
|
3042
|
+
source_text.replace(kind_field, kind_field + "," + kind_field, 1),
|
|
3043
|
+
encoding="utf-8",
|
|
3044
|
+
)
|
|
3045
|
+
PY
|
|
3046
|
+
|
|
3047
|
+
for invalid_proof in stale-wording-proof extra-result-wording-proof noninteger-schema-wording-proof extra-check-wording-proof duplicate-top-wording-proof duplicate-check-wording-proof non-wording-proof; do
|
|
3048
|
+
reset_case passed unavailable unavailable
|
|
3049
|
+
packet="$WORK/wording-punctuation.patch"
|
|
3050
|
+
[ "$invalid_proof" != non-wording-proof ] || packet="$WORK/non-wording-punctuation.patch"
|
|
3051
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
3052
|
+
--mode review --stage release --challenge-budget 0 \
|
|
3053
|
+
--cwd "$WORK/repo" --diff-file "$packet" \
|
|
3054
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
3055
|
+
--wording-only-proof-file "$WORK/$invalid_proof.json")"; rc=$?
|
|
3056
|
+
check "$invalid_proof cannot waive release challenge" \
|
|
3057
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
3058
|
+
done
|
|
3059
|
+
|
|
3060
|
+
python3 - "$WORK/wording-token-proof.json" "$WORK/surrogate-token-wording-proof.json" <<'PY'
|
|
3061
|
+
import json
|
|
3062
|
+
from pathlib import Path
|
|
3063
|
+
import sys
|
|
3064
|
+
|
|
3065
|
+
proof = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
3066
|
+
proof["check"]["old_token"] = "\ud800"
|
|
3067
|
+
Path(sys.argv[2]).write_text(json.dumps(proof), encoding="utf-8")
|
|
3068
|
+
PY
|
|
3069
|
+
reset_case passed unavailable unavailable
|
|
3070
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
3071
|
+
--mode review --stage release --challenge-budget 0 \
|
|
3072
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-token.patch" \
|
|
3073
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
3074
|
+
--wording-only-proof-file "$WORK/surrogate-token-wording-proof.json" 2>/dev/null)"; rc=$?
|
|
3075
|
+
check "a non-UTF-8 token proof fails as a structured gate result" \
|
|
3076
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
3077
|
+
|
|
3078
|
+
ln -s "$WORK/wording-punctuation-proof.json" "$WORK/linked-wording-proof.json"
|
|
3079
|
+
reset_case passed unavailable unavailable
|
|
3080
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
3081
|
+
--mode review --stage release --challenge-budget 0 \
|
|
3082
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
3083
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
3084
|
+
--wording-only-proof-file "$WORK/linked-wording-proof.json")"; rc=$?
|
|
3085
|
+
check "a linked wording-only proof cannot waive release challenge" \
|
|
3086
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
3087
|
+
unlink "$WORK/linked-wording-proof.json"
|
|
3088
|
+
|
|
3089
|
+
python3 - "$WORK/oversized-wording-proof.json" <<'PY'
|
|
3090
|
+
import os
|
|
3091
|
+
from pathlib import Path
|
|
3092
|
+
import sys
|
|
3093
|
+
|
|
3094
|
+
path = Path(sys.argv[1])
|
|
3095
|
+
path.touch()
|
|
3096
|
+
os.truncate(path, 16_001)
|
|
3097
|
+
PY
|
|
3098
|
+
reset_case passed unavailable unavailable
|
|
3099
|
+
out="$(REVIEW_GATE_TEST_STATE="$WORK/state" "$WORK/harness/scripts/review_gate.sh" \
|
|
3100
|
+
--mode review --stage release --challenge-budget 0 \
|
|
3101
|
+
--cwd "$WORK/repo" --diff-file "$WORK/wording-punctuation.patch" \
|
|
3102
|
+
--implementer-family openai --review-plan-file "$WORK/review-plan.json" \
|
|
3103
|
+
--wording-only-proof-file "$WORK/oversized-wording-proof.json")"; rc=$?
|
|
3104
|
+
check "an oversized wording-only proof cannot waive release challenge" \
|
|
3105
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=wording_only_proof_invalid'
|
|
3106
|
+
|
|
1668
3107
|
reset_case passed unavailable unavailable
|
|
1669
3108
|
out="$(run_gate --stage release --challenge-budget 0)"; rc=$?
|
|
1670
|
-
check "release review cannot
|
|
3109
|
+
check "release review without wording-only proof cannot waive its first challenge" \
|
|
1671
3110
|
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input next_action=stop_reviewer_lane'
|
|
1672
3111
|
|
|
1673
3112
|
reset_case passed unavailable unavailable
|
|
1674
3113
|
out="$(run_gate --stage explore --risk-tag shared-gate --review-plan-file "$WORK/high-risk-plan.json" --challenge-budget 0)"; rc=$?
|
|
1675
|
-
check "high-risk depth cannot
|
|
3114
|
+
check "high-risk depth without wording-only proof cannot waive its first challenge" \
|
|
1676
3115
|
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input next_action=stop_reviewer_lane'
|
|
1677
3116
|
|
|
1678
3117
|
reset_case findings unavailable unavailable
|
|
@@ -1926,6 +3365,16 @@ out="$(run_gate --timeout 90)"; rc=$?
|
|
|
1926
3365
|
check "an explicit budget still overrides the default" \
|
|
1927
3366
|
'[ "$rc" = 0 ] && [ "$(cat "$WORK/state/claude_timeout")" = 90 ]'
|
|
1928
3367
|
|
|
3368
|
+
reset_case passed unavailable unavailable
|
|
3369
|
+
out="$(run_gate --timeout 1200 --total-timeout 3600)"; rc=$?
|
|
3370
|
+
check "the generic timeout accepts and forwards the documented 1200-second ceiling" \
|
|
3371
|
+
'[ "$rc" = 0 ] && [ "$(cat "$WORK/state/claude_timeout")" = 1200 ] && json_fields "$out" status=passed selected_client=claude'
|
|
3372
|
+
|
|
3373
|
+
reset_case passed unavailable unavailable
|
|
3374
|
+
out="$(run_gate --timeout 1201 --total-timeout 3600)"; rc=$?
|
|
3375
|
+
check "the generic timeout rejects a value above the documented ceiling before client execution" \
|
|
3376
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input'
|
|
3377
|
+
|
|
1929
3378
|
# The gate always passes --timeout, which overrides each wrapper's own default,
|
|
1930
3379
|
# so the assertions above cannot see the direct-invocation defaults at all. Pin
|
|
1931
3380
|
# each declared default separately or three of the four silently regress.
|
|
@@ -1948,6 +3397,41 @@ done
|
|
|
1948
3397
|
check "every wrapper declares the same direct-invocation budget as the gate default" \
|
|
1949
3398
|
'[ "$wrapper_defaults_ok" = 1 ] && [ "$wrapper_defaults_seen" = 4 ]'
|
|
1950
3399
|
|
|
3400
|
+
# Direct wrapper invocations share one decimal-string normalizer. Exercise the
|
|
3401
|
+
# boundary itself rather than grepping arithmetic fragments: shell integer
|
|
3402
|
+
# overflow used to make a 50-digit timeout bypass Claude's clamp and fail the
|
|
3403
|
+
# other three clients as invalid input.
|
|
3404
|
+
# shellcheck source=normalize_review_timeout.sh
|
|
3405
|
+
. "$DIR/normalize_review_timeout.sh"
|
|
3406
|
+
wrapper_timeout_inputs_match=1
|
|
3407
|
+
while IFS='|' read -r input expected; do
|
|
3408
|
+
actual="$(normalize_review_timeout "$input")" || actual="invalid"
|
|
3409
|
+
if [ "$actual" != "$expected" ]; then
|
|
3410
|
+
wrapper_timeout_inputs_match=0
|
|
3411
|
+
printf 'wrapper timeout normalization drift: %s -> %s, want %s\n' \
|
|
3412
|
+
"$input" "$actual" "$expected" >&2
|
|
3413
|
+
fi
|
|
3414
|
+
done <<'EOF'
|
|
3415
|
+
4|invalid
|
|
3416
|
+
5|5
|
|
3417
|
+
1200|1200
|
|
3418
|
+
1201|1200
|
|
3419
|
+
99999999999999999999999999999999999999999999999999|1200
|
|
3420
|
+
EOF
|
|
3421
|
+
wrapper_timeout_bindings_match=1
|
|
3422
|
+
wrapper_timeout_bindings_seen=0
|
|
3423
|
+
for wrapper_file in claude_review.sh codex_review.sh kimi_review.sh opencode_review.sh; do
|
|
3424
|
+
if grep -qF 'normalize_review_timeout "$' "$DIR/$wrapper_file"; then
|
|
3425
|
+
wrapper_timeout_bindings_seen=$((wrapper_timeout_bindings_seen + 1))
|
|
3426
|
+
else
|
|
3427
|
+
wrapper_timeout_bindings_match=0
|
|
3428
|
+
printf 'wrapper timeout binding drift: %s does not call the shared normalizer\n' \
|
|
3429
|
+
"$wrapper_file" >&2
|
|
3430
|
+
fi
|
|
3431
|
+
done
|
|
3432
|
+
check "every wrapper accepts and clamps the full decimal-string timeout domain" \
|
|
3433
|
+
'[ "$wrapper_timeout_inputs_match" = 1 ] && [ "$wrapper_timeout_bindings_match" = 1 ] && [ "$wrapper_timeout_bindings_seen" = 4 ]'
|
|
3434
|
+
|
|
1951
3435
|
parser_total_timeout="$(python3 - "$DIR/review_gate.py" "$WORK" <<'PY'
|
|
1952
3436
|
import importlib.util
|
|
1953
3437
|
from pathlib import Path
|
|
@@ -2612,6 +4096,7 @@ out="$(run_gate --allow-fallback-egress)"; rc=$?
|
|
|
2612
4096
|
check "reviewer cannot self-report controller-owned stage or coverage fields" \
|
|
2613
4097
|
'[ "$rc" = 0 ] && json_fields "$out" stage=build review_depth=build owner_selection_source=implementer-declared selected_skills.0=code-review self_review_gate.required=true self_review_gate.required_triggers.0=before_completion_claim && [ "$(printf "%s" "$out" | python3 -c "import json,sys; print(len(json.load(sys.stdin).get(\"reviewed_skills\", [])))")" = 0 ] && ! grep -q spoofed <<<"$out"'
|
|
2614
4098
|
|
|
4099
|
+
rm -rf "$WORK/repo/skills"
|
|
2615
4100
|
(
|
|
2616
4101
|
cd "$WORK/repo"
|
|
2617
4102
|
git init -q
|
|
@@ -2628,6 +4113,376 @@ out="$(run_base_gate --allow-fallback-egress)"; rc=$?
|
|
|
2628
4113
|
check "base packet freezes tracked and untracked changes once" \
|
|
2629
4114
|
'[ "$rc" = 0 ] && grep -q tracked.txt "$WORK/state/claude_packet" && grep -q untracked.txt "$WORK/state/claude_packet" && grep -q "Untracked files" "$WORK/state/claude_packet"'
|
|
2630
4115
|
|
|
4116
|
+
# The explicit cwd is the only repository identity. Ambient Git variables must
|
|
4117
|
+
# not redirect discovery, objects, refs, index, or worktree to a clean decoy.
|
|
4118
|
+
git clone -q "$WORK/repo" "$WORK/git-env-decoy"
|
|
4119
|
+
git -C "$WORK/git-env-decoy" config core.worktree "$WORK/git-env-decoy"
|
|
4120
|
+
: >"$WORK/empty-grafts"
|
|
4121
|
+
: >"$WORK/empty-shallow"
|
|
4122
|
+
|
|
4123
|
+
reset_case passed unavailable unavailable
|
|
4124
|
+
out="$(GIT_DIR="$WORK/git-env-decoy/.git" run_base_gate --allow-fallback-egress)"; rc=$?
|
|
4125
|
+
check "ambient GIT_DIR cannot redirect the base-mode packet to a clean decoy" \
|
|
4126
|
+
'[ "$rc" = 0 ] && grep -q tracked.txt "$WORK/state/claude_packet" && grep -q -- "+after" "$WORK/state/claude_packet"'
|
|
4127
|
+
|
|
4128
|
+
reset_case passed unavailable unavailable
|
|
4129
|
+
out="$( \
|
|
4130
|
+
GIT_DIR="$WORK/git-env-decoy/.git" \
|
|
4131
|
+
GIT_WORK_TREE="$WORK/git-env-decoy" \
|
|
4132
|
+
GIT_IMPLICIT_WORK_TREE=1 \
|
|
4133
|
+
GIT_INDEX_FILE="$WORK/git-env-decoy/.git/index" \
|
|
4134
|
+
GIT_COMMON_DIR="$WORK/git-env-decoy/.git" \
|
|
4135
|
+
GIT_NAMESPACE=synthetic-clean \
|
|
4136
|
+
GIT_OBJECT_DIRECTORY="$WORK/git-env-decoy/.git/objects" \
|
|
4137
|
+
GIT_ALTERNATE_OBJECT_DIRECTORIES="$WORK/git-env-decoy/.git/objects" \
|
|
4138
|
+
GIT_CEILING_DIRECTORIES="$WORK/git-env-decoy" \
|
|
4139
|
+
GIT_DISCOVERY_ACROSS_FILESYSTEM=0 \
|
|
4140
|
+
GIT_GRAFT_FILE="$WORK/empty-grafts" \
|
|
4141
|
+
GIT_SHALLOW_FILE="$WORK/empty-shallow" \
|
|
4142
|
+
GIT_REPLACE_REF_BASE=refs/synthetic-replace \
|
|
4143
|
+
GIT_PREFIX=synthetic-prefix \
|
|
4144
|
+
GIT_QUARANTINE_PATH="$WORK/git-env-decoy/.git/objects" \
|
|
4145
|
+
run_base_gate --allow-fallback-egress
|
|
4146
|
+
)"; rc=$?
|
|
4147
|
+
check "ambient Git routing matrix cannot replace the requested repository" \
|
|
4148
|
+
'[ "$rc" = 0 ] && grep -q tracked.txt "$WORK/state/claude_packet" && grep -q -- "+after" "$WORK/state/claude_packet" && ! grep -q "$WORK/git-env-decoy" "$WORK/state/claude_packet"'
|
|
4149
|
+
|
|
4150
|
+
git_config_env_probe="$(
|
|
4151
|
+
GIT_CONFIG="$WORK/attacker.cfg" \
|
|
4152
|
+
GIT_CONFIG_GLOBAL="$WORK/attacker-global.cfg" \
|
|
4153
|
+
GIT_CONFIG_SYSTEM="$WORK/attacker-system.cfg" \
|
|
4154
|
+
GIT_CONFIG_NOSYSTEM=1 \
|
|
4155
|
+
GIT_CONFIG_COUNT=2 \
|
|
4156
|
+
GIT_CONFIG_PARAMETERS="'core.fsmonitor=$WORK/fake-fsmonitor.sh'" \
|
|
4157
|
+
GIT_CONFIG_KEY_0=core.worktree \
|
|
4158
|
+
GIT_CONFIG_VALUE_0="$WORK/git-env-decoy" \
|
|
4159
|
+
GIT_CONFIG_KEY_custom=include.path \
|
|
4160
|
+
GIT_CONFIG_VALUE_custom="$WORK/attacker-include.cfg" \
|
|
4161
|
+
PYTHONPATH="$WORK/harness/scripts" python3 - <<'PY' 2>&1
|
|
4162
|
+
import os
|
|
4163
|
+
|
|
4164
|
+
import review_gate
|
|
4165
|
+
|
|
4166
|
+
environment = review_gate.git_environment()
|
|
4167
|
+
for key in (
|
|
4168
|
+
"GIT_CONFIG",
|
|
4169
|
+
"GIT_CONFIG_COUNT",
|
|
4170
|
+
"GIT_CONFIG_PARAMETERS",
|
|
4171
|
+
"GIT_CONFIG_KEY_0",
|
|
4172
|
+
"GIT_CONFIG_VALUE_0",
|
|
4173
|
+
"GIT_CONFIG_KEY_custom",
|
|
4174
|
+
"GIT_CONFIG_VALUE_custom",
|
|
4175
|
+
):
|
|
4176
|
+
assert key not in environment, (key, environment[key])
|
|
4177
|
+
assert environment["GIT_CONFIG_GLOBAL"] == os.devnull
|
|
4178
|
+
assert environment["GIT_CONFIG_SYSTEM"] == os.devnull
|
|
4179
|
+
assert environment["GIT_CONFIG_NOSYSTEM"] == "1"
|
|
4180
|
+
print("git_config_environment_clean")
|
|
4181
|
+
PY
|
|
4182
|
+
)"; git_config_env_rc=$?
|
|
4183
|
+
check "base-mode Git environment removes fixed and indexed config injection variables" \
|
|
4184
|
+
'[ "$git_config_env_rc" = 0 ] && [ "$git_config_env_probe" = git_config_environment_clean ]'
|
|
4185
|
+
|
|
4186
|
+
cat >"$WORK/fake-fsmonitor.sh" <<'SH'
|
|
4187
|
+
#!/bin/sh
|
|
4188
|
+
: >"$REVIEW_FSMONITOR_MARKER"
|
|
4189
|
+
printf '\n'
|
|
4190
|
+
SH
|
|
4191
|
+
chmod +x "$WORK/fake-fsmonitor.sh"
|
|
4192
|
+
printf '[core]\n\tworktree = %s\n\tfsmonitor = %s\n' \
|
|
4193
|
+
"$WORK/git-env-decoy" "$WORK/fake-fsmonitor.sh" \
|
|
4194
|
+
>"$WORK/attacker-include.cfg"
|
|
4195
|
+
printf '[include]\n\tpath = %s\n' "$WORK/attacker-include.cfg" \
|
|
4196
|
+
>"$WORK/attacker-root.cfg"
|
|
4197
|
+
|
|
4198
|
+
for config_env in GIT_CONFIG_GLOBAL GIT_CONFIG_SYSTEM; do
|
|
4199
|
+
rm -f "$WORK/fsmonitor-marker"
|
|
4200
|
+
reset_case passed unavailable unavailable
|
|
4201
|
+
out="$(
|
|
4202
|
+
export "$config_env=$WORK/attacker-root.cfg"
|
|
4203
|
+
REVIEW_FSMONITOR_MARKER="$WORK/fsmonitor-marker" \
|
|
4204
|
+
run_base_gate --allow-fallback-egress
|
|
4205
|
+
)"; rc=$?
|
|
4206
|
+
check "ambient $config_env include cannot route the worktree or execute fsmonitor" \
|
|
4207
|
+
'[ "$rc" = 0 ] && [ ! -e "$WORK/fsmonitor-marker" ] && grep -q -- "+after" "$WORK/state/claude_packet"'
|
|
4208
|
+
done
|
|
4209
|
+
|
|
4210
|
+
for config_route in indexed parameters; do
|
|
4211
|
+
rm -f "$WORK/fsmonitor-marker"
|
|
4212
|
+
reset_case passed unavailable unavailable
|
|
4213
|
+
if [ "$config_route" = indexed ]; then
|
|
4214
|
+
out="$( \
|
|
4215
|
+
REVIEW_FSMONITOR_MARKER="$WORK/fsmonitor-marker" \
|
|
4216
|
+
GIT_CONFIG_COUNT=1 \
|
|
4217
|
+
GIT_CONFIG_KEY_0=include.path \
|
|
4218
|
+
GIT_CONFIG_VALUE_0="$WORK/attacker-include.cfg" \
|
|
4219
|
+
run_base_gate --allow-fallback-egress
|
|
4220
|
+
)"; rc=$?
|
|
4221
|
+
else
|
|
4222
|
+
out="$( \
|
|
4223
|
+
REVIEW_FSMONITOR_MARKER="$WORK/fsmonitor-marker" \
|
|
4224
|
+
GIT_CONFIG_PARAMETERS="'include.path=$WORK/attacker-include.cfg'" \
|
|
4225
|
+
run_base_gate --allow-fallback-egress
|
|
4226
|
+
)"; rc=$?
|
|
4227
|
+
fi
|
|
4228
|
+
check "ambient $config_route config cannot load executable Git configuration" \
|
|
4229
|
+
'[ "$rc" = 0 ] && [ ! -e "$WORK/fsmonitor-marker" ] && grep -q -- "+after" "$WORK/state/claude_packet"'
|
|
4230
|
+
done
|
|
4231
|
+
|
|
4232
|
+
git -C "$WORK/repo" config include.path "$WORK/attacker-include.cfg"
|
|
4233
|
+
rm -f "$WORK/fsmonitor-marker"
|
|
4234
|
+
reset_case passed unavailable unavailable
|
|
4235
|
+
out="$(REVIEW_FSMONITOR_MARKER="$WORK/fsmonitor-marker" \
|
|
4236
|
+
run_base_gate --allow-fallback-egress)"; rc=$?
|
|
4237
|
+
check "repository-local include cannot execute fsmonitor during packet construction" \
|
|
4238
|
+
'[ "$rc" = 0 ] && [ ! -e "$WORK/fsmonitor-marker" ] && grep -q -- "+after" "$WORK/state/claude_packet"'
|
|
4239
|
+
git -C "$WORK/repo" config --unset-all include.path
|
|
4240
|
+
|
|
4241
|
+
# Neither an ambient external-diff helper nor a repository-configured textconv
|
|
4242
|
+
# may execute or replace the bytes in the tracked packet.
|
|
4243
|
+
cat >"$WORK/fake-external-diff.sh" <<'SH'
|
|
4244
|
+
#!/bin/sh
|
|
4245
|
+
: >"$REVIEW_EXTERNAL_DIFF_MARKER"
|
|
4246
|
+
exit 0
|
|
4247
|
+
SH
|
|
4248
|
+
chmod +x "$WORK/fake-external-diff.sh"
|
|
4249
|
+
rm -f "$WORK/external-diff-marker"
|
|
4250
|
+
reset_case passed unavailable unavailable
|
|
4251
|
+
out="$( \
|
|
4252
|
+
REVIEW_EXTERNAL_DIFF_MARKER="$WORK/external-diff-marker" \
|
|
4253
|
+
GIT_EXTERNAL_DIFF="$WORK/fake-external-diff.sh" \
|
|
4254
|
+
GIT_DIFF_OPTS=--unified=0 \
|
|
4255
|
+
run_base_gate --allow-fallback-egress
|
|
4256
|
+
)"; rc=$?
|
|
4257
|
+
check "tracked packet disables ambient external diff execution and spoofing" \
|
|
4258
|
+
'[ "$rc" = 0 ] && [ ! -e "$WORK/external-diff-marker" ] && grep -q -- "-before" "$WORK/state/claude_packet" && grep -q -- "+after" "$WORK/state/claude_packet"'
|
|
4259
|
+
|
|
4260
|
+
printf '%s\n' 'tracked.txt diff=review-spoof' >"$WORK/repo/.gitattributes"
|
|
4261
|
+
git -C "$WORK/repo" add .gitattributes
|
|
4262
|
+
git -C "$WORK/repo" commit -q -m 'add synthetic diff driver fixture' -- .gitattributes
|
|
4263
|
+
cat >"$WORK/fake-textconv.sh" <<'SH'
|
|
4264
|
+
#!/bin/sh
|
|
4265
|
+
: >"$REVIEW_TEXTCONV_MARKER"
|
|
4266
|
+
printf '%s\n' normalized
|
|
4267
|
+
SH
|
|
4268
|
+
chmod +x "$WORK/fake-textconv.sh"
|
|
4269
|
+
git -C "$WORK/repo" config diff.review-spoof.textconv "$WORK/fake-textconv.sh"
|
|
4270
|
+
rm -f "$WORK/textconv-marker"
|
|
4271
|
+
reset_case passed unavailable unavailable
|
|
4272
|
+
out="$(REVIEW_TEXTCONV_MARKER="$WORK/textconv-marker" run_base_gate --allow-fallback-egress)"; rc=$?
|
|
4273
|
+
check "tracked packet disables configured textconv execution and clean spoofing" \
|
|
4274
|
+
'[ "$rc" = 0 ] && [ ! -e "$WORK/textconv-marker" ] && grep -q -- "-before" "$WORK/state/claude_packet" && grep -q -- "+after" "$WORK/state/claude_packet"'
|
|
4275
|
+
git -C "$WORK/repo" config --unset diff.review-spoof.textconv
|
|
4276
|
+
|
|
4277
|
+
ln -s tracked.txt "$WORK/repo/untracked-link.txt"
|
|
4278
|
+
reset_case passed unavailable unavailable
|
|
4279
|
+
out="$(run_base_gate --allow-fallback-egress)"; rc=$?
|
|
4280
|
+
check "base mode rejects an untracked symlink instead of reviewing a non-exact placeholder" \
|
|
4281
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input'
|
|
4282
|
+
unlink "$WORK/repo/untracked-link.txt"
|
|
4283
|
+
|
|
4284
|
+
ln "$WORK/repo/tracked.txt" "$WORK/repo/untracked-hardlink.txt"
|
|
4285
|
+
reset_case passed unavailable unavailable
|
|
4286
|
+
out="$(run_base_gate --allow-fallback-egress)"; rc=$?
|
|
4287
|
+
check "base mode rejects an untracked hardlink instead of omitting it" \
|
|
4288
|
+
'[ "$rc" = 2 ] && [ ! -e "$WORK/state/client_sequence" ] && json_fields "$out" reason_code=invalid_input'
|
|
4289
|
+
unlink "$WORK/repo/untracked-hardlink.txt"
|
|
4290
|
+
|
|
4291
|
+
: >"$WORK/repo/empty-untracked.txt"
|
|
4292
|
+
reset_case passed unavailable unavailable
|
|
4293
|
+
out="$(run_base_gate --allow-fallback-egress)"; rc=$?
|
|
4294
|
+
check "base mode binds an empty untracked file without a skipped placeholder" \
|
|
4295
|
+
'[ "$rc" = 0 ] && grep -q empty-untracked.txt "$WORK/state/claude_packet" && ! grep -qE "skipped|not shown as text diff" "$WORK/state/claude_packet"'
|
|
4296
|
+
unlink "$WORK/repo/empty-untracked.txt"
|
|
4297
|
+
|
|
4298
|
+
untracked_exact_probe="$(PYTHONPATH="$WORK/harness/scripts" python3 - "$WORK" <<'PY' 2>&1
|
|
4299
|
+
import os
|
|
4300
|
+
import time
|
|
4301
|
+
from pathlib import Path
|
|
4302
|
+
import sys
|
|
4303
|
+
|
|
4304
|
+
import review_gate
|
|
4305
|
+
|
|
4306
|
+
|
|
4307
|
+
root = Path(sys.argv[1]) / "untracked-exact-probe"
|
|
4308
|
+
repo = root / "repo"
|
|
4309
|
+
outside = root / "outside"
|
|
4310
|
+
repo.mkdir(parents=True)
|
|
4311
|
+
outside.mkdir()
|
|
4312
|
+
|
|
4313
|
+
|
|
4314
|
+
def expect_rejected(fake_git_output, label):
|
|
4315
|
+
original = review_gate.git_output
|
|
4316
|
+
review_gate.git_output = fake_git_output
|
|
4317
|
+
try:
|
|
4318
|
+
try:
|
|
4319
|
+
review_gate.untracked_packet(repo, [], time.monotonic() + 5)
|
|
4320
|
+
except review_gate.GateError as exc:
|
|
4321
|
+
assert exc.reason_code == "invalid_input", (label, exc.reason_code)
|
|
4322
|
+
return exc
|
|
4323
|
+
raise AssertionError(f"{label} produced a placeholder instead of failing closed")
|
|
4324
|
+
finally:
|
|
4325
|
+
review_gate.git_output = original
|
|
4326
|
+
|
|
4327
|
+
|
|
4328
|
+
gone = repo / "gone.txt"
|
|
4329
|
+
gone.write_text("gone\n", encoding="utf-8")
|
|
4330
|
+
|
|
4331
|
+
|
|
4332
|
+
def disappear_after_listing(repo_path, args, *unused, **kwargs):
|
|
4333
|
+
assert args[0] == "ls-files", args
|
|
4334
|
+
gone.unlink()
|
|
4335
|
+
return b"gone.txt\0"
|
|
4336
|
+
|
|
4337
|
+
|
|
4338
|
+
expect_rejected(disappear_after_listing, "disappeared untracked path")
|
|
4339
|
+
|
|
4340
|
+
(outside / "escaped.txt").write_text("outside\n", encoding="utf-8")
|
|
4341
|
+
(repo / "linked-parent").symlink_to(outside, target_is_directory=True)
|
|
4342
|
+
|
|
4343
|
+
|
|
4344
|
+
def parent_symlink_listing(repo_path, args, *unused, **kwargs):
|
|
4345
|
+
assert args[0] == "ls-files", args
|
|
4346
|
+
return b"linked-parent/escaped.txt\0"
|
|
4347
|
+
|
|
4348
|
+
|
|
4349
|
+
expect_rejected(parent_symlink_listing, "symlinked untracked parent")
|
|
4350
|
+
|
|
4351
|
+
fifo = repo / "fifo"
|
|
4352
|
+
os.mkfifo(fifo)
|
|
4353
|
+
|
|
4354
|
+
|
|
4355
|
+
def fifo_listing(repo_path, args, *unused, **kwargs):
|
|
4356
|
+
assert args[0] == "ls-files", args
|
|
4357
|
+
return b"fifo\0"
|
|
4358
|
+
|
|
4359
|
+
|
|
4360
|
+
expect_rejected(fifo_listing, "untracked FIFO")
|
|
4361
|
+
fifo.unlink()
|
|
4362
|
+
|
|
4363
|
+
nul = repo / "nul.bin"
|
|
4364
|
+
nul.write_bytes(b"safe\0unsafe")
|
|
4365
|
+
|
|
4366
|
+
|
|
4367
|
+
def nul_listing(repo_path, args, *unused, **kwargs):
|
|
4368
|
+
assert args[0] == "ls-files", args
|
|
4369
|
+
return b"nul.bin\0"
|
|
4370
|
+
|
|
4371
|
+
|
|
4372
|
+
error = expect_rejected(nul_listing, "NUL-bearing untracked file")
|
|
4373
|
+
assert "NUL-bearing" in error.reason, error.reason
|
|
4374
|
+
nul.unlink()
|
|
4375
|
+
|
|
4376
|
+
non_utf8 = repo / "non-utf8.bin"
|
|
4377
|
+
non_utf8.write_bytes(b"\xff")
|
|
4378
|
+
|
|
4379
|
+
|
|
4380
|
+
def non_utf8_listing(repo_path, args, *unused, **kwargs):
|
|
4381
|
+
assert args[0] == "ls-files", args
|
|
4382
|
+
return b"non-utf8.bin\0"
|
|
4383
|
+
|
|
4384
|
+
|
|
4385
|
+
error = expect_rejected(non_utf8_listing, "non-UTF-8 untracked file")
|
|
4386
|
+
assert "non-UTF-8" in error.reason, error.reason
|
|
4387
|
+
non_utf8.unlink()
|
|
4388
|
+
|
|
4389
|
+
accepted_control_paths = []
|
|
4390
|
+
for label, relative in (
|
|
4391
|
+
("tab", "tab\tname.txt"),
|
|
4392
|
+
("newline", "line\nname.txt"),
|
|
4393
|
+
("carriage-return", "carriage\rname.txt"),
|
|
4394
|
+
("C0", "unit\x01name.txt"),
|
|
4395
|
+
("DEL", "delete\x7fname.txt"),
|
|
4396
|
+
("C1-NEL", "next\u0085line.txt"),
|
|
4397
|
+
("line-separator", "line\u2028separator.txt"),
|
|
4398
|
+
("paragraph-separator", "paragraph\u2029separator.txt"),
|
|
4399
|
+
):
|
|
4400
|
+
control_path = repo / relative
|
|
4401
|
+
control_path.write_text("control path\n", encoding="utf-8")
|
|
4402
|
+
encoded_path = relative.encode("utf-8")
|
|
4403
|
+
|
|
4404
|
+
def control_listing(repo_path, args, *unused, **kwargs):
|
|
4405
|
+
assert args[0] == "ls-files", args
|
|
4406
|
+
return encoded_path + b"\0"
|
|
4407
|
+
|
|
4408
|
+
try:
|
|
4409
|
+
error = expect_rejected(control_listing, f"{label} untracked path")
|
|
4410
|
+
except AssertionError:
|
|
4411
|
+
accepted_control_paths.append(label)
|
|
4412
|
+
else:
|
|
4413
|
+
assert "control-character" in error.reason, error.reason
|
|
4414
|
+
finally:
|
|
4415
|
+
control_path.unlink()
|
|
4416
|
+
assert not accepted_control_paths, accepted_control_paths
|
|
4417
|
+
|
|
4418
|
+
oversized = repo / "oversized.txt"
|
|
4419
|
+
with oversized.open("wb") as handle:
|
|
4420
|
+
handle.truncate(review_gate.MAX_PACKET_BYTES + 1)
|
|
4421
|
+
|
|
4422
|
+
|
|
4423
|
+
def oversized_listing(repo_path, args, *unused, **kwargs):
|
|
4424
|
+
assert args[0] == "ls-files", args
|
|
4425
|
+
return b"oversized.txt\0"
|
|
4426
|
+
|
|
4427
|
+
|
|
4428
|
+
error = expect_rejected(oversized_listing, "oversized untracked file")
|
|
4429
|
+
assert "exceeds" in error.reason, error.reason
|
|
4430
|
+
oversized.unlink()
|
|
4431
|
+
|
|
4432
|
+
aggregate_a = repo / "aggregate-a.txt"
|
|
4433
|
+
aggregate_b = repo / "aggregate-b.txt"
|
|
4434
|
+
aggregate_a.write_bytes(b"a" * (review_gate.MAX_PACKET_BYTES // 2))
|
|
4435
|
+
aggregate_b.write_bytes(b"b" * (review_gate.MAX_PACKET_BYTES // 2))
|
|
4436
|
+
|
|
4437
|
+
|
|
4438
|
+
def aggregate_listing(repo_path, args, *unused, **kwargs):
|
|
4439
|
+
assert args[0] == "ls-files", args
|
|
4440
|
+
return b"aggregate-a.txt\0aggregate-b.txt\0"
|
|
4441
|
+
|
|
4442
|
+
|
|
4443
|
+
error = expect_rejected(aggregate_listing, "aggregate untracked packet overflow")
|
|
4444
|
+
assert "untracked review packet exceeds" in error.reason, error.reason
|
|
4445
|
+
aggregate_a.unlink()
|
|
4446
|
+
aggregate_b.unlink()
|
|
4447
|
+
|
|
4448
|
+
raced = repo / "raced.txt"
|
|
4449
|
+
alternate = root / "alternate.txt"
|
|
4450
|
+
backup = repo / "raced.before-swap"
|
|
4451
|
+
raced.write_text("original-safe-bytes\n", encoding="utf-8")
|
|
4452
|
+
alternate.write_text("alternate-link-bytes\n", encoding="utf-8")
|
|
4453
|
+
calls = 0
|
|
4454
|
+
|
|
4455
|
+
|
|
4456
|
+
def replace_before_old_diff(repo_path, args, *unused, **kwargs):
|
|
4457
|
+
global calls
|
|
4458
|
+
calls += 1
|
|
4459
|
+
if args[0] == "ls-files":
|
|
4460
|
+
return b"raced.txt\0"
|
|
4461
|
+
raced.rename(backup)
|
|
4462
|
+
raced.symlink_to(alternate)
|
|
4463
|
+
return b"diff --git a/raced.txt b/raced.txt\n+alternate-link-bytes\n"
|
|
4464
|
+
|
|
4465
|
+
|
|
4466
|
+
original = review_gate.git_output
|
|
4467
|
+
review_gate.git_output = replace_before_old_diff
|
|
4468
|
+
try:
|
|
4469
|
+
packet = review_gate.untracked_packet(repo, [], time.monotonic() + 5)
|
|
4470
|
+
finally:
|
|
4471
|
+
review_gate.git_output = original
|
|
4472
|
+
if raced.is_symlink():
|
|
4473
|
+
raced.unlink()
|
|
4474
|
+
backup.rename(raced)
|
|
4475
|
+
assert b"original-safe-bytes" in packet, packet
|
|
4476
|
+
assert b"alternate-link-bytes" not in packet, packet
|
|
4477
|
+
assert calls == 1, "untracked bytes were reopened by git diff"
|
|
4478
|
+
|
|
4479
|
+
print("untracked_exact_inputs_ok")
|
|
4480
|
+
PY
|
|
4481
|
+
)"
|
|
4482
|
+
untracked_exact_rc=$?
|
|
4483
|
+
check "untracked unsafe types, bytes, sizes, races, and parent links fail closed" \
|
|
4484
|
+
'[ "$untracked_exact_rc" = 0 ] && [ "$untracked_exact_probe" = untracked_exact_inputs_ok ]'
|
|
4485
|
+
|
|
2631
4486
|
check "owner selection source has one controller-owned definition" \
|
|
2632
4487
|
'[ "$(grep -c '\''"implementer-declared"'\'' "$DIR/review_gate.py")" = 1 ]'
|
|
2633
4488
|
|