@ccoalm/ccl-skills 0.13.0 → 0.14.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/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +14 -41
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/correction-routing-map.md +22 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/coverage-exhaustion-traps.md +7 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md +26 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +6 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +4 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/harness-patterns-and-eval.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/incident-postmortem-extraction.md +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/rule-consolidation.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +34 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/uiux-judgment-extraction.md +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/entrypoint_form_census.py +169 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/reference-access-census.sh +157 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/review_ledger_binding.py +454 -103
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +8 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_entrypoint_form_census.sh +174 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_reference_access_census.sh +209 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_review_ledger_binding.sh +331 -0
- package/dist/assets/release.json +45 -20
- package/package.json +1 -1
|
@@ -492,6 +492,337 @@ out="$(run_part --base "$PART_BASE")"; rc=$?
|
|
|
492
492
|
check "the passing partition state still passes after the probes" \
|
|
493
493
|
'[ "$rc" = 0 ]'
|
|
494
494
|
|
|
495
|
+
# Chain: an integration branch accumulates several reviewed rounds, each merged
|
|
496
|
+
# with its own ledger bound at its own base, and is then promoted as one pull
|
|
497
|
+
# request. No single ledger binds the promotion candidate, and a path partition
|
|
498
|
+
# cannot either when two rounds append to the same file: that file's promotion
|
|
499
|
+
# diff is the sum of both appends and no round ever froze that sum. Every byte
|
|
500
|
+
# of the promotion reached the branch through a first-parent merge whose second
|
|
501
|
+
# parent is a reviewed head, so the gate walks that chain and rebinds each round
|
|
502
|
+
# in a detached checkout at the round's own base, requiring every merge to be
|
|
503
|
+
# exactly the automatic merge of its parents.
|
|
504
|
+
CHAIN="$WORK/chain-repo"
|
|
505
|
+
mkdir -p "$CHAIN/skills/code-review/scripts" "$CHAIN/skills/skill-extraction-workflow/scripts" "$CHAIN/specs"
|
|
506
|
+
cp "$CONTROLLER" "$CHAIN/skills/code-review/scripts/review_gate.py"
|
|
507
|
+
# This stub has a rule, unlike the accept-all stubs above, so a round can forge a
|
|
508
|
+
# ledger the real validator would reject and the suite can tell whose validator
|
|
509
|
+
# judged it.
|
|
510
|
+
STRICT_VALIDATOR='import json, sys
|
|
511
|
+
payload = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
512
|
+
if payload.get("closeout_state") == "ready_for_human_decision":
|
|
513
|
+
print("extraction_review_state_ok: stub accepted")
|
|
514
|
+
sys.exit(0)
|
|
515
|
+
print("extraction_review_state_invalid: stub rejected")
|
|
516
|
+
sys.exit(1)
|
|
517
|
+
'
|
|
518
|
+
printf '%s' "$STRICT_VALIDATOR" >"$CHAIN/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py"
|
|
519
|
+
git -C "$CHAIN" init -q -b main .
|
|
520
|
+
git -C "$CHAIN" config user.email t@example.invalid
|
|
521
|
+
git -C "$CHAIN" config user.name tester
|
|
522
|
+
printf 'baseline\n' >"$CHAIN/skills/skill-extraction-workflow/SKILL.md"
|
|
523
|
+
printf '| row | owner |\n' >"$CHAIN/skills/skill-extraction-workflow/register.md"
|
|
524
|
+
printf 'baseline\n' >"$CHAIN/README.md"
|
|
525
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm baseline
|
|
526
|
+
CHAIN_BASE="$(git -C "$CHAIN" rev-parse HEAD)"
|
|
527
|
+
git -C "$CHAIN" checkout -q -b dev
|
|
528
|
+
|
|
529
|
+
run_chain() { python3 "$GATE" --repo-root "$CHAIN" "$@" 2>&1; }
|
|
530
|
+
|
|
531
|
+
# Land one reviewed round on the integration branch: branch, edit, ledger bound
|
|
532
|
+
# to the round's own candidate (its base is the integration tip it forked from),
|
|
533
|
+
# then a non-fast-forward merge so the round is one first-parent step.
|
|
534
|
+
land_round() {
|
|
535
|
+
local name="$1" ledger="$2"; shift 2
|
|
536
|
+
git -C "$CHAIN" checkout -q -b "round-$name" dev
|
|
537
|
+
"$@"
|
|
538
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round $name"
|
|
539
|
+
if [ "$ledger" = with-ledger ]; then
|
|
540
|
+
local digest
|
|
541
|
+
digest="$(run_chain --base dev --print-candidate)"
|
|
542
|
+
mkdir -p "$CHAIN/specs/$name/evidence"
|
|
543
|
+
write_closeout "$CHAIN/specs/$name/evidence/closeout.json" "$digest"
|
|
544
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round $name ledger"
|
|
545
|
+
fi
|
|
546
|
+
git -C "$CHAIN" checkout -q dev
|
|
547
|
+
git -C "$CHAIN" merge -q --no-ff -m "merge round $name" "round-$name"
|
|
548
|
+
}
|
|
549
|
+
edit_round_a() {
|
|
550
|
+
printf 'baseline\nround a\n' >"$CHAIN/skills/skill-extraction-workflow/SKILL.md"
|
|
551
|
+
printf '| round a | owner |\n' >>"$CHAIN/skills/skill-extraction-workflow/register.md"
|
|
552
|
+
}
|
|
553
|
+
edit_round_b() {
|
|
554
|
+
mkdir -p "$CHAIN/lane"
|
|
555
|
+
printf 'round b\n' >"$CHAIN/lane/file.txt"
|
|
556
|
+
printf '| round b | owner |\n' >>"$CHAIN/skills/skill-extraction-workflow/register.md"
|
|
557
|
+
}
|
|
558
|
+
land_round a with-ledger edit_round_a
|
|
559
|
+
land_round b with-ledger edit_round_b
|
|
560
|
+
CHAIN_TWO="$(git -C "$CHAIN" rev-parse HEAD)"
|
|
561
|
+
|
|
562
|
+
worktrees_before="$(git -C "$CHAIN" worktree list --porcelain)"
|
|
563
|
+
tree_before="$(git -C "$CHAIN" status --porcelain --ignored)"
|
|
564
|
+
out="$(run_chain --base "$CHAIN_BASE")"; rc=$?
|
|
565
|
+
check "two rounds appending to one file bind through the first-parent chain where no single ledger or path partition can" \
|
|
566
|
+
'[ "$rc" = 0 ] && case "$out" in *"first-parent chain"*"2 rounds"*"specs/b/evidence/closeout.json"*"specs/a/evidence/closeout.json"*) true;; *) false;; esac'
|
|
567
|
+
check "walking the chain leaves no detached checkout registered and the bound tree byte-identical" \
|
|
568
|
+
'[ "$(git -C "$CHAIN" worktree list --porcelain)" = "$worktrees_before" ] && [ "$(git -C "$CHAIN" status --porcelain --ignored)" = "$tree_before" ]'
|
|
569
|
+
|
|
570
|
+
out="$(run_chain --base "$CHAIN_BASE" --paths skills)"; rc=$?
|
|
571
|
+
check "a narrowed --paths scope does not evaluate the chain, because each round's ledger binds the whole round" \
|
|
572
|
+
'[ "$rc" = 1 ] && case "$out" in *"default path set"*) true;; *) false;; esac'
|
|
573
|
+
|
|
574
|
+
# The promotion pull request is brought up to date with its target: the target
|
|
575
|
+
# advanced, and the sync merge's second parent is already on the base. That step
|
|
576
|
+
# owes no evidence, but it must still be exactly the automatic merge.
|
|
577
|
+
git -C "$CHAIN" checkout -q main
|
|
578
|
+
printf 'baseline\nfixed on the target\n' >"$CHAIN/README.md"
|
|
579
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "target advances"
|
|
580
|
+
CHAIN_MAIN="$(git -C "$CHAIN" rev-parse HEAD)"
|
|
581
|
+
git -C "$CHAIN" checkout -q dev
|
|
582
|
+
git -C "$CHAIN" merge -q --no-ff -m "sync target into dev" main
|
|
583
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
584
|
+
check "a sync merge of the advanced target on top of the chain owes no evidence and still binds" \
|
|
585
|
+
'[ "$rc" = 0 ] && case "$out" in *"first-parent chain"*"2 rounds"*"sync"*) true;; *) false;; esac'
|
|
586
|
+
CHAIN_GOOD="$(git -C "$CHAIN" rev-parse HEAD)"
|
|
587
|
+
|
|
588
|
+
# A round the integration branch advanced past before it merged: its fork point
|
|
589
|
+
# is not the merge's first parent, the merge is still the automatic one, and the
|
|
590
|
+
# round's ledger was bound at its own fork point.
|
|
591
|
+
git -C "$CHAIN" checkout -q -b round-late dev
|
|
592
|
+
printf 'round late\n' >"$CHAIN/lane/late.txt"
|
|
593
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round late"
|
|
594
|
+
LATE_DIGEST="$(run_chain --base dev --print-candidate)"
|
|
595
|
+
mkdir -p "$CHAIN/specs/late/evidence"
|
|
596
|
+
write_closeout "$CHAIN/specs/late/evidence/closeout.json" "$LATE_DIGEST"
|
|
597
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round late ledger"
|
|
598
|
+
git -C "$CHAIN" checkout -q dev
|
|
599
|
+
edit_round_c() { printf 'round c\n' >"$CHAIN/lane/c.txt"; }
|
|
600
|
+
land_round c with-ledger edit_round_c
|
|
601
|
+
git -C "$CHAIN" merge -q --no-ff -m "merge round late" round-late
|
|
602
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
603
|
+
check "a round merged after the integration branch advanced still binds at its own fork point" \
|
|
604
|
+
'[ "$rc" = 0 ] && case "$out" in *"first-parent chain"*"4 rounds"*) true;; *) false;; esac'
|
|
605
|
+
CHAIN_ADVANCED="$(git -C "$CHAIN" rev-parse HEAD)"
|
|
606
|
+
|
|
607
|
+
# Every way a chain step can fail to be a reviewed round is refused for its own
|
|
608
|
+
# reason. Each probe branches from the passing state so the cases stay independent.
|
|
609
|
+
probe_chain() { git -C "$CHAIN" checkout -q -B dev "$CHAIN_GOOD"; }
|
|
610
|
+
|
|
611
|
+
probe_chain
|
|
612
|
+
edit_round_d() { printf 'round d\n' >"$CHAIN/lane/d.txt"; }
|
|
613
|
+
land_round d no-ledger edit_round_d
|
|
614
|
+
UNBOUND_MERGE="$(git -C "$CHAIN" rev-parse --short=12 HEAD)"
|
|
615
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
616
|
+
check "a round with no accepted ledger at its own base refuses the whole chain, naming that round" \
|
|
617
|
+
'[ "$rc" = 1 ] && case "$out" in *"round $UNBOUND_MERGE"*"does not bind at its own base"*) true;; *) false;; esac'
|
|
618
|
+
|
|
619
|
+
probe_chain
|
|
620
|
+
printf 'pushed straight to the integration branch\n' >"$CHAIN/lane/direct.txt"
|
|
621
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "direct commit on dev"
|
|
622
|
+
DIRECT="$(git -C "$CHAIN" rev-parse --short=12 HEAD)"
|
|
623
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
624
|
+
check "a non-merge commit on the integration branch is refused: nothing reviewed binds its content" \
|
|
625
|
+
'[ "$rc" = 1 ] && case "$out" in *"$DIRECT is not a merge commit"*) true;; *) false;; esac'
|
|
626
|
+
|
|
627
|
+
probe_chain
|
|
628
|
+
git -C "$CHAIN" checkout -q -b round-evil dev
|
|
629
|
+
printf 'round evil\n' >"$CHAIN/lane/evil.txt"
|
|
630
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round evil"
|
|
631
|
+
EVIL_DIGEST="$(run_chain --base dev --print-candidate)"
|
|
632
|
+
mkdir -p "$CHAIN/specs/evil/evidence"
|
|
633
|
+
write_closeout "$CHAIN/specs/evil/evidence/closeout.json" "$EVIL_DIGEST"
|
|
634
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round evil ledger"
|
|
635
|
+
git -C "$CHAIN" checkout -q dev
|
|
636
|
+
git -C "$CHAIN" merge -q --no-ff --no-commit round-evil
|
|
637
|
+
printf 'slipped into the merge commit itself\n' >"$CHAIN/lane/smuggled.txt"
|
|
638
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "merge round evil (with extra content)"
|
|
639
|
+
EVIL_MERGE="$(git -C "$CHAIN" rev-parse --short=12 HEAD)"
|
|
640
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
641
|
+
check "a merge whose tree is not the automatic merge of its parents is refused, naming the merge" \
|
|
642
|
+
'[ "$rc" = 1 ] && case "$out" in *"$EVIL_MERGE"*"automatic merge"*) true;; *) false;; esac'
|
|
643
|
+
|
|
644
|
+
probe_chain
|
|
645
|
+
git -C "$CHAIN" checkout -q -b round-conflict "$CHAIN_TWO"
|
|
646
|
+
printf 'baseline\nconflicting fix\n' >"$CHAIN/README.md"
|
|
647
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round conflict"
|
|
648
|
+
CONFLICT_DIGEST="$(run_chain --base "$CHAIN_TWO" --print-candidate)"
|
|
649
|
+
mkdir -p "$CHAIN/specs/conflict/evidence"
|
|
650
|
+
write_closeout "$CHAIN/specs/conflict/evidence/closeout.json" "$CONFLICT_DIGEST"
|
|
651
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round conflict ledger"
|
|
652
|
+
git -C "$CHAIN" checkout -q dev
|
|
653
|
+
git -C "$CHAIN" merge -q --no-ff --no-commit round-conflict >/dev/null 2>&1 || true
|
|
654
|
+
printf 'baseline\nresolved by hand\n' >"$CHAIN/README.md"
|
|
655
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "merge round conflict (resolved by hand)"
|
|
656
|
+
CONFLICT_MERGE="$(git -C "$CHAIN" rev-parse --short=12 HEAD)"
|
|
657
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
658
|
+
check "a merge that needed hand resolution is refused: the resolution is content no round reviewed" \
|
|
659
|
+
'[ "$rc" = 1 ] && case "$out" in *"$CONFLICT_MERGE"*"automatic merge"*) true;; *) false;; esac'
|
|
660
|
+
|
|
661
|
+
probe_chain
|
|
662
|
+
git -C "$CHAIN" checkout -q -b round-stale dev
|
|
663
|
+
printf 'round stale\n' >"$CHAIN/lane/stale.txt"
|
|
664
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round stale"
|
|
665
|
+
STALE_DIGEST="$(run_chain --base dev --print-candidate)"
|
|
666
|
+
mkdir -p "$CHAIN/specs/stale/evidence"
|
|
667
|
+
write_closeout "$CHAIN/specs/stale/evidence/closeout.json" "$STALE_DIGEST"
|
|
668
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round stale ledger"
|
|
669
|
+
printf 'edited after the review\n' >>"$CHAIN/lane/stale.txt"
|
|
670
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round stale moves after its ledger"
|
|
671
|
+
git -C "$CHAIN" checkout -q dev
|
|
672
|
+
git -C "$CHAIN" merge -q --no-ff -m "merge round stale" round-stale
|
|
673
|
+
STALE_MERGE="$(git -C "$CHAIN" rev-parse --short=12 HEAD)"
|
|
674
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
675
|
+
check "a round whose ledger binds a candidate it later moved away from is refused" \
|
|
676
|
+
'[ "$rc" = 1 ] && case "$out" in *"round $STALE_MERGE"*"does not bind at its own base"*) true;; *) false;; esac'
|
|
677
|
+
|
|
678
|
+
# A round is judged with the landing tree's controller and validator, never its
|
|
679
|
+
# own: a round that installs an accept-all validator in its own branch, forges a
|
|
680
|
+
# ledger the real validator rejects, and is later followed by a round restoring
|
|
681
|
+
# the real validator must not bind through history's tools.
|
|
682
|
+
probe_chain
|
|
683
|
+
git -C "$CHAIN" checkout -q -b round-forged dev
|
|
684
|
+
printf 'round forged\n' >"$CHAIN/lane/forged.txt"
|
|
685
|
+
printf 'import sys\nprint("extraction_review_state_ok: hollow validator accepts everything")\nsys.exit(0)\n' \
|
|
686
|
+
>"$CHAIN/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py"
|
|
687
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round forged"
|
|
688
|
+
FORGED_DIGEST="$(run_chain --base dev --print-candidate)"
|
|
689
|
+
mkdir -p "$CHAIN/specs/forged/evidence"
|
|
690
|
+
python3 - "$CHAIN/specs/forged/evidence/closeout.json" "$FORGED_DIGEST" <<'PY'
|
|
691
|
+
import json, sys
|
|
692
|
+
from pathlib import Path
|
|
693
|
+
Path(sys.argv[1]).write_text(json.dumps({"schema_version": 3, "closeout_state": "forged", "controller_receipts": [], "candidate_sha256": sys.argv[2]}))
|
|
694
|
+
PY
|
|
695
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "round forged ledger"
|
|
696
|
+
git -C "$CHAIN" checkout -q dev
|
|
697
|
+
git -C "$CHAIN" merge -q --no-ff -m "merge round forged" round-forged
|
|
698
|
+
FORGED_MERGE="$(git -C "$CHAIN" rev-parse --short=12 HEAD)"
|
|
699
|
+
edit_round_restore() { printf '%s' "$STRICT_VALIDATOR" >"$CHAIN/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py"; }
|
|
700
|
+
land_round restore with-ledger edit_round_restore
|
|
701
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
702
|
+
check "a round that forged a ledger under its own hollow validator is refused even after a later round restored the real one" \
|
|
703
|
+
'[ "$rc" = 1 ] && case "$out" in *"round $FORGED_MERGE"*"does not bind at its own base"*) true;; *) false;; esac'
|
|
704
|
+
|
|
705
|
+
# The walk is bounded at exactly 64 steps: a chain of 64 passes, 65 is refused.
|
|
706
|
+
# Receipt-only rounds keep the fixture fast, since a round whose only change is
|
|
707
|
+
# an excluded receipt binds as "no reviewed-path change" without a freeze.
|
|
708
|
+
probe_chain
|
|
709
|
+
fill_round() {
|
|
710
|
+
git -C "$CHAIN" checkout -q -b "round-fill-$1" dev
|
|
711
|
+
mkdir -p "$CHAIN/specs/fill-$1/evidence"
|
|
712
|
+
printf '{"schema_version": 3, "candidate_sha256": "%s"}\n' "$(printf '0%.0s' $(seq 64))" >"$CHAIN/specs/fill-$1/evidence/note.json"
|
|
713
|
+
git -C "$CHAIN" add -A && git -C "$CHAIN" commit -qm "fill $1"
|
|
714
|
+
git -C "$CHAIN" checkout -q dev
|
|
715
|
+
git -C "$CHAIN" merge -q --no-ff -m "merge fill $1" "round-fill-$1"
|
|
716
|
+
}
|
|
717
|
+
for index in $(seq 1 61); do fill_round "$index"; done
|
|
718
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
719
|
+
check "a chain of exactly 64 steps is still walked and binds" \
|
|
720
|
+
'[ "$rc" = 0 ] && case "$out" in *"first-parent chain"*"63 rounds"*) true;; *) false;; esac'
|
|
721
|
+
fill_round 62
|
|
722
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
723
|
+
check "a chain of 65 steps is refused as longer than the bound" \
|
|
724
|
+
'[ "$rc" = 1 ] && case "$out" in *"more than 64 steps"*) true;; *) false;; esac'
|
|
725
|
+
|
|
726
|
+
# A checkout this run cannot release is an error, never a pass: the removal's
|
|
727
|
+
# result is checked and the registry read back, and nothing prunes registrations
|
|
728
|
+
# the run did not create. A git shim refuses only `worktree remove`.
|
|
729
|
+
git -C "$CHAIN" checkout -q -B dev "$CHAIN_GOOD"
|
|
730
|
+
REAL_GIT="$(command -v git)"
|
|
731
|
+
mkdir -p "$WORK/fakegit" "$WORK/shim-tmp"
|
|
732
|
+
cat >"$WORK/fakegit/git" <<EOF
|
|
733
|
+
#!/usr/bin/env bash
|
|
734
|
+
if [ "\$1" = "-C" ] && [ "\$3" = "worktree" ] && [ "\$4" = "remove" ]; then
|
|
735
|
+
echo "shim: refusing to remove the worktree" >&2
|
|
736
|
+
exit 1
|
|
737
|
+
fi
|
|
738
|
+
exec "$REAL_GIT" "\$@"
|
|
739
|
+
EOF
|
|
740
|
+
chmod +x "$WORK/fakegit/git"
|
|
741
|
+
out="$(PATH="$WORK/fakegit:$PATH" TMPDIR="$WORK/shim-tmp" run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
742
|
+
check "a detached checkout that cannot be released turns the verdict into an error, never a pass" \
|
|
743
|
+
'[ "$rc" != 0 ] && case "$out" in *"cannot release the detached checkout"*) case "$out" in *review_ledger_binding_ok*) false;; *) true;; esac;; *) false;; esac'
|
|
744
|
+
git -C "$CHAIN" worktree prune
|
|
745
|
+
|
|
746
|
+
# `git worktree add` can register and populate the checkout and still return
|
|
747
|
+
# nonzero. The failure is an error either way, but the registration it left
|
|
748
|
+
# behind must be released and verified, not merely have its directory deleted.
|
|
749
|
+
mkdir -p "$WORK/fakegit-add" "$WORK/shim-add-tmp"
|
|
750
|
+
cat >"$WORK/fakegit-add/git" <<EOF
|
|
751
|
+
#!/usr/bin/env bash
|
|
752
|
+
if [ "\$1" = "-C" ] && [ "\$3" = "worktree" ] && [ "\$4" = "add" ]; then
|
|
753
|
+
"$REAL_GIT" "\$@"
|
|
754
|
+
echo "shim: worktree add failed after registering" >&2
|
|
755
|
+
exit 1
|
|
756
|
+
fi
|
|
757
|
+
exec "$REAL_GIT" "\$@"
|
|
758
|
+
EOF
|
|
759
|
+
chmod +x "$WORK/fakegit-add/git"
|
|
760
|
+
worktrees_before="$(git -C "$CHAIN" worktree list --porcelain)"
|
|
761
|
+
out="$(PATH="$WORK/fakegit-add:$PATH" TMPDIR="$WORK/shim-add-tmp" run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
762
|
+
check "a checkout creation that fails after registering is an error and leaves no registration or directory behind" \
|
|
763
|
+
'[ "$rc" != 0 ] && case "$out" in *"cannot check out round head"*) case "$out" in *review_ledger_binding_ok*) false;; *) true;; esac;; *) false;; esac && [ "$(git -C "$CHAIN" worktree list --porcelain)" = "$worktrees_before" ] && [ -z "$(ls -A "$WORK/shim-add-tmp")" ]'
|
|
764
|
+
git -C "$CHAIN" worktree prune
|
|
765
|
+
|
|
766
|
+
# The benign sibling: an add that fails before registering anything must error
|
|
767
|
+
# the same way, leave the registry untouched, and remove the directory this run
|
|
768
|
+
# created for it, without reporting a release problem it does not have.
|
|
769
|
+
mkdir -p "$WORK/fakegit-noadd" "$WORK/shim-noadd-tmp"
|
|
770
|
+
cat >"$WORK/fakegit-noadd/git" <<EOF
|
|
771
|
+
#!/usr/bin/env bash
|
|
772
|
+
if [ "\$1" = "-C" ] && [ "\$3" = "worktree" ] && [ "\$4" = "add" ]; then
|
|
773
|
+
echo "shim: worktree add refused before registering" >&2
|
|
774
|
+
exit 1
|
|
775
|
+
fi
|
|
776
|
+
exec "$REAL_GIT" "\$@"
|
|
777
|
+
EOF
|
|
778
|
+
chmod +x "$WORK/fakegit-noadd/git"
|
|
779
|
+
worktrees_before="$(git -C "$CHAIN" worktree list --porcelain)"
|
|
780
|
+
out="$(PATH="$WORK/fakegit-noadd:$PATH" TMPDIR="$WORK/shim-noadd-tmp" run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
781
|
+
check "a checkout creation that fails before registering errors cleanly with no registration, no directory, and no release complaint" \
|
|
782
|
+
'[ "$rc" != 0 ] && case "$out" in *"cannot check out round head"*) case "$out" in *"could not be released"*|*review_ledger_binding_ok*) false;; *) true;; esac;; *) false;; esac && [ "$(git -C "$CHAIN" worktree list --porcelain)" = "$worktrees_before" ] && [ -z "$(ls -A "$WORK/shim-noadd-tmp")" ]'
|
|
783
|
+
|
|
784
|
+
# The registry is read NUL-terminated: line-oriented porcelain cannot carry a
|
|
785
|
+
# path with a newline (the line splits; a tab is printed raw on the git in use,
|
|
786
|
+
# and quoting depends on version and core.quotePath), so a registration under
|
|
787
|
+
# such a temporary directory would match neither spelling and read as gone.
|
|
788
|
+
# Here the add registers and fails, the remove is refused, and the surviving
|
|
789
|
+
# registration must be reported.
|
|
790
|
+
TAB_TMP="$WORK/shim nl
|
|
791
|
+
dir"
|
|
792
|
+
mkdir -p "$WORK/fakegit-tab" "$TAB_TMP"
|
|
793
|
+
cat >"$WORK/fakegit-tab/git" <<EOF
|
|
794
|
+
#!/usr/bin/env bash
|
|
795
|
+
if [ "\$1" = "-C" ] && [ "\$3" = "worktree" ] && [ "\$4" = "add" ]; then
|
|
796
|
+
"$REAL_GIT" "\$@"
|
|
797
|
+
echo "shim: worktree add failed after registering" >&2
|
|
798
|
+
exit 1
|
|
799
|
+
fi
|
|
800
|
+
if [ "\$1" = "-C" ] && [ "\$3" = "worktree" ] && [ "\$4" = "remove" ]; then
|
|
801
|
+
echo "shim: refusing to remove the worktree" >&2
|
|
802
|
+
exit 1
|
|
803
|
+
fi
|
|
804
|
+
exec "$REAL_GIT" "\$@"
|
|
805
|
+
EOF
|
|
806
|
+
chmod +x "$WORK/fakegit-tab/git"
|
|
807
|
+
worktrees_before="$(git -C "$CHAIN" worktree list --porcelain)"
|
|
808
|
+
out="$(PATH="$WORK/fakegit-tab:$PATH" TMPDIR="$TAB_TMP" run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
809
|
+
check "a surviving registration under a newline-bearing temporary path is still reported, not read as gone through line-oriented porcelain" \
|
|
810
|
+
'[ "$rc" != 0 ] && case "$out" in *"cannot check out round head"*"could not be released"*"still registered"*) true;; *) false;; esac'
|
|
811
|
+
rm -rf "$TAB_TMP"
|
|
812
|
+
git -C "$CHAIN" worktree prune
|
|
813
|
+
# This case deliberately leaves a registration behind, so its own cleanup is
|
|
814
|
+
# what restores the fixture. Prune only drops registrations whose directory is
|
|
815
|
+
# already gone, so the directory must go first; asserting the restoration keeps
|
|
816
|
+
# a later reordering from silently handing every following case a mutated
|
|
817
|
+
# registry.
|
|
818
|
+
check "the newline-path case restores the fixture's worktree registry for the cases that follow" \
|
|
819
|
+
'[ "$(git -C "$CHAIN" worktree list --porcelain)" = "$worktrees_before" ]'
|
|
820
|
+
|
|
821
|
+
git -C "$CHAIN" checkout -q -B dev "$CHAIN_ADVANCED"
|
|
822
|
+
out="$(run_chain --base "$CHAIN_MAIN")"; rc=$?
|
|
823
|
+
check "the passing chain state still passes after the probes" \
|
|
824
|
+
'[ "$rc" = 0 ]'
|
|
825
|
+
|
|
495
826
|
# The suite above exercises a synthetic repository. The gate must also run against
|
|
496
827
|
# the real checkout it ships in, or a break in that path passes every test here.
|
|
497
828
|
REAL_ROOT="$(cd "$DIR/../../.." && pwd -P)"
|
package/dist/assets/release.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"npmPackage": "@ccoalm/ccl-skills",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"sourceCommit": "
|
|
4
|
+
"version": "0.14.0",
|
|
5
|
+
"sourceCommit": "3b42a5bd027e29f72b90b055c763333964d46259",
|
|
6
6
|
"sourceState": "clean",
|
|
7
7
|
"files": [
|
|
8
8
|
{
|
|
@@ -1992,7 +1992,7 @@
|
|
|
1992
1992
|
},
|
|
1993
1993
|
{
|
|
1994
1994
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md",
|
|
1995
|
-
"sha256": "
|
|
1995
|
+
"sha256": "b09b1b6908e87b75ecdb2cba5c2499b4a9bd490f7c7225b509a560285a61cb9d",
|
|
1996
1996
|
"mode": 420
|
|
1997
1997
|
},
|
|
1998
1998
|
{
|
|
@@ -2005,24 +2005,29 @@
|
|
|
2005
2005
|
"sha256": "a7ff4aea676dcf3abc62316a2ddb10c88b035d2f6a7b97d5a0343d8533aeb33c",
|
|
2006
2006
|
"mode": 420
|
|
2007
2007
|
},
|
|
2008
|
+
{
|
|
2009
|
+
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/correction-routing-map.md",
|
|
2010
|
+
"sha256": "6712b1a91e3a019795e1fbf58fbe52042a31962674159f868671117003f46a12",
|
|
2011
|
+
"mode": 420
|
|
2012
|
+
},
|
|
2008
2013
|
{
|
|
2009
2014
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/coverage-exhaustion-traps.md",
|
|
2010
|
-
"sha256": "
|
|
2015
|
+
"sha256": "0070f58a0df463fe491d760b2378fae85d7fea956fc01ac4a4b0aae05a25b4b5",
|
|
2011
2016
|
"mode": 420
|
|
2012
2017
|
},
|
|
2013
2018
|
{
|
|
2014
2019
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md",
|
|
2015
|
-
"sha256": "
|
|
2020
|
+
"sha256": "6d5ebb079c9f3570985c12a30995a09174ee2efef066a9da7efe89b56c0f0166",
|
|
2016
2021
|
"mode": 420
|
|
2017
2022
|
},
|
|
2018
2023
|
{
|
|
2019
2024
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md",
|
|
2020
|
-
"sha256": "
|
|
2025
|
+
"sha256": "9deda45197efe202db5b3ed1d603aa1e97090476558a254b4612c7c6ab7c95ee",
|
|
2021
2026
|
"mode": 420
|
|
2022
2027
|
},
|
|
2023
2028
|
{
|
|
2024
2029
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md",
|
|
2025
|
-
"sha256": "
|
|
2030
|
+
"sha256": "867941f4dcb59c19b36310f8f304bf883e77a5c58c9e9d5cf82e879720dff0de",
|
|
2026
2031
|
"mode": 420
|
|
2027
2032
|
},
|
|
2028
2033
|
{
|
|
@@ -2037,7 +2042,7 @@
|
|
|
2037
2042
|
},
|
|
2038
2043
|
{
|
|
2039
2044
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md",
|
|
2040
|
-
"sha256": "
|
|
2045
|
+
"sha256": "665eaecb1f9ef219f581fa86a02df4bb378694dbc4ff52b5117f7c929bdec287",
|
|
2041
2046
|
"mode": 420
|
|
2042
2047
|
},
|
|
2043
2048
|
{
|
|
@@ -2047,7 +2052,7 @@
|
|
|
2047
2052
|
},
|
|
2048
2053
|
{
|
|
2049
2054
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md",
|
|
2050
|
-
"sha256": "
|
|
2055
|
+
"sha256": "7ed3fa06032d45fa6d0ab03679cf64e571a80cdf470f34476d77ab54db0869a4",
|
|
2051
2056
|
"mode": 420
|
|
2052
2057
|
},
|
|
2053
2058
|
{
|
|
@@ -2057,12 +2062,12 @@
|
|
|
2057
2062
|
},
|
|
2058
2063
|
{
|
|
2059
2064
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/harness-patterns-and-eval.md",
|
|
2060
|
-
"sha256": "
|
|
2065
|
+
"sha256": "2149bf40adc0eabdd7d0ad4ebc7938288e6c74050ca979d9227f5d9221bec8cb",
|
|
2061
2066
|
"mode": 420
|
|
2062
2067
|
},
|
|
2063
2068
|
{
|
|
2064
2069
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/incident-postmortem-extraction.md",
|
|
2065
|
-
"sha256": "
|
|
2070
|
+
"sha256": "31ee01a1b025f1b89631f1b133dcb5449df8087bff49a80065684f39656a027b",
|
|
2066
2071
|
"mode": 420
|
|
2067
2072
|
},
|
|
2068
2073
|
{
|
|
@@ -2112,7 +2117,7 @@
|
|
|
2112
2117
|
},
|
|
2113
2118
|
{
|
|
2114
2119
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/rule-consolidation.md",
|
|
2115
|
-
"sha256": "
|
|
2120
|
+
"sha256": "e4c73b6bd412c9aca273cc36af7d987491e77f7da9639d7469110b2dc6da338a",
|
|
2116
2121
|
"mode": 420
|
|
2117
2122
|
},
|
|
2118
2123
|
{
|
|
@@ -2122,7 +2127,7 @@
|
|
|
2122
2127
|
},
|
|
2123
2128
|
{
|
|
2124
2129
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md",
|
|
2125
|
-
"sha256": "
|
|
2130
|
+
"sha256": "12ca28829763dcd5b1e7e55ffe32c5189757855d9e22d34a56c4dc1524293e18",
|
|
2126
2131
|
"mode": 420
|
|
2127
2132
|
},
|
|
2128
2133
|
{
|
|
@@ -2137,7 +2142,7 @@
|
|
|
2137
2142
|
},
|
|
2138
2143
|
{
|
|
2139
2144
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/uiux-judgment-extraction.md",
|
|
2140
|
-
"sha256": "
|
|
2145
|
+
"sha256": "969808be6cf5234e689a7675c2108d1ac464882b93216e600122e02bdc05f221",
|
|
2141
2146
|
"mode": 420
|
|
2142
2147
|
},
|
|
2143
2148
|
{
|
|
@@ -2147,7 +2152,7 @@
|
|
|
2147
2152
|
},
|
|
2148
2153
|
{
|
|
2149
2154
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md",
|
|
2150
|
-
"sha256": "
|
|
2155
|
+
"sha256": "cd5cbc4ae9185a1c96fc945dee1bcfd958638498f01ba2487b772a2e9cc49ece",
|
|
2151
2156
|
"mode": 420
|
|
2152
2157
|
},
|
|
2153
2158
|
{
|
|
@@ -2195,6 +2200,11 @@
|
|
|
2195
2200
|
"sha256": "10cdd03d67185c1b1730a34248edb6c311e6ee8b82b8ecc0f7923e8f8f368b59",
|
|
2196
2201
|
"mode": 420
|
|
2197
2202
|
},
|
|
2203
|
+
{
|
|
2204
|
+
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/entrypoint_form_census.py",
|
|
2205
|
+
"sha256": "878c4694c4177a29f19c3b46d9b595696a768e26ff275429d380bf0e4bfc809e",
|
|
2206
|
+
"mode": 493
|
|
2207
|
+
},
|
|
2198
2208
|
{
|
|
2199
2209
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-golden-trace.rb",
|
|
2200
2210
|
"sha256": "14c3c7c1437c6d70c5ab48ac8b1312fab3a421de31b110da84fa0528762b924c",
|
|
@@ -2245,6 +2255,11 @@
|
|
|
2245
2255
|
"sha256": "8bd90fc37d53e466ac5735d2fc956a9d3a31bcd31dd51614ae9e15d435e39726",
|
|
2246
2256
|
"mode": 420
|
|
2247
2257
|
},
|
|
2258
|
+
{
|
|
2259
|
+
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/reference-access-census.sh",
|
|
2260
|
+
"sha256": "c408c6d4871d91b9a7d345959154ad70e2c5bf8233ff7e88bbd7d6cd7bb01323",
|
|
2261
|
+
"mode": 493
|
|
2262
|
+
},
|
|
2248
2263
|
{
|
|
2249
2264
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/register-firing-path-resolution.rb",
|
|
2250
2265
|
"sha256": "db009982ac485aaebf708c4bff9b33fc65e2fef6264769ec1e42870f75bc8677",
|
|
@@ -2252,7 +2267,7 @@
|
|
|
2252
2267
|
},
|
|
2253
2268
|
{
|
|
2254
2269
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/review_ledger_binding.py",
|
|
2255
|
-
"sha256": "
|
|
2270
|
+
"sha256": "ae7b2aa0c7f0afa569f424fbd57d62209e21fd4da9a6b94da15686eb4b2d8719",
|
|
2256
2271
|
"mode": 493
|
|
2257
2272
|
},
|
|
2258
2273
|
{
|
|
@@ -2312,7 +2327,7 @@
|
|
|
2312
2327
|
},
|
|
2313
2328
|
{
|
|
2314
2329
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh",
|
|
2315
|
-
"sha256": "
|
|
2330
|
+
"sha256": "c8309b75883f8e7e404ae15a85efc347e58f0255385bf57ddb6b200a3b587588",
|
|
2316
2331
|
"mode": 493
|
|
2317
2332
|
},
|
|
2318
2333
|
{
|
|
@@ -2365,6 +2380,11 @@
|
|
|
2365
2380
|
"sha256": "46016ad11df8ee75dd7aa17c2e017fad60dc127a234f0bd4c5bcb3baf4f7d0ee",
|
|
2366
2381
|
"mode": 493
|
|
2367
2382
|
},
|
|
2383
|
+
{
|
|
2384
|
+
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_entrypoint_form_census.sh",
|
|
2385
|
+
"sha256": "2cb5a411fc9badfd0667cea9cc20567b7edade74b30520de184c1dfca99b6491",
|
|
2386
|
+
"mode": 493
|
|
2387
|
+
},
|
|
2368
2388
|
{
|
|
2369
2389
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_eval_routing_bank_grader_diagnostics.sh",
|
|
2370
2390
|
"sha256": "10cacb2baf142dbedddbecb34ac2ee1bd9d043b9c946acff9ce83373be0d1553",
|
|
@@ -2460,6 +2480,11 @@
|
|
|
2460
2480
|
"sha256": "c495841ae6cd6cb3bf4c65a9883da08b4b24f50b53dba2219df9327a41980251",
|
|
2461
2481
|
"mode": 493
|
|
2462
2482
|
},
|
|
2483
|
+
{
|
|
2484
|
+
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_reference_access_census.sh",
|
|
2485
|
+
"sha256": "4aa6d437078b939270e607d1ad4ea17f4043824aee807859a795a85f7ebc00a6",
|
|
2486
|
+
"mode": 493
|
|
2487
|
+
},
|
|
2463
2488
|
{
|
|
2464
2489
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_register_firing_path_resolution.sh",
|
|
2465
2490
|
"sha256": "4894f739af0b3aa995243592d71ca3f93de3108a11f9734422a4f15c2ca33668",
|
|
@@ -2482,7 +2507,7 @@
|
|
|
2482
2507
|
},
|
|
2483
2508
|
{
|
|
2484
2509
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_review_ledger_binding.sh",
|
|
2485
|
-
"sha256": "
|
|
2510
|
+
"sha256": "a7274e735371dcd80b4c1b7745e3e5648d7e84b1dee676bf2743f4600751a770",
|
|
2486
2511
|
"mode": 493
|
|
2487
2512
|
},
|
|
2488
2513
|
{
|
|
@@ -2542,7 +2567,7 @@
|
|
|
2542
2567
|
},
|
|
2543
2568
|
{
|
|
2544
2569
|
"path": "marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md",
|
|
2545
|
-
"sha256": "
|
|
2570
|
+
"sha256": "5d46b33dcc0c84ec4cac51bdea93aba643427f88dcd7ee3930b3ecd2132bc4f0",
|
|
2546
2571
|
"mode": 420
|
|
2547
2572
|
},
|
|
2548
2573
|
{
|
|
@@ -3378,5 +3403,5 @@
|
|
|
3378
3403
|
"mode": 420
|
|
3379
3404
|
}
|
|
3380
3405
|
],
|
|
3381
|
-
"snapshotHash": "
|
|
3406
|
+
"snapshotHash": "ba1bcedf0fe6247bda9eb70091de735a5e6fef8e7fc8d82f573841870dcb5302"
|
|
3382
3407
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ccoalm/ccl-skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Reusable workflows that help coding agents plan, build, test, review, and release software — for Claude Code, Codex, and OpenCode",
|
|
5
5
|
"keywords": ["skills", "agent-skills", "claude", "claude-code", "codex", "opencode", "agent", "ai", "ai-agents", "cli", "anthropic", "developer-tools"],
|
|
6
6
|
"type": "module",
|