ops-wiki-agent-kit 0.1.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 (49) hide show
  1. package/.github/agents/docs-target-catalog.agent.md +52 -0
  2. package/.github/agents/docs-target-queue-from-catalog.agent.md +34 -0
  3. package/.github/agents/source-code-to-spec-documenter.agent.md +39 -0
  4. package/.github/agents/source-code-to-spec-reviewer.agent.md +51 -0
  5. package/.github/agents/source-code-to-system-ops-overview.agent.md +39 -0
  6. package/.github/prompts/00-generate-target-all-spec.prompt.md +35 -0
  7. package/.github/prompts/01-generate-target-foundation-spec.prompt.md +35 -0
  8. package/.github/prompts/02-generate-target-architecture-spec.prompt.md +35 -0
  9. package/.github/prompts/03-generate-target-ops-spec.prompt.md +35 -0
  10. package/.github/prompts/04-review-target-spec.prompt.md +24 -0
  11. package/.github/prompts/docs-target-catalog.prompt.md +32 -0
  12. package/.github/prompts/docs-target-queue-from-catalog.prompt.md +28 -0
  13. package/.github/prompts/generate-system-ops-overview.prompt.md +62 -0
  14. package/.github/skills/database-query/SKILL.md +140 -0
  15. package/.github/skills/database-query/references/client-commands.md +189 -0
  16. package/.github/skills/database-query/references/query-safety.md +109 -0
  17. package/.github/skills/database-query/scripts/find_db_config.py +273 -0
  18. package/.github/skills/docs-target-catalog/SKILL.md +194 -0
  19. package/.github/skills/docs-target-catalog/references/docs-target-queue-conversion.md +164 -0
  20. package/.github/skills/docs-target-catalog/references/entrypoint-source-patterns.md +83 -0
  21. package/.github/skills/docs-target-catalog/references/output-templates.md +168 -0
  22. package/.github/skills/docs-target-queue-from-catalog/SKILL.md +255 -0
  23. package/.github/skills/docs-target-queue-from-catalog/references/docs-target-queue-contract.md +125 -0
  24. package/.github/skills/docs-target-queue-from-catalog/references/metadata-acquisition-patterns.md +149 -0
  25. package/.github/skills/docs-target-queue-from-catalog/scripts/write_documentation_target_queue.py +527 -0
  26. package/.github/skills/source-code-to-ops-spec-guidelines/SKILL.md +128 -0
  27. package/.github/skills/source-code-to-ops-spec-guidelines/references/01-system-overview-and-business-scenarios-guideline.md +172 -0
  28. package/.github/skills/source-code-to-ops-spec-guidelines/references/02-core-architecture-flow-data-logic-guideline.md +637 -0
  29. package/.github/skills/source-code-to-ops-spec-guidelines/references/03-error-ops-scenario-coverage-guideline.md +533 -0
  30. package/.github/skills/source-code-to-ops-spec-guidelines/references/supporting-output-format-diagram-and-example-reference.md +523 -0
  31. package/.github/skills/source-code-to-spec-documenter/SKILL.md +80 -0
  32. package/.github/skills/source-code-to-spec-documenter/references/generation-handoff-contract.md +155 -0
  33. package/.github/skills/source-code-to-spec-documenter/references/generation-workflow.md +184 -0
  34. package/.github/skills/source-code-to-spec-documenter/references/source-tracing-rules.md +271 -0
  35. package/.github/skills/source-code-to-spec-documenter/references/target-queue-contract.md +78 -0
  36. package/.github/skills/source-code-to-spec-documenter/scripts/spec_queue.py +222 -0
  37. package/.github/skills/source-code-to-spec-tools/SKILL.md +117 -0
  38. package/.github/skills/source-code-to-spec-tools/references/repository-artifact-contract.md +122 -0
  39. package/.github/skills/source-code-to-spec-tools/references/target-queue-schema-contract.md +116 -0
  40. package/.github/skills/source-code-to-spec-tools/references/terminology-contract.md +121 -0
  41. package/.github/skills/source-code-to-spec-tools/scripts/catalog_query.py +324 -0
  42. package/.github/skills/source-code-to-spec-tools/scripts/queue_contract.py +210 -0
  43. package/.github/skills/source-code-to-spec-tools/scripts/source_lookup.py +360 -0
  44. package/.github/skills/source-code-to-spec-tools/scripts/target_query.py +407 -0
  45. package/.github/skills/source-code-to-system-ops-overview/SKILL.md +82 -0
  46. package/.github/skills/source-code-to-system-ops-overview/references/system-operations-overview-guideline.md +332 -0
  47. package/README.md +116 -0
  48. package/ops-wiki-agent-kit.js +173 -0
  49. package/package.json +22 -0
