Align Workflow C release safety docs

Constraint: Round-3 review required release/documentation alignment without changing runtime behavior.
Rejected: Assert a specific .changeset file exists in tests | consumed Changesets would break the release version commit.
Confidence: high
Scope-risk: narrow
Directive: Keep Workflow C BLOCKED/ipcheck=false retry opt-in only; do not document confirmed blocks as safe automatic fallback.
Tested: npm test --workspace court-auction-notice-search; npm run lint --workspace court-auction-notice-search; npm run ci; CLI codes usages smoke; CLI page-size rejection smoke; architect verification CLEAR
Not-tested: Live courtauction.go.kr property search, because this follow-up only changes release/docs/test guardrails.
This commit is contained in:
Jeffrey (Dongkyu) Kim 2026-05-08 02:39:23 +09:00
commit d4ab8fcd79
3 changed files with 62 additions and 3 deletions

View file

@ -8,11 +8,11 @@ The request body matches the canonical PGJ151M01 submission captured from a real
The static usage/region codetables come from upstream discovery captures: 4 대분류 (`10000=토지`, `20000=건물`, `30000=차량및운송장비`, `40000=기타`) plus representative mid/small classes; 19 시도 with their official codes. Sigungu/dong cascade XHRs are not reliable so callers pass raw codes (e.g. `"11680"`) directly.
`searchProperties()` automatically falls back to the Playwright client when the direct HTTP call returns `BLOCKED` or `UPSTREAM_ERROR` 400 (the WAF blocks bursty raw HTTP). Disable with `{ fallback: false }`.
`searchProperties()` automatically falls back to the Playwright client only for WAF-style raw HTTP `UPSTREAM_ERROR` 400 responses. Confirmed `BLOCKED` / `ipcheck=false` responses stop by default to avoid extending an IP block; retrying that condition requires explicit `fallbackOnBlocked:true`. Disable fallback entirely with `{ fallback: false }`.
Other fixes:
- `resolveUsageCode(name, level)` now refuses to silently return a wrong-level code for ambiguous names (e.g. `"아파트"` exists at multiple levels) — returns the input unchanged so the upstream rejects it instead of producing a wrong query.
- `resolveRegionCodes({})` no longer accidentally maps "no region" to the first row's sido.
- `flbdCount` is integer-only; `pageSize` is capped at 100 to avoid overloading the upstream.
- `flbdCount` is integer-only; `pageSize` is restricted to the observed PGJ151 dropdown values `10`/`20`/`50`/`100` to avoid unsupported upstream requests.
- Endpoint-aware HTTP/Playwright warmup (`PGJ151F00` for property search instead of `PGJ143M01`).
- CLI `search` accepts `--region 시도:시군구:읍면동` and `--usage 대:중:소` colon shorthand alongside the existing split flags.

View file

@ -94,7 +94,7 @@ const properties = await searchProperties({
| 물건 자유 조건검색 | `POST /pgj/pgjsearch/searchControllerMain.on` | canonical body captured via Playwright (`scripts/capture-pgj151-submit.cjs`); fixture at `packages/court-auction-notice-search/test/fixtures/canonical-search-body.json`. `pageNo/pageSize/statNum` 은 number, `pageSize` 는 upstream 드롭다운 값 `10`/`20`/`50`/`100`만 허용, `notifyLoc` 기본 `"off"`. |
| 법원사무소 코드 | `POST /pgj/pgjComm/selectCortOfcCdLst.on` | `{}` |
세션 cookie(`JSESSIONID`, `WMONID`)는 `GET /pgj/index.on?w2xPath=/pgj/ui/pgj100/PGJ143M01.xml&pgjId=143M01` 으로 사전에 한 번 받아둡니다.
세션 cookie(`JSESSIONID`, `WMONID`)는 endpoint별 진입 화면을 먼저 열어 받아둡니다. 매각공고/상세는 `GET /pgj/index.on?w2xPath=/pgj/ui/pgj100/PGJ143M01.xml&pgjId=143M01`, 물건 자유 조건검색(Workflow C)은 `GET /pgj/index.on?w2xPath=/pgj/ui/pgj100/PGJ151F00.xml&pgjId=151F00` 으로 warmup 합니다.
## 설치

View file

@ -3872,3 +3872,62 @@ test("repository docs advertise the k-skill-cleaner skill and agent usage source
assert.match(readme, /\[K-스킬 클리너 가이드\]\(docs\/features\/k-skill-cleaner\.md\)/);
assert.match(install, /--skill k-skill-cleaner/);
});
test("court auction Workflow C docs preserve the PGJ151 safety contract", () => {
const featureDoc = read(path.join("docs", "features", "court-auction-notice-search.md"));
assert.match(
featureDoc,
/물건 자유 조건검색[\s\S]*PGJ151F00/,
"Workflow C docs should name the PGJ151F00 warmup path for property search",
);
assert.match(
featureDoc,
/pageSize[\s\S]*`10`\/`20`\/`50`\/`100`/,
"Workflow C docs should keep pageSize aligned with the observed PGJ151 dropdown values",
);
assert.doesNotMatch(
featureDoc,
/세션 cookie\([^\n]+\)는 `GET \/pgj\/index\.on\?w2xPath=\/pgj\/ui\/pgj100\/PGJ143M01\.xml&pgjId=143M01` 으로 사전에 한 번 받아둡니다\./,
"Workflow C docs should not imply every endpoint warms up through PGJ143M01 only",
);
});
test("court auction pending changeset does not publish stale fallback or pageSize guidance", () => {
const changesetDir = path.join(repoRoot, ".changeset");
if (!fs.existsSync(changesetDir)) return;
const pendingCourtAuctionChangesets = fs
.readdirSync(changesetDir)
.filter((entry) => entry.endsWith(".md"))
.map((entry) => read(path.join(".changeset", entry)))
.filter((contents) => contents.includes('"court-auction-notice-search"') && contents.includes("searchProperties()"));
for (const contents of pendingCourtAuctionChangesets) {
assert.doesNotMatch(
contents,
/direct HTTP call returns `BLOCKED` or `UPSTREAM_ERROR` 400/,
"Changeset must not tell users that confirmed BLOCKED/ipcheck=false auto-falls back by default",
);
assert.doesNotMatch(
contents,
/`pageSize` is capped at 100/,
"Changeset must not describe pageSize as an arbitrary 1..100 cap",
);
assert.match(
contents,
/`UPSTREAM_ERROR` 400/,
"Changeset should still document the raw-HTTP WAF-style 400 fallback path",
);
assert.match(
contents,
/`BLOCKED`[\s\S]*`fallbackOnBlocked:true`/,
"Changeset should document that confirmed BLOCKED retry is explicit opt-in",
);
assert.match(
contents,
/`10`\/`20`\/`50`\/`100`/,
"Changeset should document the exact PGJ151 pageSize allowlist",
);
}
});