@ccoalm/ccl-skills 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +5 -0
  2. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/manual-invocation-and-prompts.md +6 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +5 -0
  4. package/dist/assets/marketplace/plugins/ccl-skills/skills/defect-diagnosis/SKILL.md +1 -0
  5. package/dist/assets/marketplace/plugins/ccl-skills/skills/feature-risk-router/SKILL.md +3 -1
  6. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +1 -1
  7. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +1 -1
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/references/state-machine-task-patterns.md +2 -0
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/inference-capacity-operations.md +24 -0
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/llm-client-gateway.md +1 -1
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/references/model-prompt-evaluation.md +4 -1
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/references/contracts-and-state.md +5 -0
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +1 -0
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +3 -2
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/metrics-conventions.md +8 -1
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +2 -2
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/canary-and-rollout-strategy.md +16 -2
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +9 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +12 -12
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/code-review-checklist.md +4 -0
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/delivery-lifecycle.md +1 -1
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/rd-standards-doc-family-checklist.md +1 -0
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +2 -0
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +2 -2
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +1 -0
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +8 -0
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +1 -1
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +2 -0
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +1 -1
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +12 -15
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md +37 -0
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md +13 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +37 -30
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md +24 -3
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +5 -5
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/rule-consolidation.md +1 -1
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +81 -0
  38. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +12 -0
  39. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +1 -1
  40. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +30 -0
  41. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-contract-anchors.sh +126 -0
  42. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-size-budget.sh +197 -1
  43. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/contract-anchors.tsv +15 -0
  44. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-routing-bank.rb +210 -36
  45. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +3 -3
  46. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/gate_receipt.py +576 -0
  47. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_antipattern_grep_panel.sh +80 -0
  48. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_body_compliance_grading.sh +99 -0
  49. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +25 -0
  50. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_size_budget.sh +251 -0
  51. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_contract_anchors.sh +196 -0
  52. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_eval_routing_bank_grader_diagnostics.sh +222 -0
  53. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +16 -10
  54. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_frozen_case_sanctity.sh +178 -0
  55. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_frozen_case_sanctity_selfproof.sh +108 -0
  56. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_gate_receipt.sh +431 -0
  57. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_pinned_phrase_mutation_walk.sh +151 -0
  58. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_bank_integrity.sh +86 -5
  59. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +27 -21
  60. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +25 -15
  61. package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/classical-test-design-techniques.md +1 -1
  62. package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/tc-review-and-prioritization.md +1 -1
  63. package/dist/assets/marketplace/plugins/ccl-skills/skills/test-artifact-management/references/update-lifecycle.md +2 -0
  64. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +9 -9
  65. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/ci-fixtures-and-flake-control.md +5 -1
  66. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/e2e-real-flow-testing.md +2 -2
  67. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/integration-contract-testing.md +10 -0
  68. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +2 -2
  69. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-topology-and-commands.md +1 -1
  70. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +2 -1
  71. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/annotation-driven-revision.md +9 -0
  72. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/figure-and-table-craft.md +8 -2
  73. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +1 -0
  74. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/react-architecture.md +3 -0
  75. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-quality-release.md +37 -4
  76. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/references/web-ui-quality.md +10 -1
  77. package/dist/assets/release.json +127 -67
  78. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env bash