@@ -0,0 +1,407 @@
1
+ #!/usr/bin/env python3
2
+ import sys
3
+
4
+ sys.dont_write_bytecode = True
5
+
6
+ import argparse
7
+ import json
8
+ import re
9
+ from pathlib import Path
10
+
11
+ from queue_contract import (
12
+ COVERAGE_COLUMNS,
13
+ MAIN_COLUMNS,
14
+ SOURCE_TYPE_SCOPE_HINTS,
15
+ SUMMARY_COLUMNS,
16
+ SUMMARY_SCOPE_COLUMNS,
17
+ normalize_key,
18
+ normalize_doc_profile,
19
+ normalize_text,
20
+ )
21
+
22
+
23
+ def configure_stdio():
24
+ for stream_name in ("stdout", "stderr"):
25
+ stream = getattr(sys, stream_name, None)
26
+ reconfigure = getattr(stream, "reconfigure", None)
27
+ if callable(reconfigure):
28
+ reconfigure(encoding="utf-8")
29
+
30
+
31
+ def tokenize_scope(value):
32
+ return re.findall(r"[a-z0-9]+", normalize_key(value))
33
+
34
+
35
+ def token_matches_hint(hint_token, token):
36
+ if token == hint_token:
37
+ return True
38
+ if token in {f"{hint_token}s", f"{hint_token}es"}:
39
+ return True
40
+ if hint_token.endswith("y") and token == f"{hint_token[:-1]}ies":
41
+ return True
42
+ return False
43
+
44
+
45
+ def hint_matches_scope(hint, scope_tokens):
46
+ hint_tokens = tokenize_scope(hint)
47
+ if not hint_tokens:
48
+ return False
49
+ if len(hint_tokens) == 1:
50
+ return any(token_matches_hint(hint_tokens[0], token) for token in scope_tokens)
51
+ for index in range(0, len(scope_tokens) - len(hint_tokens) + 1):
52
+ if scope_tokens[index : index + len(hint_tokens)] == hint_tokens:
53
+ return True
54
+ return False
55
+
56
+
57
+ def split_markdown_row(line):
58
+ line = line.strip()
59
+ if not line.startswith("|") or not line.endswith("|"):
60
+ return []
61
+ cells = []
62
+ current = []
63
+ escaped = False
64
+ for char in line[1:-1]:
65
+ if escaped:
66
+ current.append(char)
67
+ escaped = False
68
+ elif char == "\\":
69
+ escaped = True
70
+ elif char == "|":
71
+ cells.append("".join(current).strip())
72
+ current = []
73
+ else:
74
+ current.append(char)
75
+ cells.append("".join(current).strip())
76
+ return cells
77
+
78
+
79
+ def is_separator(cells):
80
+ return bool(cells) and all(re.fullmatch(r":?-{3,}:?", cell.strip()) for cell in cells)
81
+
82
+
83
+ def markdown_escape(value):
84
+ return normalize_text(value).replace("|", "\\|")
85
+
86
+
87
+ def normalize_row(row):
88
+ normalized = {column: row.get(column, "") for column in MAIN_COLUMNS}
89
+ normalized["doc_profile"] = normalize_doc_profile(
90
+ normalized.get("doc_profile", ""), normalized.get("source_type", "")
91
+ )
92
+ return normalized
93
+
94
+
95
+ def iter_markdown_tables(lines):
96
+ current_heading = ""
97
+ for index, line in enumerate(lines):
98
+ heading = re.match(r"^(#{1,6})\s+(.+?)\s*$", line)
99
+ if heading:
100
+ current_heading = heading.group(2).strip()
101
+ continue
102
+
103
+ header = split_markdown_row(line)
104
+ if not header or index + 1 >= len(lines):
105
+ continue
106
+ separator = split_markdown_row(lines[index + 1])
107
+ if len(separator) != len(header) or not is_separator(separator):
108
+ continue
109
+
110
+ rows = []
111
+ end_line = index + 2
112
+ for data_index in range(index + 2, len(lines)):
113
+ values = split_markdown_row(lines[data_index])
114
+ if len(values) != len(header):
115
+ break
116
+ rows.append(dict(zip(header, values)))
117
+ end_line = data_index + 1
118
+ yield {
119
+ "section": current_heading,
120
+ "columns": header,
121
+ "rows": rows,
122
+ "start_line": index + 1,
123
+ "end_line": end_line,
124
+ }
125
+
126
+
127
+ def read_queue(path):
128
+ path = Path(path)
129
+ lines = path.read_text(encoding="utf-8-sig").splitlines()
130
+ main_rows = []
131
+ summary_rows = []
132
+ coverage_rows = []
133
+ table_meta = []
134
+
135
+ for table in iter_markdown_tables(lines):
136
+ table_meta.append(
137
+ {
138
+ "section": table["section"],
139
+ "columns": table["columns"],
140
+ "row_count": len(table["rows"]),
141
+ "start_line": table["start_line"],
142
+ "end_line": table["end_line"],
143
+ }
144
+ )
145
+ if table["columns"] == MAIN_COLUMNS:
146
+ main_rows = [normalize_row(row) for row in table["rows"]]
147
+ elif table["columns"] == SUMMARY_COLUMNS:
148
+ summary_rows = table["rows"]
149
+ elif table["columns"] == COVERAGE_COLUMNS:
150
+ coverage_rows = table["rows"]
151
+
152
+ if not main_rows:
153
+ raise SystemExit(f"No documentation target queue table found in {path}")
154
+ return {
155
+ "path": str(path),
156
+ "rows": main_rows,
157
+ "summary": summary_rows,
158
+ "coverage": coverage_rows,
159
+ "tables": table_meta,
160
+ }
161
+
162
+
163
+ def row_text(row):
164
+ return "\n".join(str(value) for value in row.values())
165
+
166
+
167
+ def matches_filters(row, args):
168
+ if args.id and normalize_key(row.get("id")) not in {normalize_key(value) for value in args.id}:
169
+ return False
170
+ if args.source_type and normalize_key(row.get("source_type")) not in {
171
+ normalize_key(value) for value in args.source_type
172
+ }:
173
+ return False
174
+ for field_name in ("group", "name", "entrypoint", "keyword"):
175
+ expected = getattr(args, field_name)
176
+ if expected and normalize_key(expected) not in normalize_key(row.get(field_name)):
177
+ return False
178
+ if args.query and normalize_key(args.query) not in normalize_key(row_text(row)):
179
+ return False
180
+ if args.foundation and normalize_key(row.get("foundation_doc_status")) != normalize_key(args.foundation):
181
+ return False
182
+ if args.architecture and normalize_key(row.get("architecture_doc_status")) != normalize_key(args.architecture):
183
+ return False
184
+ if args.ops and normalize_key(row.get("ops_doc_status")) != normalize_key(args.ops):
185
+ return False
186
+ if args.review and normalize_key(row.get("review_status")) != normalize_key(args.review):
187
+ return False
188
+ if args.profile and normalize_key(row.get("doc_profile")) != normalize_key(args.profile):
189
+ return False
190
+ if args.completed and normalize_key(row.get("document_completed_flag(Y/N)")) != normalize_key(args.completed):
191
+ return False
192
+ return True
193
+
194
+
195
+ def find_rows(queue, args):
196
+ rows = [row for row in queue["rows"] if matches_filters(row, args)]
197
+ if args.limit is not None:
198
+ rows = rows[: args.limit]
199
+ return rows
200
+
201
+
202
+ def get_row(rows, target_id):
203
+ for row in rows:
204
+ if normalize_key(row.get("id")) == normalize_key(target_id):
205
+ return row
206
+ raise SystemExit(f"Target id not found: {target_id}")
207
+
208
+
209
+ def basename_handle(value):
210
+ text = normalize_text(value).strip("`")
211
+ text = text.replace("\\", "/")
212
+ if "/" in text:
213
+ return text.rsplit("/", 1)[-1].casefold()
214
+ return text.casefold()
215
+
216
+
217
+ def related_reasons(seed, candidate):
218
+ reasons = []
219
+ for field_name in ("group", "entrypoint", "keyword", "document_path", "name"):
220
+ left = normalize_key(seed.get(field_name))
221
+ right = normalize_key(candidate.get(field_name))
222
+ if left and left == right:
223
+ reasons.append(f"same_{field_name}")
224
+ seed_base = basename_handle(seed.get("entrypoint", ""))
225
+ candidate_base = basename_handle(candidate.get("entrypoint", ""))
226
+ if seed_base and seed_base == candidate_base and "same_entrypoint" not in reasons:
227
+ reasons.append("same_entrypoint_basename")
228
+ return reasons
229
+
230
+
231
+ def related_rows(queue, target_id, limit, include_self):
232
+ seed = get_row(queue["rows"], target_id)
233
+ related = []
234
+ for row in queue["rows"]:
235
+ if normalize_key(row.get("id")) == normalize_key(seed.get("id")) and not include_self:
236
+ continue
237
+ reasons = related_reasons(seed, row)
238
+ if reasons:
239
+ item = dict(row)
240
+ item["related_reasons"] = reasons
241
+ related.append(item)
242
+ related.sort(key=lambda row: (len(row["related_reasons"]) * -1, row.get("id", "")))
243
+ if limit is not None:
244
+ related = related[:limit]
245
+ return seed, related
246
+
247
+
248
+ def source_summary_for_row(queue, row):
249
+ source_type = normalize_key(row.get("source_type"))
250
+ hints = SOURCE_TYPE_SCOPE_HINTS.get(source_type, [source_type])
251
+ matched = []
252
+ for summary in queue["summary"]:
253
+ scope_tokens = tokenize_scope(" ".join(summary.get(column, "") for column in SUMMARY_SCOPE_COLUMNS))
254
+ if any(hint_matches_scope(hint, scope_tokens) for hint in hints):
255
+ matched.append(summary)
256
+ return matched
257
+
258
+
259
+ def coverage_for_row(queue, row):
260
+ terms = [
261
+ row.get("id"),
262
+ row.get("source_type"),
263
+ row.get("group"),
264
+ row.get("name"),
265
+ row.get("entrypoint"),
266
+ row.get("keyword"),
267
+ ]
268
+ normalized_terms = [normalize_key(term) for term in terms if normalize_key(term)]
269
+ matched = []
270
+ for coverage in queue["coverage"]:
271
+ haystack = normalize_key(" ".join(coverage.values()))
272
+ if any(term in haystack for term in normalized_terms):
273
+ matched.append(coverage)
274
+ return matched
275
+
276
+
277
+ def render_table(rows, columns):
278
+ lines = []
279
+ lines.append("| " + " | ".join(columns) + " |")
280
+ lines.append("| " + " | ".join("---" for _ in columns) + " |")
281
+ for row in rows:
282
+ lines.append("| " + " | ".join(markdown_escape(row.get(column, "")) for column in columns) + " |")
283
+ return "\n".join(lines)
284
+
285
+
286
+ def print_output(payload, output_format, columns=None):
287
+ if output_format == "json":
288
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
289
+ return
290
+ if isinstance(payload, dict) and "rows" in payload:
291
+ rows = payload["rows"]
292
+ elif isinstance(payload, list):
293
+ rows = payload
294
+ else:
295
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
296
+ return
297
+ if not rows:
298
+ print("(no rows)")
299
+ return
300
+ selected_columns = columns or [column for column in MAIN_COLUMNS if column in rows[0]]
301
+ extra_columns = [column for column in rows[0].keys() if column not in selected_columns]
302
+ print(render_table(rows, selected_columns + extra_columns))
303
+
304
+
305
+ def command_find(args):
306
+ queue = read_queue(args.queue)
307
+ rows = find_rows(queue, args)
308
+ print_output({"queue": queue["path"], "count": len(rows), "rows": rows}, args.output)
309
+
310
+
311
+ def command_related(args):
312
+ queue = read_queue(args.queue)
313
+ seed, rows = related_rows(queue, args.id, args.limit, args.include_self)
314
+ print_output(
315
+ {
316
+ "queue": queue["path"],
317
+ "target": seed,
318
+ "count": len(rows),
319
+ "rows": rows,
320
+ },
321
+ args.output,
322
+ )
323
+
324
+
325
+ def command_preflight(args):
326
+ queue = read_queue(args.queue)
327
+ row = get_row(queue["rows"], args.id)
328
+ _, related = related_rows(queue, args.id, args.related_limit, False)
329
+ payload = {
330
+ "queue": queue["path"],
331
+ "target": row,
332
+ "source_acquisition_summary": source_summary_for_row(queue, row),
333
+ "coverage_review": coverage_for_row(queue, row),
334
+ "related_rows": related,
335
+ }
336
+ print_output(payload, args.output)
337
+
338
+
339
+ def command_sections(args):
340
+ queue = read_queue(args.queue)
341
+ payload = {
342
+ "queue": queue["path"],
343
+ "tables": queue["tables"],
344
+ "source_acquisition_summary": queue["summary"],
345
+ "coverage_review": queue["coverage"],
346
+ }
347
+ print_output(payload, args.output)
348
+
349
+
350
+ def build_parser():
351
+ parser = argparse.ArgumentParser(
352
+ description="Query docs/docs-target-queue.md rows and preflight context without manual table scanning."
353
+ )
354
+ subparsers = parser.add_subparsers(dest="command", required=True)
355
+
356
+ find = subparsers.add_parser("find", help="Find queue target rows by id, text, type, status, or handle.")
357
+ find.add_argument("--queue", default="docs/docs-target-queue.md")
358
+ find.add_argument("--id", action="append")
359
+ find.add_argument("--query")
360
+ find.add_argument("--source-type", action="append")
361
+ find.add_argument("--group")
362
+ find.add_argument("--name")
363
+ find.add_argument("--entrypoint")
364
+ find.add_argument("--keyword")
365
+ find.add_argument("--foundation")
366
+ find.add_argument("--architecture")
367
+ find.add_argument("--ops")
368
+ find.add_argument("--review")
369
+ find.add_argument("--profile")
370
+ find.add_argument("--completed")
371
+ find.add_argument("--limit", type=int)
372
+ find.add_argument("--output", choices=("json", "markdown"), default="json")
373
+ find.set_defaults(func=command_find)
374
+
375
+ related = subparsers.add_parser("related", help="Find queue rows related to one target row.")
376
+ related.add_argument("--queue", default="docs/docs-target-queue.md")
377
+ related.add_argument("--id", required=True)
378
+ related.add_argument("--limit", type=int, default=25)
379
+ related.add_argument("--include-self", action="store_true")
380
+ related.add_argument("--output", choices=("json", "markdown"), default="json")
381
+ related.set_defaults(func=command_related)
382
+
383
+ preflight = subparsers.add_parser("preflight", help="Build target preflight context from queue summary and rows.")
384
+ preflight.add_argument("--queue", default="docs/docs-target-queue.md")
385
+ preflight.add_argument("--id", required=True)
386
+ preflight.add_argument("--related-limit", type=int, default=25)
387
+ preflight.add_argument("--output", choices=("json", "markdown"), default="json")
388
+ preflight.set_defaults(func=command_preflight)
389
+
390
+ sections = subparsers.add_parser("sections", help="Show parsed queue sections and supporting tables.")
391
+ sections.add_argument("--queue", default="docs/docs-target-queue.md")
392
+ sections.add_argument("--output", choices=("json", "markdown"), default="json")
393
+ sections.set_defaults(func=command_sections)
394
+ return parser
395
+
396
+
397
+ def main():
398
+ configure_stdio()
399
+ args = build_parser().parse_args()
400
+ args.func(args)
401
+
402
+
403
+ if __name__ == "__main__":
404
+ try:
405
+ main()
406
+ except BrokenPipeError:
407
+ sys.exit(1)
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: source-code-to-system-ops-overview
3
+ description: 當需要根據 source code、SQL、設定檔、部署線索、依賴檔、Log keyword、錯誤訊息或既有文件,產生、更新或審查系統層級維運總覽文件時使用。適用於 web、mobile app、desktop application、DB SQL/database program、ERP、batch、CLI、integration service 等系統型態,以及 Java、C#、JavaScript/TypeScript、SQL、PL/SQL、Python 等技術棧;重點是建立 system-level operations overview、module inventory、runtime/config/integration/error/log/triage summary 與 source-backed boundary,而不是單一功能細節。
4
+ ---
5
+
6
+ # Source Code to System Ops Overview
7
+
8
+ ## Purpose
9
+
10
+ 使用這個 skill 依據 source code、config、deployment artifact、dependency file、SQL、database object、Log keyword、error message 與既有文件,產生或審查 `S01 系統維運總覽文件`。
11
+
12
+ 本 skill 的輸出是系統層級維運總覽,用來協助 L1/L2 Support、維運工程師、系統負責人、交接人員、QA 或 PM 快速判斷系統邊界、module、runtime/config、external integration、error/log clue 與第一查找方向。
13
+
14
+ 輸出與說明使用繁體中文。Source code、class name、method name、variable name、API path、SQL、database object、YAML/JSON/XML key、CLI command、Git command、protocol name、tool name、prompt placeholder、Markdown syntax 與 Mermaid syntax 必須保留英文或原始語法。
15
+
16
+ ## Reference Guideline
17
+
18
+ 主要 reference guideline:
19
+
20
+ | Reference | Responsibility |
21
+ |---|---|
22
+ | `references/system-operations-overview-guideline.md` | 定義系統層級維運總覽的文件邊界、跨系統型態適用原則、章節架構、表格欄位、禁止混入的功能層內容與品質檢查。 |
23
+
24
+ 產生、更新或審查系統層級維運總覽前,先載入此 reference guideline。不要把 guideline-only 的欄位說明、撰寫要求或品質檢查文字逐字輸出到最終文件。
25
+
26
+ ## Scope Boundary
27
+
28
+ 輸出範圍是系統層級 source-backed overview、module / function inventory、navigation、triage map 與 evidence boundary。單一功能、API、job、screen、SQL routine、ERP customization 或 external interface 的完整流程若可由來源確認,應導向功能層 SPEC、API 文件、batch 文件、database routine 文件或正式 SOP;系統總覽只保留能支援系統層分流的入口、模組、可觀測線索、coverage gap 與文件連結。
29
+
30
+ 若使用者要求下列內容,應改用功能層 SPEC、API 文件、batch 文件、database routine 文件、正式 SOP,或在系統總覽中只留下導覽連結與 `Manual Required`:
31
+
32
+ - request / response field mapping
33
+ - 完整 SQL 查詢條件或資料修復語句
34
+ - 單一功能 validation rule、status transition 或 error handling flow
35
+ - production rerun / rollback / data fix SOP
36
+ - 正式 SLA、owner、escalation window 或監控閾值
37
+
38
+ ## Workflow
39
+
40
+ 1. 確認使用者的 source root、目標系統範圍、既有文件與預期輸出位置;若未指定輸出檔,使用專案慣例或建議 `docs/system-operations-overview.md`。
41
+ 2. 載入 `references/system-operations-overview-guideline.md`,確認章節邊界、來源標示、禁止內容與品質檢查。
42
+ 3. 掃描可確認來源,優先使用 `rg`、build/dependency files、config files、deployment files、entrypoints、route/job/screen/script/database object 線索與既有文件。
43
+ 4. 依系統型態整理 source evidence,不預設一定是 web 或 Java:
44
+ - web/API:route、controller、middleware、service、dependency、config。
45
+ - mobile app / desktop application:screen、view model、event handler、local config、installer/package metadata。
46
+ - DB SQL / database program:schema、table、view、stored procedure、function、trigger、job、SQL script。
47
+ - ERP / packaged system:module、customization、interface program、report、batch job、table extension、exported metadata。
48
+ - batch / CLI / integration service:scheduler、script、queue/topic、file path、external endpoint、job log。
49
+ 5. 產出文件時,每個重要結論都標示可回查來源位置;無法由 source 確認的正式規則列入人工補充項目,不輸出額外判斷欄位。
50
+ 6. 無法確認的 runtime truth、production rule、owner、SLA、credential、formal SOP 或 monitoring threshold,一律寫成「無法由目前來源確認」或列入人工補充項目,不要用命名推論補完。
51
+ 7. 對既有系統總覽做更新時,先比對現有內容與目前 source evidence;保留仍正確的段落,修正過期、無證據、過度功能層化或洩漏 secret value 的內容。
52
+ 8. 完成前依 reference guideline 的品質檢查逐項審查,特別確認沒有 secret value、production 操作指令、單一功能完整流程或無來源的關鍵結論。
53
+
54
+ ## Output Contract
55
+
56
+ 最終文件應優先包含下列章節;若某章不適用,保留簡短 `N/A` 或說明「無法由目前來源確認」,不要硬填推測內容。
57
+
58
+ 1. `系統用途與邊界`
59
+ 2. `系統主要模組與功能清單`
60
+ 3. `技術與執行環境線索`
61
+ 4. `系統設定與環境參數`
62
+ 5. `外部介面與整合點摘要`
63
+ 6. `錯誤訊息、Exception 與 Log 線索摘要`
64
+ 7. `維運初步分流判斷`
65
+ 8. `Source Code 證據範圍與人工補充項目`
66
+
67
+ 文件應是 narrative-first 加上必要 inventory tables。表格用來支援查找與交接,不應變成無來源的大量猜測清單。
68
+
69
+ ## Quality Gate
70
+
71
+ 完成前檢查:
72
+
73
+ - `SKILL.md` frontmatter 只有 `name` 與 `description`。
74
+ - `SKILL.md` 引用的 reference file 實際存在。
75
+ - 最終文件是 system-level overview,不是功能層 SPEC 或 runbook。
76
+ - web、Java、Spring、REST 或 server deployment 沒有被當成所有系統的預設前提。
77
+ - Java、C#、JavaScript/TypeScript、SQL、PL/SQL、Python、ERP metadata 或其他 technical identifiers 保留原文。
78
+ - Secret value、password、token、private key、certificate content 沒有被輸出。
79
+ - 每個重要 module、config、integration、error/log、triage 結論都有來源位置;無法確認的項目列入 coverage gaps 或人工補充項目。
80
+ - 沒有把猜測寫成已確認事實。
81
+
82
+ 若 validator 可用,針對 skill 目錄執行 skill validation;若沒有 validator,至少用 `git diff --check` 檢查 Markdown 空白與格式問題。