@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.
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/references/mobile-quality-release.md +5 -0
- 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 +5 -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/references/state-machine-task-patterns.md +2 -0
- 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/references/contracts-and-state.md +5 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/references/async-lifecycle-and-performance.md +1 -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 +12 -12
- 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/rd-standards-doc-family-checklist.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/design-system-source-of-truth.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/platform-mobile-patterns.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/tokens-and-components.md +1 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-ui-ux-design/references/ui-ux-audit.md +8 -0
- 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/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 +12 -15
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/attention-budget-ratchet.md +37 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/description-authoring.md +13 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +37 -30
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md +24 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/extraction-quickstart.md +5 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/rule-consolidation.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +81 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +12 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/validation-and-landing.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +30 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-contract-anchors.sh +126 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-size-budget.sh +197 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/contract-anchors.tsv +15 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-routing-bank.rb +210 -36
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/extraction_review_gate.sh +3 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/gate_receipt.py +576 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_antipattern_grep_panel.sh +80 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_body_compliance_grading.sh +99 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +25 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_size_budget.sh +251 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_contract_anchors.sh +196 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_eval_routing_bank_grader_diagnostics.sh +222 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_extraction_review_gate.sh +16 -10
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_frozen_case_sanctity.sh +178 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_frozen_case_sanctity_selfproof.sh +108 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_gate_receipt.sh +431 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_pinned_phrase_mutation_walk.sh +151 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_routing_bank_integrity.sh +86 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +27 -21
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +25 -15
- 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 +9 -9
- 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/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 +1 -0
- 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 +127 -67
- 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)
|