|
| 1 | +# Analyze phase |
| 2 | + |
| 3 | +Goal: produce a findings YAML that lists every public API in the requested scope, each annotated with the Go handler that serves it and the input/output types we plan to lift into OpenAPI. The human reviews this file before we spend tokens generating JSON. |
| 4 | + |
| 5 | +## Inputs |
| 6 | + |
| 7 | +- `--scope` — module or sub-module key from `mapping.yaml`, or `all`. |
| 8 | +- Parsed registry from `scripts/parse_pgy_registry.py` (run it once, cache the JSON in memory for the rest of this phase). |
| 9 | +- Source repos, synced to latest `origin/main`. |
| 10 | + |
| 11 | +## Step 1 — load and filter the registry |
| 12 | + |
| 13 | +Run the parser: |
| 14 | + |
| 15 | +```bash |
| 16 | +python3 <skill-path>/scripts/parse_pgy_registry.py ../fc-pgy/logic/api/api_test.go --format json |
| 17 | +``` |
| 18 | + |
| 19 | +Keep only rows where: |
| 20 | + |
| 21 | +- `auth == "all"` (other auth modes are not public — see `references/auth-modes.md`), AND |
| 22 | +- the `path` does NOT start with `/event/push/` (integration-key routes, never `app_key`-callable), AND |
| 23 | +- the row's `path` top-level segment matches one of the requested scope's `path_prefixes`, AND |
| 24 | +- the row's `provider` is in the scope's `providers` list (when specified — if `providers` is omitted, match on prefix alone). |
| 25 | + |
| 26 | +If `--scope all`, iterate every module where `hidden` is falsy and run the rest of the pipeline once per module. |
| 27 | + |
| 28 | +**Commented vs uncommented — treat them the same.** The convention: commented-out rows are existing production APIs already persisted in the DB; uncommented rows are new additions left active so `go test` can register them. Commenting is a deployment workflow detail, not a status indicator — a commented row is NOT deleted or inactive. All rows in this file are live APIs. This is the single biggest thing to remember. |
| 29 | + |
| 30 | +**Subgroup matching:** When a module defines `subgroups` in `mapping.yaml`, each surviving row must be assigned to a subgroup. Match by checking the row's `path` against each subgroup's `path_prefixes` in order — **first matching subgroup wins**. If no subgroup matches, assign the row to a catch-all group and emit a warning in findings so the user can update the mapping. |
| 31 | + |
| 32 | +## Step 2 — resolve the backend handler for each row |
| 33 | + |
| 34 | +For each surviving row, find the Go file that serves its `path`. The scope's `repos` entry tells you where to look. The convention is: |
| 35 | + |
| 36 | +- `fc-event`: `cmd/server/controller/<feature>/<operation>.go` — one file per operation, named after the URL segment. |
| 37 | +- `fc-oncall`: `callee/...` — grep for the path string. |
| 38 | +- `fc-pgy`: `cmd/server/controller/...` or `logic/.../controller.go`. |
| 39 | +- `monit-webapi`: `api/...` or `logic/.../http.go`. |
| 40 | +- `fc-rum`: `controller/...`. |
| 41 | +- `fc-statuspage`: `cmd/.../handler.go`. |
| 42 | + |
| 43 | +Practical lookup strategy: |
| 44 | + |
| 45 | +1. Try the file-name convention first. `/template/info` → `cmd/server/controller/template/info.go`. That's an O(1) hit rate >70% of the time inside fc-event. |
| 46 | +2. If not found, `git -C <repo> grep -l '"<path>"' origin/<branch> -- <controller-paths>` — the exact path string often appears in a routes.go file near the handler reference. |
| 47 | +3. If still not found, grep for the last URL segment as a Go identifier (e.g. `/template/preview` → `func Preview(`). Confirm you found the right handler by looking for `srv.GinToCtx` or `srv.Validate` in the same file. |
| 48 | +4. If none of those work, record the row under `unresolved` in findings and move on. Do not guess — unresolved rows are fine and the user can fix the mapping. |
| 49 | + |
| 50 | +For each resolved row, extract three things from the handler file: |
| 51 | + |
| 52 | +- `handler_file`: repo-relative path (e.g., `cmd/server/controller/template/info.go`). |
| 53 | +- `input_type`: the Go type passed to `srv.Validate(ctx, ...)`, `c.ShouldBindJSON(...)`, or `c.ShouldBindQuery(...)`. This is usually a local `xxxInput` struct in the same file. Record its name. |
| 54 | +- `output_type`: the Go type of the value passed to `srv.JSON(ctx, <value>)` on the success branch. This is trickier — follow these rules in order: |
| 55 | + 1. If the handler builds a struct literal inline (e.g., `srv.JSON(ctx, gin.H{"items": items, "total": total})`), record `output_type: inline` and include the field→type mapping. |
| 56 | + 2. If the handler calls a logic method and passes the result directly (e.g., `srv.JSON(ctx, item)`), trace the Go type of `item` — usually `*structs.IncidentItem` or `[]*structs.ChannelItem` — and record the fully qualified type name. |
| 57 | + 3. If the type is a generic helper (e.g., `listResult[structs.XxxItem]`), record both the wrapper and the element type. |
| 58 | + |
| 59 | +Do not inline the entire struct definition here — that's phase 2's job. Phase 1 just names types and their source files so phase 2 can read them. |
| 60 | + |
| 61 | +## Step 3 — assemble the findings YAML |
| 62 | + |
| 63 | +Write to `.api-review/findings-<scope-slug>-<YYYYMMDD-HHMMSS>.yaml` in the docs repo. Use forward slashes in the slug (`on-call-template`). |
| 64 | + |
| 65 | +Structure: |
| 66 | + |
| 67 | +```yaml |
| 68 | +scope: on-call/template |
| 69 | +generated_at: 2026-04-10T18:30:12Z |
| 70 | +docs_path: api-reference/on-call/template/ |
| 71 | +tag_en: Templates |
| 72 | +tag_zh: 模板管理 |
| 73 | +providers: [event] |
| 74 | +source_registry: ../fc-pgy/logic/api/api_test.go |
| 75 | +source_registry_commit: <git sha of origin/main HEAD> |
| 76 | + |
| 77 | +operations: |
| 78 | + - id: 1060 |
| 79 | + method: POST |
| 80 | + path: /template/info |
| 81 | + name: template:read:info |
| 82 | + name_cn: 查看模板详情 |
| 83 | + description: "" |
| 84 | + auth: all |
| 85 | + is_dangerous: false |
| 86 | + is_audit: false |
| 87 | + handler: |
| 88 | + repo: fc-event |
| 89 | + file: cmd/server/controller/template/info.go |
| 90 | + input_type: infoInput |
| 91 | + output_type: "*structs.TemplateItem" |
| 92 | + notes: "" |
| 93 | + - id: 1061 |
| 94 | + # ... |
| 95 | + |
| 96 | +unresolved: |
| 97 | + - id: 1234 |
| 98 | + method: POST |
| 99 | + path: /template/mysterious-op |
| 100 | + reason: "no handler found under cmd/server/controller/template/" |
| 101 | + |
| 102 | +stats: |
| 103 | + total_public: 8 |
| 104 | + resolved: 8 |
| 105 | + unresolved: 0 |
| 106 | +``` |
| 107 | +
|
| 108 | +`source_registry_commit` matters — it pins what we parsed so stale re-runs are obvious later. |
| 109 | + |
| 110 | +## Step 4 — emit the JSON sidecar and HTML report |
| 111 | + |
| 112 | +Right after writing the YAML, emit two companion files with the same stem: |
| 113 | + |
| 114 | +1. **JSON sidecar** at `.api-review/findings-<scope-slug>-<ts>.json` — identical structure to the YAML (`scope`, `generated_at`, `tag_en`, `tag_zh`, `operations`, `unresolved`, `stats`, …). Powers the HTML render without a PyYAML dependency. |
| 115 | +2. **HTML report** via the bundled renderer: |
| 116 | + |
| 117 | + ```bash |
| 118 | + python3 <skill_dir>/scripts/render_html.py \ |
| 119 | + .api-review/findings-<scope-slug>-<ts>.json \ |
| 120 | + --out .api-review/findings-<scope-slug>-<ts>.html |
| 121 | + ``` |
| 122 | + |
| 123 | + `<skill_dir>` is the directory containing this `analyze.md`. The renderer is stdlib-only — no install step. It renders operations sorted by path with HTTP-method chips, dangerous / audit-logged flags, and a dedicated "unresolved" section coloured warm oxblood so missing handlers are impossible to miss. |
| 124 | + |
| 125 | +**Rule of thumb:** YAML is for agents (the generate phase reads it), HTML is for humans (review the picked-up set). The HTML must never be parsed or rewritten by an agent. |
| 126 | + |
| 127 | +## Step 5 — human checkpoint |
| 128 | + |
| 129 | +Print to the user: |
| 130 | + |
| 131 | +- The paths to BOTH the YAML and HTML files. |
| 132 | +- The stats block. |
| 133 | +- Any `unresolved` rows with their reasons. |
| 134 | +- A one-line instruction: "Review the HTML at `<html_path>`, edit the YAML to drop any rows you don't want, then run `/api-review --mode generate --scope <scope>`." |
| 135 | + |
| 136 | +Stop. Do not proceed to generate unless `--auto` was set. |
| 137 | + |
| 138 | +## Auto-mode behavior |
| 139 | + |
| 140 | +If `--auto` is set, skip the "stop and wait" step but still write the findings file. Then invoke the generate phase in the same run, reading the file you just wrote. |
0 commit comments