@ccoalm/ccl-skills 0.6.2 → 0.7.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 (62) hide show
  1. package/dist/assets/marketplace/plugins/ccl-skills/hooks/hooks.json +11 -0
  2. package/dist/assets/marketplace/plugins/ccl-skills/hooks/remind-unverified-cli-flag.sh +309 -0
  3. package/dist/assets/marketplace/plugins/ccl-skills/hooks/test_remind_unverified_cli_flag.sh +483 -0
  4. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +5 -0
  5. package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +2 -1
  6. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/SKILL.md +1 -1
  7. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/architecture-playbook.md +2 -0
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/data-platform-architecture.md +1 -1
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/event-driven-architecture.md +14 -11
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-architecture/references/multi-tenant-isolation.md +2 -2
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +1 -0
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +2 -1
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +1 -1
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/sli-slo-design.md +25 -9
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/source-register.md +1 -0
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/SKILL.md +1 -1
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/promotion-gate-and-review.md +16 -0
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/references/secret-and-config-management.md +7 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/retry-timeout-circuit-breaker.md +11 -0
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/SKILL.md +5 -1
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/architecture-playbook.md +1 -1
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/audit-history-architecture.md +31 -0
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/data-platform-architecture.md +1 -1
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/event-driven-architecture.md +7 -4
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/multi-tenant-isolation.md +2 -2
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/notification-architecture.md +28 -0
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/packaging-runtime-readiness.md +1 -1
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/replay-comparison-architecture.md +28 -0
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-architecture/references/workflow-state-architecture.md +39 -0
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +6 -6
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/ai-service-wiring-patterns.md +8 -0
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/audit-history-patterns.md +29 -0
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/background-job-patterns.md +16 -0
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/batch-and-artifact-patterns.md +25 -1
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/notification-patterns.md +40 -0
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/public-api-security-patterns.md +1 -1
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/replay-comparison-patterns.md +30 -0
  38. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/state-machine-task-patterns.md +48 -0
  39. package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/references/testing-and-quality-patterns.md +10 -1
  40. package/dist/assets/marketplace/plugins/ccl-skills/skills/release-coordination/SKILL.md +2 -0
  41. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/coverage-exhaustion-traps.md +45 -0
  42. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +48 -0
  43. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +21 -2
  44. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/firing-point-placement.md +8 -0
  45. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/parallel-stack-references-pattern.md +5 -4
  46. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +15 -0
  47. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-to-skill-extraction.md +2 -0
  48. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-ccl-skills.sh +24 -0
  49. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/check-parallel-stack-parity.sh +119 -0
  50. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_parallel_stack_parity.sh +183 -0
  51. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +2 -0
  52. package/dist/assets/marketplace/plugins/ccl-skills/skills/terminal-cli-dev/SKILL.md +1 -0
  53. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +3 -4
  54. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/fitness-functions.md +16 -0
  55. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/scenario-testing.md +1 -1
  56. package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/references/test-code-authoring-patterns.md +16 -5
  57. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/SKILL.md +5 -3
  58. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/delivery-face-closeout.md +16 -6
  59. package/dist/assets/marketplace/plugins/ccl-skills/skills/tighten-doc/references/self-benchmark-baseline.md +37 -0
  60. package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +1 -0
  61. package/dist/assets/release.json +115 -50
  62. package/package.json +1 -1