2
+ # Contract-anchor gate: declarative wording-existence pinning for load-bearing
3
+ # prose contracts that no structural check would otherwise protect.
4
+ #
5
+ # Problem class (observed, 074 RED-baseline probes): a skill reference's
6
+ # load-bearing contract sentence — a verdict-taxonomy discriminator, a
7
+ # stop-condition predicate, an externally verified numeric tier — can be
8
+ # deleted, semantically inverted, or numerically falsified while every
9
+ # structural gate stays green, because structural gates check shape and
10
+ # references, never the survival of specific contract wording.
11
+ #
12
+ # Mechanism: a sidecar TSV table (contract-anchors.tsv, same directory) lists
13
+ # one anchor per row: id <TAB> repo-relative path <TAB> pinned literal <TAB> note
14
+ # - the literal must occur EXACTLY ONCE in the named file (fixed-string match).
15
+ # 0 occurrences => contract_anchor_missing (drift/deletion) exit 1
16
+ # >1 occurrences => contract_anchor_duplicate (decoy/ambiguity;
17
+ # an anchor that matches twice can no longer prove which copy is the
18
+ # contract — same rule as the sync-pointer registry) exit 1
19
+ # - a missing anchored file is a broken contract, not infrastructure:
20
+ # contract_anchor_file_missing exit 1
21
+ # Table integrity is fail-closed (exit 2, infra): unreadable/empty table
22
+ # (an empty list scanning nothing must never certify), malformed row,
23
+ # duplicate id, or a literal under 16 characters (too weak to be unique —
24
+ # same floor as firing-path anchors).
25
+ #
26
+ # All rows are scanned before any exit: anchor-drift findings (rc 1) and
27
+ # row-integrity findings (rc 2) are each collected across the whole table, so
28
+ # one run reports every problem; when both classes are present the run exits 2
29
+ # (a broken table means no verdict can be trusted). Green output grammar is
30
+ # pinned for the caller:
31
+ # contract_anchor_gate_ok (N anchors)
32
+ # Exemptions: none, and deliberately no environment override — an anchor is
33
+ # removed or reworded only by editing the table in the same MR that changes
34
+ # the pinned wording (reject-and-instruct failure message points there).
35
+ #
36
+ # Intentional-change recipe (printed on failure): edit the contract sentence
37
+ # AND its table row together; the diff then shows the contract change
38
+ # explicitly instead of a silent drift.
39
+ set -u
40
+
41
+ script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
42
+ root="${1:-.}"
43
+ table="$script_dir/contract-anchors.tsv"
44
+ if [[ "${2:-}" == "--table" && -n "${3:-}" ]]; then
45
+ table="$3"
46
+ fi
47
+
48
+ if [[ ! -d "$root" ]]; then
49
+ echo "contract_anchor_root_missing: $root" >&2
50
+ exit 2
51
+ fi
52
+ if [[ ! -f "$table" ]]; then
53
+ echo "contract_anchor_table_missing: $table" >&2
54
+ exit 2
55
+ fi
56
+
57
+ anchor_count=0
58
+ fail_count=0
59
+ infra_count=0
60
+ # Bash 3.2-safe id-uniqueness set (stock macOS bash has no associative arrays):
61
+ # newline-delimited membership string, ids are TSV fields so they carry no tabs
62
+ # and no newlines.
63
+ seen_ids=$'\n'
64
+ line_no=0
65
+ while IFS= read -r raw || [[ -n "$raw" ]]; do
66
+ line_no=$((line_no + 1))
67
+ [[ -z "$raw" || "$raw" == \#* ]] && continue
68
+ IFS=$'\t' read -r id path literal note <<<"$raw"
69
+ if [[ -z "${id:-}" || -z "${path:-}" || -z "${literal:-}" || -z "${note:-}" ]]; then
70
+ echo "contract_anchor_row_malformed: line $line_no of $table (need id<TAB>path<TAB>literal<TAB>note)" >&2
71
+ infra_count=$((infra_count + 1))
72
+ continue
73
+ fi
74
+ if [[ "$note" == *$'\t'* ]]; then
75
+ echo "contract_anchor_row_malformed: line $line_no of $table (extra tab-separated field)" >&2
76
+ infra_count=$((infra_count + 1))
77
+ continue
78
+ fi
79
+ case "$seen_ids" in
80
+ *$'\n'"$id"$'\n'*)
81
+ echo "contract_anchor_duplicate_id: $id (line $line_no repeats an earlier row's id)" >&2
82
+ infra_count=$((infra_count + 1))
83
+ continue
84
+ ;;
85
+ esac
86
+ seen_ids="$seen_ids$id"$'\n'
87
+ if (( ${#literal} < 16 )); then
88
+ echo "contract_anchor_literal_too_short: $id (${#literal} chars, need >=16)" >&2
89
+ infra_count=$((infra_count + 1))
90
+ continue
91
+ fi
92
+ anchor_count=$((anchor_count + 1))
93
+ target="$root/$path"
94
+ fix_line=" fix: restore the contract wording, or — for an intentional contract change — update this anchor row in $(basename "$table") in the same MR"
95
+ if [[ ! -f "$target" ]]; then
96
+ echo "contract_anchor_file_missing: $id $path" >&2
97
+ echo " fix: restore the anchored file, or — for an intentional move/retirement — update or remove this anchor row in $(basename "$table") in the same MR" >&2
98
+ fail_count=$((fail_count + 1))
99
+ continue
100
+ fi
101
+ occurrences=$(grep -oF -- "$literal" "$target" | wc -l | tr -d '[:space:]')
102
+ if [[ "$occurrences" -eq 0 ]]; then
103
+ echo "contract_anchor_missing: $id in $path" >&2
104
+ echo " pinned literal: $literal" >&2
105
+ echo "$fix_line" >&2
106
+ fail_count=$((fail_count + 1))
107
+ elif [[ "$occurrences" -gt 1 ]]; then
108
+ echo "contract_anchor_duplicate: $id in $path ($occurrences occurrences; the anchor can no longer prove which copy is the contract)" >&2
109
+ echo " fix: keep the pinned wording in exactly one place (dedupe the copy), or repin the anchor row in $(basename "$table") to a longer unique literal in the same MR" >&2
110
+ fail_count=$((fail_count + 1))
111
+ fi
112
+ done < "$table"
113
+
114
+ if [[ "$infra_count" -gt 0 ]]; then
115
+ echo "contract_anchor_table_invalid: $infra_count integrity error(s) in $table (no verdict from a broken table — fail-closed)" >&2
116
+ exit 2
117
+ fi
118
+ if [[ "$anchor_count" -eq 0 ]]; then
119
+ echo "contract_anchor_table_empty: $table has no data rows (an empty anchor set must never certify)" >&2
120
+ exit 2
121
+ fi
122
+ if [[ "$fail_count" -gt 0 ]]; then
123
+ echo "contract_anchor_gate_failed: $fail_count of $anchor_count anchors" >&2
124
+ exit 1
125
+ fi
126
+ echo "contract_anchor_gate_ok ($anchor_count anchors)"
@@ -21,6 +21,18 @@
21
21
  # advisory = no signal). A agent-context/session-start.md absent from base is treated as a NEW
22
22
  # every-session injection and blocks like a new severe entrypoint.
23
23
  #
24
+ # Blocking (delta-scoped, skills/*/references/**/*.md — the reference-file line
25
+ # ratchet; design invariants in references/attention-budget-ratchet.md):
26
+ # - a changed reference that is NEW or CROSSES over 500 physical lines (base
27
+ # was not over) => block ("new reference over line budget")
28
+ # - a changed reference already over 500 lines in base that GREW
29
+ # (head_lines > base_lines) => block ("over-limit reference grew")
30
+ # references/source-register.md is structurally excluded (append-only ledger;
31
+ # its growth is governed by the append-only contract, not a line cap) — the
32
+ # exclusion lives in this gate, never in the candidate. A NEW reference over
33
+ # 100 lines with no "##" section headings draws an advisory token (never
34
+ # blocks). Rename credit is path-paired and non-growing, same as entrypoints.
35
+ #
24
36
  # Advisory (never blocks on its own): the recommended-band body-char debt
25
37
  # counters, severe-debt counters, per-file deltas, and the agent-context/session-start.md 13000B
26
38
  # byte tripwire BAND remain VISIBILITY / DEBT-MANAGEMENT only (the band flags the
@@ -113,6 +125,11 @@ end
113
125
  WORD_BUDGET_MAX = 5000
114
126
  WORD_TOKEN_PATTERN = /\p{Han}|\uFFFD|(?:(?!\p{Han})[\p{L}\p{N}])+(?:[\u0027\u2019_-](?:(?!\p{Han})[\p{L}\p{N}])+)*/u
115
127
 
128
+ REF_LINE_MAX = 500
129
+ REF_NAV_MIN_LINES = 100
130
+ REF_PATH_RE = %r{\Askills/[^/]+/references/.+\.md\z}
131
+ REF_LEDGER_RE = %r{\Askills/[^/]+/references/source-register\.md\z}
132
+
116
133
  Metric = Struct.new(:body_chars, :body_words, :bytes, keyword_init: true)
117
134
 
118
135
  def metric_for_text(text)
@@ -195,6 +212,42 @@ def head_metric_for_path(root, rel)
195
212
  metric_for_file(full)
196
213
  end
197
214
 
215
+ def ref_info_for_text(raw)
216
+ text = raw.b.dup.force_encoding(Encoding::UTF_8).scrub { |invalid| "�" * invalid.bytesize }
217
+ # Line-ending normalization comes first: a CR-only or CRLF file must measure the
218
+ # same as the LF file a reader sees, otherwise a 501-line CR-delimited file reads
219
+ # as one line and earns a clean verdict. Headings count EXACT H2 only, because an
220
+ # H3-only file gives a chunked read no section structure to navigate by.
221
+ text = text.gsub(/\r\n?/, "\n")
222
+ lines = text.lines
223
+ { lines: lines.length, headings: lines.count { |l| l.match?(/\A##[ \t]/) } }
224
+ end
225
+
226
+ def base_ref_info_for_path(root, base, rel)
227
+ return :unknown if base.nil?
228
+ return :missing unless git_success?("cat-file", "-e", "#{base}:#{rel}", root: root)
229
+ ref_info_for_text(git_text("show", "#{base}:#{rel}", root: root))
230
+ end
231
+
232
+ def head_ref_info_for_path(root, rel)
233
+ full = File.join(root, rel)
234
+ return :missing unless File.file?(full)
235
+ ref_info_for_text(File.binread(full))
236
+ end
237
+
238
+ def fmt_ref_value(info)
239
+ case info
240
+ when :unknown then "unknown"
241
+ when :missing then "missing"
242
+ else info[:lines].to_s
243
+ end
244
+ end
245
+
246
+ def fmt_ref_delta(base_info, head_info)
247
+ return "unknown" unless base_info.is_a?(Hash) && head_info.is_a?(Hash)
248
+ format("%+d", head_info[:lines] - base_info[:lines])
249
+ end
250
+
198
251
  def fmt_metric_value(metric, field)
199
252
  case metric
200
253
  when :unknown then "unknown"
@@ -437,6 +490,144 @@ if bootstrap_changed
437
490
  # :missing (deleted) => nothing left to size-gate, same as a deleted entrypoint
438
491
  end
439
492
 
493
+ # --- reference-file line-budget ratchet (delta-scoped) ------------------------
494
+ # Design invariants: references/attention-budget-ratchet.md. Physical lines are
495
+ # the stable proxy metric; the append-only ledger references/source-register.md
496
+ # is excluded by gate design (its growth is governed by the append-only
497
+ # contract) and the exclusion lives HERE so a candidate cannot nominate its own
498
+ # exemption.
499
+ ref_blocks = []
500
+ ref_partials = []
501
+ ref_changed = []
502
+ ref_scan_error = nil
503
+ begin
504
+ if system("git", "-C", root, "rev-parse", "--is-inside-work-tree", out: File::NULL, err: File::NULL)
505
+ rpaths = []
506
+ rpaths.concat(git_lines("diff", "--name-only", base, "HEAD", "--", "skills", root: root)) if base
507
+ rpaths.concat(git_lines("diff", "--name-only", "--", "skills", root: root))
508
+ rpaths.concat(git_lines("diff", "--cached", "--name-only", "--", "skills", root: root))
509
+ rpaths.concat(git_lines("ls-files", "--others", "--exclude-standard", "--", "skills", root: root))
510
+ ref_changed = rpaths.select { |p| p.match?(REF_PATH_RE) }.reject { |p| p.match?(REF_LEDGER_RE) }.uniq
511
+ end
512
+ rescue StandardError => e
513
+ ref_scan_error = "#{e.class.name} #{e.message.lines.first.to_s.strip}"
514
+ warn "changed_reference_line_scan_skipped: #{ref_scan_error}"
515
+ end
516
+ # A scan failure must not silently read as "no references changed" — that would
517
+ # false-green a real growth, so it fails closed as a partial.
518
+ ref_partials << "skills/*/references: changed-detection failed (#{ref_scan_error}) — line-budget check unavailable (fail-closed)" if ref_scan_error
519
+
520
+ ref_move_map = {}
521
+ begin
522
+ if system("git", "-C", root, "rev-parse", "--is-inside-work-tree", out: File::NULL, err: File::NULL)
523
+ status_args = []
524
+ status_args << ["diff", "--name-status", base, "HEAD"] if base
525
+ status_args << ["diff", "--name-status", "--cached"]
526
+ status_args << ["diff", "--name-status"]
527
+ status_args.each do |cmd|
528
+ git_lines(*cmd, "--", "skills", root: root).each do |line|
529
+ status, *paths = line.split("\t")
530
+ next unless status.start_with?("R") && paths.length >= 2
531
+ next unless status[1..].to_i >= 90
532
+ old_rel = paths[0].match?(REF_PATH_RE) ? paths[0] : nil
533
+ new_rel = paths[1].match?(REF_PATH_RE) ? paths[1] : nil
534
+ ref_move_map[new_rel] = old_rel if old_rel && new_rel
535
+ end
536
+ end
537
+ end
538
+ rescue StandardError => e
539
+ warn "reference_move_pair_scan_skipped: #{e.class.name}"
540
+ end
541
+
542
+ # The census is visibility only, so an unreadable or vanished file must degrade the
543
+ # COUNTER to unknown rather than abort the program: aborting here would skip the
544
+ # per-file fail-closed partials below and emit no verdict token at all.
545
+ head_ref_over_count = 0
546
+ head_ref_census_error = nil
547
+ begin
548
+ Dir[File.join(root, "skills", "*", "references", "**", "*.md")].each do |path|
549
+ rel = path.start_with?(root + "/") ? path[(root.length + 1)..] : path.sub(%r{\A\./}, "")
550
+ next unless rel.match?(REF_PATH_RE)
551
+ next if rel.match?(REF_LEDGER_RE)
552
+ info = begin
553
+ head_ref_info_for_path(root, rel)
554
+ rescue StandardError => e
555
+ head_ref_census_error ||= "#{rel}: #{e.class.name}"
556
+ next
557
+ end
558
+ head_ref_over_count += 1 if info.is_a?(Hash) && info[:lines] > REF_LINE_MAX
559
+ end
560
+ rescue StandardError => e
561
+ head_ref_census_error ||= e.class.name
562
+ end
563
+ if head_ref_census_error
564
+ warn "reference_line_census_partial: head over-limit census incomplete (#{head_ref_census_error}) — counter reported unknown; per-file verdicts below are unaffected"
565
+ head_ref_over_count = nil
566
+ end
567
+ base_ref_over_count = nil
568
+ if base
569
+ begin
570
+ base_ref_over_count = 0
571
+ git_lines("grep", "-c", "-e", "", base, "--", "skills", root: root).each do |line|
572
+ rest, _, cnt = line.rpartition(":")
573
+ _, _, rel = rest.partition(":")
574
+ next unless rel.match?(REF_PATH_RE)
575
+ next if rel.match?(REF_LEDGER_RE)
576
+ base_ref_over_count += 1 if cnt.to_i > REF_LINE_MAX
577
+ end
578
+ rescue StandardError => e
579
+ base_ref_over_count = nil
580
+ warn "reference_line_trend_partial: base=unknown reason=#{e.class.name}"
581
+ end
582
+ end
583
+ puts "reference_line_over_limit_count_base=#{base_ref_over_count.nil? ? "unknown" : base_ref_over_count}"
584
+ puts "reference_line_over_limit_count_head=#{head_ref_over_count.nil? ? "unknown" : head_ref_over_count}"
585
+ puts "reference_line_over_limit_count_delta=#{(base_ref_over_count.nil? || head_ref_over_count.nil?) ? "unknown" : format("%+d", head_ref_over_count - base_ref_over_count)}"
586
+
587
+ # Ledger visibility: excluded from the ratchet, still reported when over.
588
+ Dir[File.join(root, "skills", "*", "references", "source-register.md")].sort.each do |path|
589
+ rel = path.start_with?(root + "/") ? path[(root.length + 1)..] : path.sub(%r{\A\./}, "")
590
+ info = begin
591
+ head_ref_info_for_path(root, rel)
592
+ rescue StandardError
593
+ :missing
594
+ end
595
+ if info.is_a?(Hash) && info[:lines] > REF_LINE_MAX
596
+ puts "reference_line_budget_ledger_excluded: #{rel} lines=#{info[:lines]} — append-only ledger, excluded by gate design (growth governed by the append-only contract)"
597
+ end
598
+ end
599
+
600
+ ref_changed.sort.each do |rel|
601
+ begin
602
+ head_info = head_ref_info_for_path(root, rel)
603
+ rescue StandardError => e
604
+ ref_partials << "#{rel}: head lines unreadable (#{e.class.name}) — line-budget check unavailable (fail-closed)"
605
+ next
606
+ end
607
+ next if head_info == :missing # deleted reference: nothing left to gate
608
+ base_info = base_ref_info_for_path(root, base, rel)
609
+ puts "changed_reference_line_delta: #{rel} base_lines=#{fmt_ref_value(base_info)} head_lines=#{head_info[:lines]} delta_lines=#{fmt_ref_delta(base_info, head_info)}"
610
+ if base_info == :missing && head_info[:lines] > REF_NAV_MIN_LINES && head_info[:headings].zero?
611
+ warn "reference_nav_advisory: #{rel} lines=#{head_info[:lines]} headings=0 — a new reference over #{REF_NAV_MIN_LINES} lines needs ## section structure so chunked reads and greps can navigate (advisory, never blocks)"
612
+ end
613
+ next unless head_info[:lines] > REF_LINE_MAX
614
+ if base_info == :unknown
615
+ ref_partials << "#{rel}: base unknown — reference line-budget check unavailable (fail-closed)"
616
+ elsif base_info == :missing || base_info[:lines] <= REF_LINE_MAX
617
+ source_rel = ref_move_map[rel]
618
+ source_info = source_rel ? base_ref_info_for_path(root, base, source_rel) : nil
619
+ if source_info.is_a?(Hash) && source_info[:lines] > REF_LINE_MAX && head_info[:lines] <= source_info[:lines]
620
+ puts "reference_line_budget_move_ok: #{rel} head_lines=#{head_info[:lines]} allowed_lines=#{source_info[:lines]} moved_from=#{source_rel}"
621
+ else
622
+ ref_blocks << "#{rel}: new reference over line budget head_lines=#{head_info[:lines]} (> #{REF_LINE_MAX}) — split it by subtopic before landing (see references/attention-budget-ratchet.md; there is no exempt marker or waiver flag)"
623
+ end
624
+ elsif head_info[:lines] > base_info[:lines]
625
+ ref_blocks << "#{rel}: over-limit reference grew base_lines=#{base_info[:lines]} head_lines=#{head_info[:lines]} — shrink or stay level; fund additions by consolidating text in the same file"
626
+ else
627
+ puts "reference_line_budget_legacy_ok: #{rel} base_lines=#{base_info[:lines]} head_lines=#{head_info[:lines]} allowed_lines=#{base_info[:lines]}"
628
+ end
629
+ end
630
+
440
631
  # Advisory markers only when the blocking verdict is clean — a consumer reading
441
632
  # the preserved legacy token must never see ok next to a block.
442
633
  #
@@ -451,18 +642,20 @@ end
451
642
  # — but the token says un-evaluated, not ok.
452
643
  # NOTE: this comment lives inside the single-quoted `ruby -e` program; an
453
644
  # apostrophe here terminates the shell quote and breaks the script.
454
- if blocks.empty? && partials.empty? && word_blocks.empty? && word_partials.empty?
645
+ if blocks.empty? && partials.empty? && word_blocks.empty? && word_partials.empty? && ref_blocks.empty? && ref_partials.empty?
455
646
  puts "size_budget_advisory_#{size_state}"
456
647
  puts "entrypoint_size_budget_advisory_ok"
457
648
  if base.nil?
458
649
  puts "entrypoint_size_blocking_unevaluated: base=unknown — committed changes were NOT delta-checked; this is not a pass"
459
650
  puts "entrypoint_word_budget_blocking_unevaluated: base=unknown — committed changes were NOT delta-checked; this is not a pass"
651
+ puts "reference_line_budget_blocking_unevaluated: base=unknown — committed changes were NOT delta-checked; this is not a pass"
460
652
  if File.file?(File.join(root, "agent-context/session-start.md"))
461
653
  puts "bootstrap_size_delta_unevaluated: base=unknown — agent-context/session-start.md committed changes were NOT delta-checked; this is not a pass"
462
654
  end
463
655
  else
464
656
  puts "entrypoint_size_blocking_ok"
465
657
  puts "entrypoint_word_budget_blocking_ok"
658
+ puts "reference_line_budget_blocking_ok"
466
659
  end
467
660
  exit 0
468
661
  end
@@ -470,7 +663,10 @@ blocks.each { |b| warn "entrypoint_size_block: #{b}" }
470
663
  partials.each { |b| warn "entrypoint_size_block_partial: #{b}" }
471
664
  word_blocks.each { |b| warn "entrypoint_word_budget_block: #{b}" }
472
665
  word_partials.each { |b| warn "entrypoint_word_budget_block_partial: #{b}" }
666
+ ref_blocks.each { |b| warn "reference_line_block: #{b}" }
667
+ ref_partials.each { |b| warn "reference_line_block_partial: #{b}" }
473
668
  puts "entrypoint_word_budget_blocking_failed" unless word_blocks.empty? && word_partials.empty?
669
+ puts "reference_line_budget_blocking_failed" unless ref_blocks.empty? && ref_partials.empty?
474
670
  # Legacy aggregate token for every blocking verdict owned by this size-budget
475
671
  # script, including the body-word rule. Keep it unconditional so existing
476
672
  # consumers cannot miss a new word-only failure.
@@ -0,0 +1,15 @@
1
+ # Contract-anchor table for check-contract-anchors.sh (same directory).
2
+ # One row per pinned load-bearing contract literal:
3
+ # id <TAB> repo-relative path <TAB> pinned literal (exactly-once, >=16 chars) <TAB> note
4
+ # The note names the sanitized provenance label and why the wording is load-bearing.
5
+ # To change pinned wording intentionally: edit the contract sentence AND this row
6
+ # in the same MR. Keep this set small — load-bearing contracts only, never style.
7
+ verdict-taxonomy-discriminator skills/testing-strategy/references/ci-fixtures-and-flake-control.md One discriminating predicate decides the verdict 071-chainA-r1f1: fault-origin discriminator sentence; deleting it reverts verdicts to case-by-case judgment
8
+ verdict-taxonomy-false-green skills/testing-strategy/references/ci-fixtures-and-flake-control.md excludes `infra-error` cases to present a clean total is a false-green report 071-chainA-r1f1: report-completeness clause; deleting it legalizes infra-error-excluding clean totals
9
+ stop-predicate-materially-differing skills/product-rd-workflow/SKILL.md materially differing viable approaches (none dominant-and-reversible) 071-chainB-r1f1: stop-condition predicate wording; inverting it flips blocked/continue classification
10
+ stop-predicate-evidenced-cause skills/product-rd-workflow/SKILL.md a fix lacking evidenced cause 071-chainB-r1f1: stop-condition predicate wording; weakening it admits plausible-but-unevidenced fixes
11
+ burn-rate-tiers-entry skills/platform-observability/SKILL.md page 14.4× 1h/5m, page 6× 6h/30m, ticket 1× 3d/6h 071-chainC-r1f4: SRE Workbook multiwindow tiers, externally verified value (specs/071 source-verification); silent renumbering must not pass
12
+ burn-rate-page-row-reference skills/platform-observability/references/sli-slo-design.md | Page (P0) | 1h | 5m | 14.4 | 071-chainC-r1f4: reference-side copy of the page tier; pinning both sides makes entry/reference drift visible
13
+ coverage-tier-provenance skills/testing-strategy/references/test-code-authoring-patterns.md 60% acceptable / 75% commendable / 90% exemplary 分档 071-chainC-r1f4: externally verified coverage tiers (specs/071 source-verification)
14
+ pairwise-trigger-range skills/test-artifact-management/references/classical-test-design-techniques.md 2-way 累计触发 53–97% 071-chainC-r1f4: NIST SP 800-142 empirical range, externally verified (specs/071 source-verification)
15
+ bva-two-vs-three-value skills/test-artifact-management/references/classical-test-design-techniques.md 2-value(边界 + 下一格)和 3-value(边界 + 两侧) 071-chainC-r1f4: ISTQB v4 BVA variant definitions, externally verified (specs/071 source-verification)