@@ -0,0 +1,483 @@
1
+ #!/usr/bin/env bash
2
+ # Deterministic behavior suite for hooks/remind-unverified-cli-flag.sh.
3
+ # Registered in the Makefile `test` target; requires jq (the hook's dependency —
4
+ # without jq the hook degrades to silence, so this suite requires it and fails
5
+ # loudly instead of false-greening).
6
+ #
7
+ # The hook deliberately does NOT parse shell, so this suite does not probe shell
8
+ # forms. It probes the three things the hook actually asserts — tool-word match,
9
+ # long-flag presence, per-session dedup — plus the two contracts it must never
10
+ # break (exit 0 / valid JSON / silent stderr) and the marker-safety guards. The
11
+ # documented FALSE FIRES are asserted too, so they stay intentional rather than
12
+ # drifting into accidents.
13
+ set -u
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
16
+ HOOK="${CLI_FLAG_HOOK:-$SCRIPT_DIR/remind-unverified-cli-flag.sh}"
17
+ [ -f "$HOOK" ] || { echo "FAIL: hook not found: $HOOK" >&2; exit 1; }
18
+ command -v jq >/dev/null 2>&1 || { echo "FAIL: jq required for this suite" >&2; exit 1; }
19
+ # Fail LOUDLY on a hook syntax error rather than as a wall of mysterious probe
20
+ # failures — one stray apostrophe inside an embedded program did exactly that.
21
+ bash -n "$HOOK" 2>/dev/null || { echo "FAIL: hook is not syntactically valid" >&2; bash -n "$HOOK"; exit 1; }
22
+
23
+ pass=0; fail=0; probe_n=0
24
+
25
+ # Isolate the hook's dedup markers into a per-RUN directory. Without this the
26
+ # suite self-poisoned across runs: sessions derive from `$$`, the hook persists
27
+ # markers, and after PID reuse a stale one silently turned an expected `remind`
28
+ # into `quiet`.
29
+ MARKER_DIR=$(mktemp -d "${TMPDIR:-/tmp}/cliflag-suite-XXXXXX") || {
30
+ echo "FAIL: cannot create isolated marker dir" >&2; exit 1; }
31
+ trap 'rm -rf "$MARKER_DIR"' EXIT INT TERM
32
+ export TMPDIR="$MARKER_DIR"
33
+
34
+ # The hook's core promise is that it never disturbs the Bash call: exit 0, valid
35
+ # JSON, silent stderr. All three were discarded by an earlier version of this
36
+ # suite, so a nonzero exit or a stray diagnostic passed every assertion.
37
+ contract_check() {
38
+ local out="$1" rc="$2" label="$3" errbytes
39
+ if [ -n "${PROBE_ERR:-}" ] && [ -f "$PROBE_ERR" ]; then
40
+ errbytes=$(wc -c < "$PROBE_ERR" | tr -d ' ')
41
+ if [ "$errbytes" != "0" ]; then
42
+ fail=$((fail+1)); printf 'FAIL [wrote %s bytes to stderr] %s\n' "$errbytes" "$label" >&2
43
+ printf ' %s\n' "$(head -1 "$PROBE_ERR")" >&2; return
44
+ fi
45
+ fi
46
+ if [ "$rc" -ne 0 ]; then
47
+ fail=$((fail+1)); printf 'FAIL [exit=%s, must be 0] %s\n' "$rc" "$label" >&2; return
48
+ fi
49
+ if [ -n "$out" ] && ! printf '%s' "$out" | jq -e . >/dev/null 2>&1; then
50
+ fail=$((fail+1)); printf 'FAIL [output is not valid JSON] %s\n' "$label" >&2; return
51
+ fi
52
+ pass=$((pass+1))
53
+ }
54
+
55
+ run_hook() { # run_hook <command> <session>; sets HOOK_OUT / HOOK_RC
56
+ PROBE_ERR=$(mktemp "${MARKER_DIR}/err-XXXXXX")
57
+ HOOK_OUT=$(jq -nc --arg c "$1" --arg s "$2" \
58
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' | bash "$HOOK" 2>"$PROBE_ERR"); HOOK_RC=$?
59
+ contract_check "$HOOK_OUT" "$HOOK_RC" "$1"
60
+ rm -f "$PROBE_ERR"; PROBE_ERR=""
61
+ }
62
+
63
+ # probe <remind|quiet> <command> [session]
64
+ probe() {
65
+ local expect="$1" cmd="$2" sess="${3:-}" got
66
+ probe_n=$((probe_n+1)); [ -n "$sess" ] || sess="probe-$$-$probe_n"
67
+ run_hook "$cmd" "$sess"
68
+ got="quiet"
69
+ # Assert the STRUCTURE the PreToolUse consumer reads, not just the key name: a
70
+ # top-level `additionalContext` is valid JSON the host ignores.
71
+ printf '%s' "$HOOK_OUT" | jq -e \
72
+ '.hookSpecificOutput.hookEventName == "PreToolUse"
73
+ and (.hookSpecificOutput.additionalContext | type) == "string"
74
+ and (.hookSpecificOutput.additionalContext | length) > 0' >/dev/null 2>&1 && got="remind"
75
+ if [ "$got" = "$expect" ]; then pass=$((pass+1))
76
+ else fail=$((fail+1)); printf 'FAIL [want=%s got=%s] %s\n' "$expect" "$got" "$cmd" >&2; fi
77
+ }
78
+
79
+ # attrib <expected tool> <command> — which tool the advisory names.
80
+ attrib() {
81
+ local want="$1" cmd="$2" got
82
+ probe_n=$((probe_n+1))
83
+ run_hook "$cmd" "attrib-$$-$probe_n"
84
+ got=$(printf '%s' "$HOOK_OUT" | jq -r '.hookSpecificOutput.additionalContext // ""' 2>/dev/null \
85
+ | grep -o '`[^`]*`' | head -1 | tr -d '`')
86
+ if [ "$got" = "$want" ]; then pass=$((pass+1))
87
+ else fail=$((fail+1)); printf 'FAIL [want-attrib=%s got=%s] %s\n' "$want" "${got:-<none>}" "$cmd" >&2; fi
88
+ }
89
+
90
+ # --- FIRES: the recorded failure shapes this hook exists to catch ---
91
+ probe remind 'glab mr list --state opened'
92
+ probe remind 'glab ci list --branch dev'
93
+ probe remind 'glab pipeline view 21852 --output json'
94
+ probe remind 'gh pr list --state open'
95
+ probe remind 'lark-cli schema tbl --jq .fields'
96
+ probe remind 'uv publish --check-url https://example.invalid/simple'
97
+ attrib 'glab' 'glab mr list --state opened'
98
+ attrib 'uv' 'uv publish --check-url https://example.invalid/simple'
99
+ # No shell parsing means every spelling of the same invocation behaves alike —
100
+ # this is the property the fifteen-round parser was failing to achieve.
101
+ probe remind 'cd /some/path && glab mr list --state merged'
102
+ probe remind 'GITLAB_HOST=x glab mr list --state opened'
103
+ probe remind 'env GITLAB_HOST=x glab mr list --state opened'
104
+ probe remind 'command glab mr list --state opened'
105
+ probe remind 'gh --repo=owner/repo pr list --state open'
106
+ probe remind 'gh -R owner/repo pr list --state open'
107
+ probe remind 'glab mr list \
108
+ --state opened'
109
+ probe remind 'git --help && glab mr list --state opened'
110
+
111
+ # --- QUIET: no long flag means nothing to guess ---
112
+ probe quiet 'glab mr view 123'
113
+ probe quiet 'gh pr list'
114
+ probe quiet 'uv sync'
115
+ # --- QUIET: tools outside the list are a documented NON-fire ---
116
+ probe quiet 'git log --oneline --graph'
117
+ probe quiet 'kubectl get pods --all-namespaces'
118
+ probe quiet 'npm install --save-dev typescript'
119
+ # --- QUIET: the tool name must appear as a WORD ---
120
+ probe quiet 'echo glabber --state opened'
121
+ probe quiet './myglab --state opened'
122
+ # --- QUIET: whitespace-bearing quoted prose is masked ---
123
+ probe quiet 'git commit -m "note that glab mr list --state is unsupported"'
124
+ probe quiet 'git log --grep="glab mr list --state"'
125
+ # --- QUIET: a help request whose FIRST WORD is the tool ---
126
+ probe quiet 'glab mr list --help'
127
+ probe quiet 'glab mr list -h'
128
+ probe quiet 'glab help mr list --json'
129
+ # A bare `help` counts only in the SUBCOMMAND slot: as a positional argument it
130
+ # is an endpoint name, not a help request, and suppressing there swallowed the
131
+ # advisory for a genuine flag.
132
+ probe remind 'gh api help --paginate'
133
+ probe quiet 'gh pr merge --help'
134
+ # A standalone `--` hands the rest of the line to another program, so a `--help`
135
+ # after it is that program's, not this tool's — the flag before it still needs
136
+ # the advisory.
137
+ probe remind 'uv run --python 3.12 -- python --help'
138
+ # ...but suppression must NOT extend to a compound command: the first word is
139
+ # still the tool while the unrelated `--help` belongs to something else, and the
140
+ # whole-command grep swallowed the advisory for a genuine invocation.
141
+ probe remind 'glab mr list --state opened && git --help'
142
+ probe remind 'glab mr list --state opened; gh pr list --help'
143
+ # A NEWLINE separates commands exactly as `;` does, and grep is line-oriented so
144
+ # a character class can never see one — a two-line command read as "simple" and
145
+ # the second line's `--help` suppressed the advisory for the first line.
146
+ probe remind 'glab mr list --state opened
147
+ git --help'
148
+
149
+ # --- DOCUMENTED FALSE FIRES (asserted so they stay intentional) ---
150
+ # A mention inside a heredoc body fires once for that session. Accepted: chasing
151
+ # it is what produced the fifteen-round parser.
152
+ probe remind 'cat <<EOF
153
+ glab mr list --state opened
154
+ EOF'
155
+ # A help request that is not the command's first word still fires.
156
+ probe remind 'cd /x && glab mr list --help'
157
+ # A payload inside a whitespace-bearing quoted string is masked away and stays
158
+ # QUIET. The mask cannot separate a launcher payload from a commit message
159
+ # without parsing, which is exactly what this hook refuses to do; asserted so the
160
+ # trade-off stays deliberate rather than drifting into an accident.
161
+ probe quiet "sh -c 'glab mr list --state opened'"
162
+ probe quiet 'bash -c "glab mr list --state opened"'
163
+
164
+ # --- DEDUP: once per (session, tool); a different tool still fires ---
165
+ d="dedup-$$"
166
+ probe remind 'glab mr list --state opened' "$d"
167
+ probe quiet 'glab ci list --branch dev' "$d"
168
+ probe remind 'gh pr list --state open' "$d"
169
+ probe remind 'glab mr list --state opened' "${d}-other"
170
+ # A tool that always TRAILS another in the same command must still get its own
171
+ # advisory: taking only the first tool word meant `gh` stayed silent for the
172
+ # whole session once `glab` was deduped.
173
+ m="mask-$$"
174
+ probe remind 'glab mr list --state opened' "$m"
175
+ attrib_in_session() { # attrib_in_session <expected tool> <cmd> <session>
176
+ probe_n=$((probe_n+1)); run_hook "$2" "$3"
177
+ local got
178
+ got=$(printf '%s' "$HOOK_OUT" | jq -r '.hookSpecificOutput.additionalContext // ""' 2>/dev/null \
179
+ | grep -o '`[^`]*`' | head -1 | tr -d '`')
180
+ if [ "$got" = "$1" ]; then pass=$((pass+1))
181
+ else fail=$((fail+1)); printf 'FAIL [want-attrib=%s got=%s] %s\n' "$1" "${got:-<none>}" "$2" >&2; fi
182
+ }
183
+ attrib_in_session 'gh' 'glab mr list --state opened && gh pr list --state open' "$m"
184
+ probe quiet 'glab mr list --state opened && gh pr list --state open' "$m"
185
+
186
+ # --- UNSAFE TEMP ROOT: the marker TOCTOU cannot be closed from bash, so where
187
+ # the temp root is group/world-writable and NOT sticky the hook must decline
188
+ # to dedup rather than pretend the window is shut. Sticky is what makes a
189
+ # shared /tmp safe; a per-user temp root is not other-writable at all, so
190
+ # neither normal case is affected. ---
191
+ unsafe_root=$(mktemp -d "${MARKER_DIR}/unsafe-XXXXXX")
192
+ chmod 777 "$unsafe_root"; chmod -t "$unsafe_root" 2>/dev/null || true
193
+ u_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
194
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'unsafe-sess' \
195
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$u_in"
196
+ u1=$(TMPDIR="$unsafe_root" bash "$HOOK" < "$u_in" 2>/dev/null)
197
+ u2=$(TMPDIR="$unsafe_root" bash "$HOOK" < "$u_in" 2>/dev/null)
198
+ rm -f "$u_in"
199
+ if printf '%s' "$u1" | grep -q additionalContext && printf '%s' "$u2" | grep -q additionalContext; then
200
+ pass=$((pass+1))
201
+ else
202
+ fail=$((fail+1)); printf 'FAIL [unsafe temp root: dedup was applied instead of skipped]\n' >&2
203
+ fi
204
+ if [ ! -e "$unsafe_root/ccl-skills-cliflag" ]; then pass=$((pass+1))
205
+ else fail=$((fail+1)); printf 'FAIL [unsafe temp root: marker directory was created]\n' >&2; fi
206
+
207
+ # Sticky exempts a writable directory only when we or root own it, so a
208
+ # self-owned sticky temp root must STILL dedup (the common shared-/tmp shape).
209
+ own_sticky=$(mktemp -d "${MARKER_DIR}/own-XXXXXX"); chmod 1777 "$own_sticky"
210
+ o_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
211
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'own-sess' \
212
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$o_in"
213
+ o1=$(TMPDIR="$own_sticky" bash "$HOOK" < "$o_in" 2>/dev/null)
214
+ o2=$(TMPDIR="$own_sticky" bash "$HOOK" < "$o_in" 2>/dev/null)
215
+ rm -f "$o_in"
216
+ if printf '%s' "$o1" | grep -q additionalContext && ! printf '%s' "$o2" | grep -q additionalContext; then
217
+ pass=$((pass+1))
218
+ else
219
+ fail=$((fail+1)); printf 'FAIL [self-owned sticky temp root: dedup was skipped]\n' >&2
220
+ fi
221
+
222
+ # The walk must use the PHYSICAL path: `dirname` on a logical path resolves
223
+ # neither symlinks nor `..`, so a TMPDIR that merely POINTS at a hostile-ancestor
224
+ # directory walked a chain that does not exist on disk.
225
+ sym_parent=$(mktemp -d "${MARKER_DIR}/sym-XXXXXX")
226
+ chmod 777 "$sym_parent"; chmod -t "$sym_parent" 2>/dev/null || true
227
+ sym_real="$sym_parent/inner"; mkdir -p "$sym_real"; chmod 700 "$sym_real"
228
+ sym_link="${MARKER_DIR}/symlink-root-$$"; ln -s "$sym_real" "$sym_link"
229
+ s_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
230
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'sym-sess' \
231
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$s_in"
232
+ s1=$(TMPDIR="$sym_link" bash "$HOOK" < "$s_in" 2>/dev/null)
233
+ s2=$(TMPDIR="$sym_link" bash "$HOOK" < "$s_in" 2>/dev/null)
234
+ rm -f "$s_in"
235
+ if printf '%s' "$s1" | grep -q additionalContext && printf '%s' "$s2" | grep -q additionalContext; then
236
+ pass=$((pass+1))
237
+ else
238
+ fail=$((fail+1)); printf 'FAIL [symlinked temp root to hostile ancestor: dedup was applied]\n' >&2
239
+ fi
240
+
241
+ # A mode-0700 temp root under a world-writable NON-STICKY parent is swappable
242
+ # wholesale, so checking only the temp root itself proves nothing about the path.
243
+ anc_parent=$(mktemp -d "${MARKER_DIR}/anc-XXXXXX")
244
+ chmod 777 "$anc_parent"; chmod -t "$anc_parent" 2>/dev/null || true
245
+ anc_root="$anc_parent/inner"; mkdir -p "$anc_root"; chmod 700 "$anc_root"
246
+ a_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
247
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'anc-sess' \
248
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$a_in"
249
+ a1=$(TMPDIR="$anc_root" bash "$HOOK" < "$a_in" 2>/dev/null)
250
+ a2=$(TMPDIR="$anc_root" bash "$HOOK" < "$a_in" 2>/dev/null)
251
+ rm -f "$a_in"
252
+ if printf '%s' "$a1" | grep -q additionalContext && printf '%s' "$a2" | grep -q additionalContext \
253
+ && [ ! -e "$anc_root/ccl-skills-cliflag" ]; then
254
+ pass=$((pass+1))
255
+ else
256
+ fail=$((fail+1)); printf 'FAIL [unsafe ANCESTOR of temp root: dedup was applied]\n' >&2
257
+ fi
258
+
259
+ # --- BOUNDED INPUT: past the 1 MiB read cap the payload is ignored entirely.
260
+ # Deterministic, not a timing bound: a timing probe could not fail at a
261
+ # CI-safe size, so it was removed rather than shipped. ---
262
+ big_in=$(mktemp "${MARKER_DIR}/big-XXXXXX")
263
+ awk 'BEGIN{printf "{\"tool_input\":{\"command\":\"glab mr list --state opened "; for(i=0;i<2200000;i++)printf "x"; printf "\"},\"session_id\":\"bigpay\",\"cwd\":\"/tmp\"}"}' > "$big_in"
264
+ big_out=$(bash "$HOOK" < "$big_in" 2>/dev/null); big_rc=$?
265
+ if [ "$big_rc" -ne 0 ]; then
266
+ fail=$((fail+1)); printf 'FAIL [oversized payload: rc=%s]\n' "$big_rc" >&2
267
+ elif printf '%s' "$big_out" | grep -q additionalContext; then
268
+ fail=$((fail+1)); printf 'FAIL [oversized payload parsed past the 1MiB read cap]\n' >&2
269
+ else pass=$((pass+1)); fi
270
+ rm -f "$big_in"
271
+
272
+ # --- MARKER SAFETY. Every attack here was reproduced first-hand before its fix.
273
+ # Each plant targets BOTH the current directory layout AND the pre-hardening
274
+ # flat path: planting only at the current path made this whole group vacuous
275
+ # (a mutation reverting the layout passed all of it). ---
276
+ FLAT='ccl-skills-cliflag-hostile-sess'
277
+ DIRP='ccl-skills-cliflag'
278
+
279
+ run_bounded() { # run_bounded <seconds> <input-file> <cmd...>
280
+ # `timeout` is NOT installed on stock macOS; calling it there returns 127 and
281
+ # would fail every probe here on a Darwin runner with a correct hook.
282
+ local limit="$1" infile="$2"; shift 2
283
+ local outfile pid waited=0
284
+ outfile=$(mktemp "${MARKER_DIR}/bounded-XXXXXX")
285
+ "$@" <"$infile" >"$outfile" 2>/dev/null & pid=$!
286
+ while kill -0 "$pid" 2>/dev/null && [ "$waited" -lt "$limit" ]; do sleep 1; waited=$((waited+1)); done
287
+ if kill -0 "$pid" 2>/dev/null; then kill -9 "$pid" 2>/dev/null; wait "$pid" 2>/dev/null; BOUNDED_RC=124
288
+ else wait "$pid"; BOUNDED_RC=$?; fi
289
+ BOUNDED_OUT=$(cat "$outfile"); rm -f "$outfile"
290
+ }
291
+
292
+ hostile() { # hostile <label> <plant-fn>; publishes HOSTILE_SANDBOX
293
+ local label="$1" plant="$2" sandbox infile start elapsed
294
+ sandbox=$(mktemp -d "${MARKER_DIR}/hostile-XXXXXX") || { fail=$((fail+1)); return; }
295
+ "$plant" "$sandbox"
296
+ infile=$(mktemp "${MARKER_DIR}/in-XXXXXX")
297
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'hostile-sess' \
298
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$infile"
299
+ start=$(date +%s)
300
+ TMPDIR="$sandbox" run_bounded 8 "$infile" bash "$HOOK"
301
+ elapsed=$(( $(date +%s) - start )); rm -f "$infile"
302
+ if [ "$BOUNDED_RC" -ne 0 ] || [ "$elapsed" -ge 5 ]; then
303
+ fail=$((fail+1)); printf 'FAIL [hostile %s: rc=%s elapsed=%ss]\n' "$label" "$BOUNDED_RC" "$elapsed" >&2
304
+ elif ! printf '%s' "$BOUNDED_OUT" | grep -q 'additionalContext'; then
305
+ fail=$((fail+1)); printf 'FAIL [hostile %s: advisory suppressed]\n' "$label" >&2
306
+ else pass=$((pass+1)); fi
307
+ HOSTILE_SANDBOX="$sandbox"
308
+ }
309
+
310
+ victim_intact() { # victim_intact <label>
311
+ if [ -f "$HOSTILE_SANDBOX/victim.txt" ] \
312
+ && [ "$(cat "$HOSTILE_SANDBOX/victim.txt")" = 'original content' ]; then
313
+ pass=$((pass+1))
314
+ else
315
+ fail=$((fail+1)); printf 'FAIL [hostile %s: victim file was written through]\n' "$1" >&2
316
+ fi
317
+ }
318
+
319
+ plant_symlink() { echo 'original content' > "$1/victim.txt"
320
+ ln -s "$1/victim.txt" "$1/$DIRP"; ln -s "$1/victim.txt" "$1/$FLAT"; }
321
+ plant_fifo() { mkfifo "$1/$DIRP" 2>/dev/null || true; mkfifo "$1/$FLAT" 2>/dev/null || true; }
322
+ # NOT an ownership probe: it creates the directory as the SAME uid, so `-O`
323
+ # stays true and that rejection is never reached. What it covers is a
324
+ # PRE-EXISTING marker directory being reused without incident, plus the flat
325
+ # hostile object. Cross-uid `-O` has no executable probe here.
326
+ plant_preexisting(){ mkdir -p "$1/$DIRP"; chmod 700 "$1/$DIRP"; mkfifo "$1/$FLAT" 2>/dev/null || true; }
327
+ plant_inner_fifo() { mkdir -p -m 700 "$1/$DIRP"; mkfifo "$1/$DIRP/hostile-sess" 2>/dev/null || true
328
+ mkfifo "$1/$FLAT" 2>/dev/null || true; }
329
+ plant_inner_link() { mkdir -p -m 700 "$1/$DIRP"; echo 'original content' > "$1/victim.txt"
330
+ ln -s "$1/victim.txt" "$1/$DIRP/hostile-sess"; ln -s "$1/victim.txt" "$1/$FLAT"; }
331
+ # A HARD LINK passes -f, -O and the symlink test alike; reachable when the marker
332
+ # directory pre-existed group/world-writable.
333
+ plant_inner_hard() { mkdir -p "$1/$DIRP"; chmod 777 "$1/$DIRP"
334
+ echo 'original content' > "$1/victim.txt"
335
+ ln "$1/victim.txt" "$1/$DIRP/hostile-sess"
336
+ ln "$1/victim.txt" "$1/$FLAT" 2>/dev/null || true; }
337
+
338
+ hostile 'symlink at marker dir' plant_symlink; victim_intact 'symlink at marker dir'
339
+ hostile 'FIFO at marker dir' plant_fifo
340
+ hostile 'pre-existing marker dir' plant_preexisting
341
+ hostile 'FIFO as marker file' plant_inner_fifo
342
+ hostile 'symlink as marker file' plant_inner_link; victim_intact 'symlink as marker file'
343
+ hostile 'hard link as marker file' plant_inner_hard; victim_intact 'hard link as marker file'
344
+
345
+ # A pre-existing world-writable marker directory must be TIGHTENED, not merely
346
+ # accepted: `mkdir -p -m 700` does not touch an existing directory's mode.
347
+ mode_sb=$(mktemp -d "${MARKER_DIR}/mode-XXXXXX")
348
+ mkdir -p "$mode_sb/$DIRP"; chmod 777 "$mode_sb/$DIRP"
349
+ mode_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
350
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'mode-sess' \
351
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$mode_in"
352
+ TMPDIR="$mode_sb" run_bounded 8 "$mode_in" bash "$HOOK"; rm -f "$mode_in"
353
+ if [ "$BOUNDED_RC" -eq 0 ] && [ "$(ls -ld "$mode_sb/$DIRP" | cut -c1-10)" = 'drwx------' ]; then
354
+ pass=$((pass+1))
355
+ else
356
+ fail=$((fail+1)); printf 'FAIL [world-writable marker dir not tightened]\n' >&2
357
+ fi
358
+
359
+ # An OVERSIZED marker must be neither read nor written, in BOTH shapes. The size
360
+ # cap was once removed on the argument that the line-count bound already covered
361
+ # this — an argument checked only against the many-line shape, where it holds. A
362
+ # single enormous LINE has a line count of 1, sails past that bound, and gets
363
+ # appended to. Probing one dimension and concluding over both is the mistake;
364
+ # both shapes are asserted here so the removal cannot recur on the same reasoning.
365
+ big_sb=$(mktemp -d "${MARKER_DIR}/bigm-XXXXXX")
366
+ mkdir -p -m 700 "$big_sb/ccl-skills-cliflag"
367
+ awk 'BEGIN{for(i=0;i<5000;i++)print "junk"}' > "$big_sb/ccl-skills-cliflag/big-sess"
368
+ # ...and the single-enormous-line shape, which the line-count bound does not see.
369
+ awk 'BEGIN{for(i=0;i<300000;i++)printf "x"; print ""}' > "$big_sb/ccl-skills-cliflag/one-sess"
370
+ b_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
371
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'big-sess' \
372
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$b_in"
373
+ b1=$(TMPDIR="$big_sb" bash "$HOOK" < "$b_in" 2>/dev/null)
374
+ b2=$(TMPDIR="$big_sb" bash "$HOOK" < "$b_in" 2>/dev/null)
375
+ rm -f "$b_in"
376
+ if printf '%s' "$b1" | grep -q additionalContext && printf '%s' "$b2" | grep -q additionalContext \
377
+ && [ "$(wc -l < "$big_sb/ccl-skills-cliflag/big-sess" | tr -d ' ')" = "5000" ]; then
378
+ pass=$((pass+1))
379
+ else
380
+ fail=$((fail+1)); printf 'FAIL [oversized many-line marker: appended to or dedup applied]\n' >&2
381
+ fi
382
+ o_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
383
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'one-sess' \
384
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$o_in"
385
+ o1=$(TMPDIR="$big_sb" bash "$HOOK" < "$o_in" 2>/dev/null)
386
+ rm -f "$o_in"
387
+ if printf '%s' "$o1" | grep -q additionalContext \
388
+ && [ "$(wc -l < "$big_sb/ccl-skills-cliflag/one-sess" | tr -d ' ')" = "1" ]; then
389
+ pass=$((pass+1))
390
+ else
391
+ fail=$((fail+1)); printf 'FAIL [oversized single-line marker: was appended to]\n' >&2
392
+ fi
393
+
394
+ # An EMPTY marker: `grep -c ''` prints 0 but exits 1, which produced "0\n0" and a
395
+ # stderr diagnostic from the numeric test.
396
+ empty_sb=$(mktemp -d "${MARKER_DIR}/empty-XXXXXX")
397
+ mkdir -p -m 700 "$empty_sb/$DIRP"; : > "$empty_sb/$DIRP/probe-empty"
398
+ PROBE_ERR=$(mktemp "${MARKER_DIR}/err-XXXXXX")
399
+ e_out=$(TMPDIR="$empty_sb" jq -nc --arg c 'glab mr list --state opened' --arg s 'probe-empty' \
400
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' \
401
+ | TMPDIR="$empty_sb" bash "$HOOK" 2>"$PROBE_ERR"); e_rc=$?
402
+ contract_check "$e_out" "$e_rc" 'empty marker file'
403
+ rm -f "$PROBE_ERR"; PROBE_ERR=""
404
+
405
+ # A marker that is owned and single-link but NOT WRITABLE: the append is
406
+ # attempted and must fail SILENTLY. This is the probe that covers the subshell —
407
+ # an explicit `-w` check used to short-circuit here, which is why removing the
408
+ # subshell used to change nothing observable.
409
+ # Skipped as root: `chmod 400` does not stop uid 0 from appending, so under a
410
+ # container CI running as root this probe would pass without exercising anything.
411
+ # Recording the skip beats a vacuous pass.
412
+ if [ "$(id -u)" = "0" ]; then
413
+ printf 'SKIP [read-only marker probe: running as root, chmod 400 is not a barrier]\n' >&2
414
+ else
415
+ ro_sb=$(mktemp -d "${MARKER_DIR}/ro-XXXXXX")
416
+ mkdir -p -m 700 "$ro_sb/$DIRP"; : > "$ro_sb/$DIRP/ro-sess"; chmod 400 "$ro_sb/$DIRP/ro-sess"
417
+ PROBE_ERR=$(mktemp "${MARKER_DIR}/err-XXXXXX")
418
+ r_out=$(TMPDIR="$ro_sb" jq -nc --arg c 'glab mr list --state opened' --arg s 'ro-sess' \
419
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' \
420
+ | TMPDIR="$ro_sb" bash "$HOOK" 2>"$PROBE_ERR"); r_rc=$?
421
+ contract_check "$r_out" "$r_rc" 'read-only marker file'
422
+ rm -f "$PROBE_ERR"; PROBE_ERR=""
423
+ fi
424
+
425
+ # An over-long session id became an over-long filename and the failed open
426
+ # printed `File name too long` to real stderr.
427
+ long_sess=$(awk 'BEGIN{for(i=0;i<400;i++)printf "s"}')
428
+ probe remind 'glab mr list --state opened' "$long_sess"
429
+ # ...and the capped name must still DEDUP; without the cap the marker can never
430
+ # be written and the advisory repeats forever.
431
+ probe quiet 'glab ci list --branch dev' "$long_sess"
432
+
433
+ # A TMPDIR that looks like an OPTION (`-P`, `-L`) must not send `cd` to HOME and
434
+ # plant the marker directory there.
435
+ # NEVER delete anything this suite did not create. An earlier version removed
436
+ # `$HOME/ccl-skills-cliflag` on failure — and this suite runs in `make test`, so
437
+ # a developer whose real marker directory happened to sit there would have had it
438
+ # destroyed by a test run. If the path already exists, the probe cannot attribute
439
+ # what it finds and SKIPS rather than guess.
440
+ if [ -e "$HOME/ccl-skills-cliflag" ]; then
441
+ printf 'SKIP [option-shaped TMPDIR probe: %s pre-exists, refusing to touch it]\n' \
442
+ "$HOME/ccl-skills-cliflag" >&2
443
+ else
444
+ opt_in=$(mktemp "${MARKER_DIR}/in-XXXXXX")
445
+ jq -nc --arg c 'glab mr list --state opened' --arg s 'opt-sess' \
446
+ '{tool_input:{command:$c},session_id:$s,cwd:"/tmp"}' > "$opt_in"
447
+ TMPDIR=-P bash "$HOOK" < "$opt_in" >/dev/null 2>&1
448
+ TMPDIR=-L bash "$HOOK" < "$opt_in" >/dev/null 2>&1
449
+ rm -f "$opt_in"
450
+ if [ ! -e "$HOME/ccl-skills-cliflag" ]; then
451
+ pass=$((pass+1))
452
+ else
453
+ fail=$((fail+1))
454
+ # DO NOT REMOVE IT. "It was absent a moment ago, so this probe must have made
455
+ # it" is an inference, not attribution — a concurrent process can create the
456
+ # path inside that window, and this suite runs in `make test` on machines
457
+ # that are doing other things. A test does not delete anything it did not
458
+ # certainly create; reporting the path and leaving it is the correct end.
459
+ printf 'FAIL [option-shaped TMPDIR: %s now exists; NOT removed, inspect it yourself]\n' \
460
+ "$HOME/ccl-skills-cliflag" >&2
461
+ fi
462
+ fi
463
+
464
+ # --- PRESENCE, NOT BEHAVIOR. Two guards cannot be reached by any portable probe
465
+ # here (both need a second user id), and this suite says so. That honesty has
466
+ # a cost the review named: a later change could delete either one and stay
467
+ # green. These checks close only that half — they assert the guard is still
468
+ # WRITTEN, never that it works. A green here is not evidence of behavior, and
469
+ # must not be cited as if it were. If a guard is deliberately removed, delete
470
+ # its check in the same change and say why in the header. ---
471
+ for guard in '-O "$marker_dir"' '"$dir_owner" != "root"'; do
472
+ # `--` because the first guard string starts with `-O`, which grep would
473
+ # otherwise take as an option — the check then always failed, on a clean hook.
474
+ if grep -Fq -- "$guard" "$HOOK"; then
475
+ pass=$((pass+1))
476
+ else
477
+ fail=$((fail+1))
478
+ printf 'FAIL [unprobed guard vanished from the hook: %s]\n' "$guard" >&2
479
+ fi
480
+ done
481
+
482
+ printf '\n%s: pass=%d fail=%d\n' "$(basename "$0")" "$pass" "$fail"
483
+ [ "$fail" -eq 0 ] || exit 1
@@ -54,6 +54,7 @@ export const OPENCODE_HOOK_BINDINGS = Object.freeze({
54
54
  "guard-edit-isolation.sh": "tool.execute.before:edit/write/apply_patch",
55
55
  "owner-dispatch-guard.sh": "tool.execute.before:edit/write/apply_patch/bash",
56
56
  "guard-merge-authorization.sh": "tool.execute.before:bash",
57
+ "remind-unverified-cli-flag.sh": "tool.execute.before:bash",
57
58
  "guard-delegation-owner.sh": "tool.execute.before:task/agent",
58
59
  "remind-post-merge-cleanup.sh": "tool.execute.after:bash",
59
60
  "merge-authorization-prompt.sh": "chat.message",
@@ -482,6 +483,10 @@ export const CclSkills = async (context: {
482
483
  throw new Error(`ccl-skills merge authorization blocked this operation.\n${merge.message ?? "OpenCode hook runtime unavailable."}`)
483
484
  }
484
485
  enforce(merge, "ccl-skills merge authorization")
486
+ // Advisory only: it never blocks, so its context is appended rather than
487
+ // enforced. Mirrors hooks/remind-unverified-cli-flag.sh under Claude Code.
488
+ const flagNote = additionalContext(runHook(hooksRoot, "remind-unverified-cli-flag.sh", hookPayload, directory, 10_000))
489
+ if (flagNote) prependTaskContext(args, [flagNote])
485
490
  }
486
491
 
487
492
  if (tool === "task" || tool === "agent") {
@@ -25,7 +25,7 @@ Before editing app code, native project files, platform configs, styles, assets,
25
25
 
26
26
  Repo-local agent contracts (`AGENTS.md` at the repo root and in source directories) are part of the delivery contract: when a change moves a stable boundary, generated surface, workflow, or directory-local rule, update the nearest contract in the same MR and keep coverage in sync per `product-rd-workflow`'s spec / repo-contract sync gate.
27
27
 
28
- When checking a cross-platform app against team standards, split conformance into deterministic and agent review evidence. Deterministic checks cover build flavors/schemes, package locks, generated API/IDL client usage, environment/lane config, unit/widget/instrumented/E2E commands, CI gates, signing/release config, and request/trace identifiers in central clients. Agent review checks cover screen/module boundaries, platform abstraction leakage, native bridge contracts, finite-value mapping, device evidence quality, and whether tests prove the shipped runtime paths rather than only mocked widgets. For the per-substack deterministic executor list (each analyzer is its own toolchain with thin defaults — `dart analyze`+`flutter_lints` with `analyzer>errors:` severity escalation, detekt `coroutines`/`exceptions` inactive rules, SwiftLint opt-in safety rules, RN typed-ESLint) and the shipped client conformance checkers, see `testing-strategy/references/fitness-functions.md` §4.1.3 (client language-basics; spec 006). Layering/dependency-direction has no out-of-box rule on any substack — express it as import-ban config (Konsist/ArchUnit/custom_lint/dep-cruiser).
28
+ When checking a cross-platform app against team standards, split conformance into deterministic and agent review evidence. Deterministic checks cover build flavors/schemes, package locks, generated API/IDL client usage, environment/lane config, unit/widget/instrumented/E2E commands, CI gates, signing/release config, and request/trace identifiers in central clients. Agent review checks cover screen/module boundaries, platform abstraction leakage, native bridge contracts, finite-value mapping, device evidence quality, and whether tests prove the shipped runtime paths rather than only mocked widgets. For the per-substack deterministic executor list (each analyzer ships thin defaults) and the shipped client conformance checkers, see `testing-strategy/references/fitness-functions.md` §4.1.3 (spec 006). Layering/dependency-direction has no out-of-box rule on any substack — express it as import-ban config (Konsist/ArchUnit/custom_lint/dep-cruiser).
29
29
 
30
30
  1. Resolve the target app shape.
31
31
  - Flutter shared app, React Native app, native Android, native iOS, or mixed native plus shared module.
@@ -138,6 +138,7 @@ When checking a cross-platform app against team standards, split conformance int
138
138
 
139
139
  7. Verify on rendered surfaces.
140
140
  - Run the repo's formatter, analyzer/linter, typecheck/build, and focused tests.
141
+ - Test-code authoring: pick the matching § from the decision table in `testing-strategy/references/test-code-authoring-patterns.md`; smell lint: `testing-strategy/references/fitness-functions.md` §4.1.4.
141
142
  - **TC traceability**: Flutter/Dart tests link via `tcTest(['TC-XX-NNN'], 'desc', () { ... })` wrapper (registers at registration time, so `skip:` / `skipIf:` etc. still register correctly). Helper from `test-artifact-management/references/tc_helpers/tc.dart`, installed under `test/tc.dart`. Native Android/iOS use the language-appropriate wrapper convention (write a small helper that appends to `test/results/tc-map.jsonl`). See `test-artifact-management/references/tc-marker-conventions.md`. Before adding tests, `grep -rn 'tcTest.*"TC-[A-Z]' test/` plus the sidecar to check for existing coverage — extend rather than duplicate. When a TC is marked 废弃, grep both source and sidecar for that TC ID; follow deprecation cascade in `testing-strategy`. Tests without any `tcTest(...)` / equivalent helper call: prompt user only when the underlying code is also removed.
142
143
  - **废弃级联:业务代码是否仍在用** — 每端原生 + 跨端各有套路:
143
144
  1. Flutter / Dart:`grep -rEn "import ['\"]package:<pkg>/<file>|import ['\"]\.+/<file>" lib/`;运行 `dart analyze` 看 unused-import warning;路由表 `MaterialApp.routes` / GoRouter 配置内未注册的页面可删
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: go-microservice-architecture
3
- description: Use when designing, reviewing, or explaining a new product's Go backend or microservice architecture using Kitex or similar RPC, Hertz or similar HTTP gateway, protobuf IDL, MySQL/GORM-style relational storage, Redis cache/locks/rate limiting, service discovery, dynamic config, message queues, observability, DI, and code generation. Product-agnostic; do not depend on existing codebase paths, service names, or legacy repositories. Prefer this for architecture, service boundaries, contracts, data ownership, reliability, security, and platform decisions when no code changes are requested; use go-microservice-dev for implementation work. Triggers also include "Go 后端架构怎么设计", "Go 微服务怎么拆", "RPC 接口怎么定义", "Go 服务边界", "Go 服务重构 / 架构分层重构", "拆分 Go 服务的上帝类/模块边界", "decompose a god class in a Go service".
3
+ description: Design, review, or explain a new product's Go backend/microservice architecture using Kitex or similar RPC, Hertz or similar HTTP gateway, protobuf IDL, MySQL/GORM storage, Redis cache/locks/limits, service discovery, dynamic config, message queues, observability, DI, and code generation. Also owns multi-tenant isolation, event-driven/Kafka, and data-platform (sharding/replicas/HA) architecture. Product-agnostic; no dependence on existing codebase paths, service names, or legacy repos. Prefer this for architecture/boundary/contract/data-ownership/reliability/security/platform decisions with no code changes; use go-microservice-dev for implementation. Triggers include "Go 后端架构怎么设计", "Go 微服务怎么拆", "RPC 接口怎么定义", "Go 服务边界", "Go 服务重构 / 架构分层重构", "拆分 Go 服务的上帝类/模块边界 / decompose a god class".
4
4
  ---
5
5
 
6
6
  # Go Microservice Architecture
@@ -101,6 +101,8 @@ Not appropriate for:
101
101
 
102
102
  ## Dependency Direction
103
103
 
104
+ This layering is the **[Ports-and-Adapters / Hexagonal](https://alistair.cockburn.us/hexagonal-architecture/)** idea in moderation (Alistair Cockburn; borrowed scope: the ports/adapters placement idea only, not the full pattern vocabulary) — infrastructure adapters sit behind interfaces the inner layers own.
105
+
104
106
  Recommended:
105
107
 
106
108
  ```text
@@ -4,7 +4,7 @@ Use when designing the data-platform substrate of a service or service-fleet: DB
4
4
 
5
5
  This complements `data-modeling-and-migrations.md` (which owns schema, index, transaction, outbox, and per-service migration concerns): this file owns the **substrate** that schema and queries sit on. Load both when designing a new data-bound service or auditing an existing one.
6
6
 
7
- > **Sibling sync.** A parallel `python-service-architecture/references/data-platform-architecture.md` mirrors **all non-stack-specific sections** of this file. Only the *Go-specific implementation patterns* section diverges by stack. The mirrored sections stay free of three categories of stack-specific token: DB-engine-specific syntax, runtime/concurrency-mechanic names, and library/framework API names. The concrete token list and grep command live in the *Mirrored-section grep gate* subsection at the end of this file's stack-glue.
7
+ > **Sibling sync.** A parallel `python-service-architecture/references/data-platform-architecture.md` mirrors **all non-stack-specific sections** of this file. Only the *Go-specific implementation patterns* section diverges by stack. The mirrored sections stay free of three categories of stack-specific token: DB-engine-specific syntax, runtime/concurrency-mechanic names, and library/framework API names. The concrete token list and grep command live in the *Mirrored-section grep gate* subsection at the end of this file's stack-glue. Tree-specific routing references are written inline for both trees so the mirrored bytes stay identical; cross-file parity is machine-checked by `skill-extraction-workflow/scripts/check-parallel-stack-parity.sh` (wired into `check-ccl-skills.sh`), which diffs the mirrored regions byte-for-byte (no normalization) and blocks on any divergence.
8
8
 
9
9
  > **Sanitization boundary.** Vendor names (PostgreSQL, MySQL, Vitess, TiDB, CockroachDB, Aurora, Cloud Spanner, AlloyDB, Cloud SQL, DynamoDB, RDS Proxy, PgBouncer, ProxySQL, S3, Glacier, gp3, io2, etc.) below are illustrative; concrete topology choices, region names, cluster identifiers, and capacity numbers live only in the maintainer's private alias map. The sanitization audience list is positive (external / client / regulator / SOC / procurement / internal-compliance / sales-engineering / partner draft / forwardable-internal); sanitize before any document leaves the implementation team's approved audience.
10
10
  >