Add the first cheap-gas-nearby skill/package pair so nearby gas-price
queries can resolve a user-supplied location, translate it into Opinet's
KATEC search contract, and return the cheapest nearby stations with
address and facility detail. The docs and setup surfaces now advertise
the new skill and its Opinet API key requirement.
Constraint: Nearby fuel prices must come from the official KNOC Opinet API when available
Constraint: No new external dependencies were allowed for coordinate conversion or location resolution
Rejected: Map-only gas price scraping | official Opinet Open API exists and is the preferred source
Rejected: Require lat/lng input only | poorer UX than supporting landmark/station queries through anchor resolution
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep `OPINET_API_KEY` as the only supported official-price credential unless the repo adopts an Opinet proxy later
Tested: npm run ci; node --test packages/cheap-gas-nearby/test/index.test.js; offline fixture smoke via searchCheapGasStationsByLocationQuery('서울역', ...)
Not-tested: Live Opinet API call with a real `OPINET_API_KEY` (no non-placeholder key was configured locally)
Related: #54
The install guide had drifted from the real-estate skill and feature docs and still implied that macOS launchd should auto-run both the server and tunnel. This narrows the guidance back to tunnel-only launchd ownership and extends regression coverage so docs/install.md cannot silently reintroduce the server-side loop wording.
Constraint: Upstream docker compose already uses restart: unless-stopped
Constraint: Review-round-2 scope is limited to docs and regression coverage
Rejected: Leave docs/install.md under a generic launchd presence check | it missed the exact server/터널 regression
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep docs/install.md aligned with the detailed real-estate feature guide when self-host guidance changes
Tested: node --test scripts/skill-docs.test.js; npm run ci; fresh-clone upstream smoke with uv sync, uv run real-estate-mcp --help, and HTTP initialize on 127.0.0.1:8017/mcp
Not-tested: None
Tighten the real-estate skill docs so the launchd fallback stays operational
and the Onbid bid-result tools are described with the same WIP caveat the
upstream project still publishes.
Constraint: Upstream Docker compose already uses restart: unless-stopped while `docker compose ... up -d` daemonizes immediately
Rejected: Keep a separate server LaunchAgent with RunAtLoad + KeepAlive | launchd would restart-loop on the exiting compose command
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Mirror upstream capability caveats in k-skill docs and do not wrap daemonized server commands in launchd KeepAlive jobs
Tested: node --test scripts/skill-docs.test.js
Tested: npm run ci
Tested: uv sync
Tested: uv run real-estate-mcp --help
Tested: DATA_GO_KR_API_KEY=dummy uv run real-estate-mcp --transport http --host 127.0.0.1 --port 8017
Tested: curl initialize on http://127.0.0.1:8017/mcp returned protocolVersion 2024-11-05
Not-tested: Live 거래 조회 with a real DATA_GO_KR_API_KEY
Issue #53 is intentionally doc-only, so this change adds the
real-estate-search skill, feature guide, setup/security notes,
and regression coverage around the upstream real-estate-mcp
integration instead of importing server code.
The new docs keep the original MCP link, cover Codex/Claude
registration, and spell out the self-host + Cloudflare Tunnel +
launchd path for environments where no fixed hosted endpoint is
available.
Constraint: Must use tae0y/real-estate-mcp without copying its source into this repository
Constraint: Must include the original MCP link and a stable self-host fallback when no hosted endpoint is available
Rejected: Vendor the upstream MCP source | issue explicitly requires skill docs only
Rejected: Assume a public hosted MCP endpoint exists | upstream docs did not publish one
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep this integration doc-only; do not add a workspace or vendored server without revisiting issue #53 constraints
Tested: node --test scripts/skill-docs.test.js
Tested: npm run ci
Tested: upstream bootstrap smoke (`uv sync`, `uv run real-estate-mcp --help`, HTTP initialize on 127.0.0.1:8017)
Not-tested: live property data queries with a valid DATA_GO_KR_API_KEY
Merged dev into feature/#59 and resolved the overlap between the Joseon Sillok and Korean spell-check additions. The resolution keeps both helper/documentation surfaces, extends the root lint/test wiring to cover both Python helpers, and updates the stale negative docs regression so the merged branch verifies the shipped Korean spell-check assets instead of rejecting them.
Constraint: PR #62 must remain unmerged while restoring a clean merge against dev
Rejected: Drop the korean-spell-check changes from dev | would discard already-merged functionality on the base branch
Rejected: Keep the stale negative docs regression | contradicted the merged branch and broke ci
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When merging feature branches that add repo-level docs regressions, re-check for contradictory assertions before rerunning ci
Tested: npm run ci
Not-tested: Live upstream joseon-sillok or Nara spell-check HTTP probes during merge resolution
The helper already shipped a stdlib urllib opener that can reach the
official search endpoint in environments where requests/urllib3 aborts.
This change keeps that opener available even when requests imports
successfully and falls back to it on retryable requests transport
failures. Added regression coverage for opener availability and the
requests-to-urllib fallback so the default CLI path matches the live
verified behavior.
Constraint: Official sillok detail GETs can still time out transiently in this environment
Constraint: Keep TLS verification enabled and preserve the documented CLI entrypoints
Rejected: Force urllib for every request | keep the existing requests fast path when it succeeds
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Preserve the stdlib fallback tests whenever the transport layer changes
Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_sillok_search
Tested: node --test scripts/skill-docs.test.js
Tested: npm run ci
Tested: live forced-fallback probe against searchResultList.do with requests.post patched to OSError(22)
Not-tested: full live CLI completion through the detail GET in this environment
The skill installer only ships the skill directory, so the sillok helper has to live inside that payload. This moves the authoritative helper into joseon-sillok-search/scripts/, keeps the repo-root script as a thin shim for tests and docs, and trims footer metadata from parsed article bodies so excerpts match the published examples.
Constraint: skills add exposes only the installed skill payload, not repo-root helpers
Rejected: Keep the helper only under scripts/ | installed skill commands fail after copy-only installs
Confidence: high
Scope-risk: narrow
Directive: Keep the repo-root sillok wrapper thin and update the bundled helper first when behavior changes
Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_sillok_search
Tested: node --test scripts/skill-docs.test.js
Tested: temp installed-skill smoke run of python3 scripts/sillok_search.py --help plus footer-cleaning parse_detail_page check
Tested: npm run ci
Not-tested: live sillok.history.go.kr shell requests still hit connect timeouts in this environment
This follow-up addresses the blocking PR review items on the Joseon Sillok helper. The search loop now derives total pages from the first live page size so king/year filtering can reach later pages, TLS verification stays enabled on both requests and urllib paths, detail parsing accepts the live classification brackets, and repo docs stop linking to missing Korean spell-check assets on this branch.
Constraint: Must preserve the existing joseon-sillok-search surface and avoid new dependencies
Rejected: Keep verify=False behind an implicit fallback | still weakens authenticity for the default path
Rejected: Infer pagination from the hardcoded 50-row default | misses valid later-page matches when the site serves smaller pages
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If the official site changes page size or pagination markup again, update the first-page pagination regression before altering search_sillok pagination math
Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_sillok_search
Tested: node --test scripts/skill-docs.test.js
Tested: npm run ci
Tested: python3 scripts/sillok_search.py --query '훈민정음' --king 세종 --year 1443 --limit 1 --timeout 20
Tested: python3 - <<'PY' ... fetch_detail_page(..., article_id='kda_12512030_002', timeout=20) ... PY
Not-tested: Successful live sillok.history.go.kr responses in this environment (POST and detail GET both timed out at 20s)
Related: PR #62
Add a joseon-sillok-search skill and a Python helper that scrape the
official Joseon Annals search/detail pages. The helper normalizes
king/year metadata, fetches detail excerpts, and locks the repository
docs plus regression coverage around the shipped workflow.
Constraint: v1 must stay on official public HTML surfaces only
Constraint: Must avoid adding new dependencies for a simple scraping helper
Constraint: Shell connectivity to sillok.history.go.kr became intermittent during final live reruns
Rejected: Ship a new npm workspace | repo skill/docs pattern is enough for v1
Rejected: Add BeautifulSoup or another parser dependency | unnecessary for the bounded HTML patterns
Confidence: medium
Scope-risk: narrow
Reversibility: clean
Directive: Keep year filtering Gregorian and derived from official regnal metadata unless the upstream site exposes a better structured contract
Tested: npm run ci
Tested: Earlier live POST/detail probes against search/searchResultList.do and /id/kda_12512030_002 during implementation
Tested: Live official article inspection for kda_12512030_002 via the public site
Not-tested: Final end-to-end CLI live run after the last refactor, because the shell hit transient TCP timeouts to sillok.history.go.kr
Related: #59
The Nara/PNU surface can return a plain 'no issues found' HTML page
without the embedded result payload when a chunk is already clean.
Chunked file and markdown runs could hit that response on earlier
chunks, raise a ValueError, and never reach later chunks that still
needed corrections. Treat the no-issues page as an empty result set
and lock the behavior with narrow regression coverage.
Constraint: Upstream no-issue responses omit the JavaScript payload entirely and can split the status message across HTML whitespace
Rejected: Fabricate a synthetic result page | empty-page handling already preserves the original clean chunk
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep empty-result detection aligned with the upstream no-issues message unless the service publishes a structured empty payload contract
Tested: python3 -m unittest scripts.test_korean_spell_check
Tested: npm run lint
Tested: npm run typecheck
Tested: npm test
Tested: npm run build
Tested: python3 scripts/korean_spell_check.py --text '아버지가방에들어가신다.' --format json
Tested: python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n아버지가방에들어가신다.' --max-chars 15 --format json
Tested: python3 scripts/korean_spell_check.py --text 테스트 --max-chars 0 --format json
Tested: python3 scripts/korean_spell_check.py --file <tmpfile> --format json
Tested: python3 scripts/korean_spell_check.py --file <tmpfile> --max-chars 18 --format json
Not-tested: Other upstream empty-result templates beyond the current no-issues HTML wording
The file-layout follow-up already preserved blank runs and indentation,
but the overlong-unit path still checked the wrong variable when
extracting a trailing separator. That could strand separators in
a standalone chunk and break exact reassembly for tight max-char
limits.
This commit fixes the separator extraction guard and locks the
behavior with a regression that proves chunk concatenation still
matches the original text when an overlong paragraph is followed
by a blank-line separator.
Constraint: File-mode reconstruction must preserve exact layout while chunking long input
Rejected: Broader chunking rewrite | existing structure only needed the overflow guard corrected
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep split_text_into_chunks chunk concatenation identical to the original input for layout-sensitive file checks
Tested: npm run lint; npm run typecheck; npm test; npm run build; python3 scripts/korean_spell_check.py --text '아버지가방에들어가신다.' --format json; python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n아버지가방에들어가신다.' --max-chars 15 --format json; python3 scripts/korean_spell_check.py --text 테스트 --max-chars 0 --format json; manual --file smoke with triple blank lines and indentation
Not-tested: Live-service failure modes such as Cloudflare/browser challenges
The follow-up layout preservation pass renamed the working unit variable, but one separator-length guard still referenced the old paragraph name. This commit finishes the refactor so exact-boundary paragraph chunks keep the preserved-separator logic reachable and the new regression coverage stays green.
Constraint: Must preserve the existing feature/#47 branch and PR #60 flow while fixing the approved review follow-up
Rejected: Revert the layout-preserving chunking refactor | would discard the verified file-layout fix and its regressions
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep chunk reassembly lossless; new chunking changes should continue to round-trip original separators byte-for-byte
Tested: npm run lint; npm run typecheck; npm test; npm run build; python3 scripts/korean_spell_check.py --text '아버지가방에들어가신다.' --format json; python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n아버지가방에들어가신다.' --max-chars 15 --format json; python3 scripts/korean_spell_check.py --file <tempfile-with-triple-blank-lines> --format json; python3 scripts/korean_spell_check.py --text 테스트 --max-chars 0 --format json (expected argument error)
Not-tested: Live multi-page service responses beyond the local smoke cases
The spell-check helper now keeps exact blank-line runs and paragraph
indentation when chunking and reassembling file-style input, while still
allowing the official service's spacing corrections to flow through.
Regression coverage now locks the collapsed-layout, triple-blank-line,
and cross-boundary spacing cases that triggered the PR review.
Constraint: Official Nara/PNU payloads can collapse layout and sometimes merge corrections across preserved paragraph boundaries
Rejected: Narrow docs away from file-level proofreading | the existing feature scope explicitly supports file and Markdown checks
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Preserve exact layout tokens only at original newline boundaries; do not reintroduce global strip/join normalization without real file-mode verification
Tested: npm run lint; npm run typecheck; npm test; npm run build; python3 scripts/korean_spell_check.py --text '아버지가방에들어가신다.' --format json; python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n아버지가방에들어가신다.' --max-chars 15 --format json; python3 scripts/korean_spell_check.py --file <tmpfile> --format json; python3 scripts/korean_spell_check.py --text 테스트 --max-chars 0 --format json
Not-tested: Live upstream behavior for extremely long leading/trailing-whitespace-only files
Related: PR #60
The Nara payload can collapse paragraph separators into normalized page text, so the helper now maps corrections back onto the original chunk before rebuilding corrected_text. The CLI also rejects non-positive --max-chars values, and regression tests cover both the layout-preservation path and invalid argument handling.
Constraint: Nara result pages can normalize blank lines and sentence spacing before exposing errInfo offsets
Rejected: Narrow docs away from file/Markdown proofreading | preserving original chunk separators keeps the documented workflow intact
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep multiline separator preservation tied to the original chunk whenever a suggestion only changes whitespace across collapsed boundaries
Tested: python3 -m unittest scripts.test_korean_spell_check; npm run lint; npm run typecheck; npm test; npm run build; python3 scripts/korean_spell_check.py --text '아버지가방에들어가신다.' --format json; python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n아버지가방에들어가신다.' --max-chars 15 --format json; python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n왠지 않되요.' --format json; python3 scripts/korean_spell_check.py --text 테스트 --max-chars 0 --format json
Not-tested: Live multi-page Nara payloads with separator-sensitive corrections across multiple returned pages
Add a skill guide and Python helper that use the approved old_speller HTML flow, chunk long text conservatively, and report original/suggestion/reason deltas. The docs also record the public-site limits, Cloudflare behavior, and non-commercial usage policy so agents do not overreach the free surface.
Constraint: Public site is HTML-only and may return 403 to non-browser clients
Constraint: Must not add new dependencies or high-volume crawling behavior
Rejected: Node fetch client | Cloudflare returned 403 in this environment
Rejected: Paid API integration | no public contract or credentials were available for this task
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep usage low-rate and non-commercial unless supplier-approved API terms are added
Tested: npm run lint
Tested: npm run typecheck
Tested: npm test
Tested: npm run build
Tested: python3 scripts/korean_spell_check.py --text '아버지가방에들어가신다.' --format json
Tested: python3 scripts/korean_spell_check.py --text $'아버지가방에들어가신다.\n\n아버지가방에들어가신다.' --max-chars 15 --format json
Not-tested: Paid API/order flow
Not-tested: High-volume commercial workloads
Not-tested: Non-UTF-8 file inputs
Drop the browser-based scraping package (packages/coupang-product-search)
and switch to the uju777/coupang-mcp MCP server for all Coupang product
searches. This removes the anti-bot workaround complexity and provides
8 ready-to-use tools via MCP Streamable HTTP with no API key required.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Issue #44 adds Beopmang as the documented fallback when the primary korean-law-mcp path is unavailable, and locks the new routing into the doc regression suite so future edits do not silently revert the policy.
Constraint: Existing guidance must still prefer korean-law-mcp and keep LAW_OC scoped to the local CLI/MCP path
Rejected: Add a repo-local Beopmang client package | issue only requires fallback registration, not a new implementation surface
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the primary-first rule intact; Beopmang is an outage fallback, not the default path
Tested: node --test scripts/skill-docs.test.js; npm run ci; korean-law list; python3 live Beopmang search smoke for 관세법
Not-tested: Live Beopmang MCP handshake from a GUI MCP client
A fresh rerun showed the SK direct inventory total continues to move during the day, so the feature doc now records the verified smoke-run timestamp without freezing a brittle exact count. The regression suite was updated first so the docs stay variability-aware and diff-clean in future follow-ups.
Constraint: Live SK direct inventory totals change over time even when the parsing contract stays stable
Rejected: Keep documenting a fixed total from one smoke run | it immediately drifted and made the docs stale
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the used-car live verification note timestamped and variability-aware instead of pinning an exact inventory total
Tested: node --test scripts/skill-docs.test.js; git diff --check; npm run ci; npx changeset status; live 10-query run against https://www.skdirect.co.kr/tb at 2026-04-02T07:59:41.391Z; LSP diagnostics on scripts/skill-docs.test.js; architect verification
Not-tested: No additional content changes outside the used-car feature doc
Review feedback found that the new used-car workspace would not ship because it lacked a changeset, and the fallback install docs still omitted the runtime package. This follow-up adds regression coverage first, then restores both release and install-path coverage with the smallest possible diff.
Constraint: New publishable workspaces must be wired through Changesets to reach npm release automation
Constraint: Fallback install docs must list runtime packages users need when skill files are present but global packages are missing
Rejected: Fix only the changeset gap | would leave the documented fallback install path incomplete
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep used-car-price-search in both the fallback global install example and a Changesets entry whenever release wiring changes
Tested: node --test scripts/skill-docs.test.js; npx changeset status; live 10-query used-car run against SK direct at 2026-04-02T07:38:44.949Z; npm run ci; LSP diagnostics on used-car package files and scripts/skill-docs.test.js; architect verification
Not-tested: No additional live provider permutations beyond the verified 10-query smoke run
Issue #46 required surveying major Korean rental companies before implementation and then choosing the easiest stable provider. SK렌터카 다이렉트 exposes 타고BUY inventory in public Next.js page data, so the feature stays dependency-free while still supporting live repeated lookups and documented provider rationale.
Constraint: Must compare major Korean rental companies before implementation
Constraint: Must verify 10+ live lookups against a real provider surface
Rejected: 롯데오토옥션 as v1 provider | public list contract was unstable and legacy .do flows returned inconsistent or 404 pages
Rejected: 레드캡렌터카 as v1 provider | no public used-car inventory or API surface was found
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep the v1 provider read-only and inventory-snapshot-based unless a stable documented public API is confirmed
Tested: npm run ci
Tested: Live 10-query run against https://www.skdirect.co.kr/tb at 2026-04-02T07:22:46Z
Tested: LSP diagnostics on affected files
Not-tested: Seller-specific detail drilldowns or non-SK providers
Related: #46
The previous follow-up aligned the main skill examples, but the done-criteria text still left interpretation and ordinance routing implicit. This commit makes the completion checklist explicit and extends the doc regression so future edits cannot silently drop those lookup paths.
Constraint: Must stay within the existing issue #41 korean-law-mcp-only contract
Rejected: Leave the done checklist implicit | reviewers and future edits could drift from the enforced lookup set
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When the supported Korean-law lookup set changes, update the done checklist and regression together
Tested: node --test scripts/skill-docs.test.js
Not-tested: full CI before commit
The approved issue #41 feature already worked, but the shipped skill text still under-described ordinance and interpretation lookups compared with the documented capability set. This follow-up tightens the skill copy and locks that contract with a doc regression so the branch stays merge-ready without changing the underlying korean-law-mcp routing rules.
Constraint: Must preserve the existing korean-law-mcp-only and mode-specific LAW_OC setup contract
Rejected: Leave the skill wording as-is | drift from the documented lookup surface would remain
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the skill examples and doc regression aligned whenever supported korean-law-mcp search surfaces change
Tested: node --test scripts/skill-docs.test.js
Not-tested: live LAW_OC-backed upstream API queries
Verification uncovered trailing whitespace in the new korean-law feature guide when checking the branch diff. I added a regression to the skill docs suite and removed the whitespace so the approved documentation contract stays clean and reviewable.
Constraint: Preserve the existing korean-law-mcp + mode-specific LAW_OC contract without widening scope
Rejected: Leave the whitespace issue as-is | git diff --check stayed dirty on the branch
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep docs/features/korean-law-search.md free of trailing whitespace so diff hygiene stays enforced by the regression
Tested: node --test scripts/skill-docs.test.js; npx tsc --noEmit --pretty false --project /Users/jeffrey/Projects/k-skill/tsconfig.json; npm install -g korean-law-mcp && korean-law list && korean-law help search_law; npm run ci; git diff --check
Not-tested: Live credentialed LAW_OC search execution against the upstream API
A review found the new korean-law-search docs treated LAW_OC as an unconditional prerequisite even though the upstream remote MCP endpoint is configured separately from the local CLI/server path. This update makes the docs and regression tests mode-specific: local CLI/MCP uses LAW_OC, while the remote endpoint stays a korean-law-mcp-only url fallback without a user-supplied credential.
Constraint: Upstream korean-law-mcp uses LAW_OC on the local CLI/server path while the documented remote MCP endpoint is configured with url only
Rejected: Keep LAW_OC mandatory for every korean-law-mcp mode | contradicts upstream docs and reviewer evidence
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep README/setup/skill docs and regression tests aligned on the local-vs-remote korean-law-mcp contract
Tested: node --test scripts/skill-docs.test.js; npm install -g korean-law-mcp && korean-law list && korean-law help search_law; npx tsc --noEmit --pretty false --project /Users/jeffrey/Projects/k-skill/tsconfig.json; npm run ci; lsp_diagnostics scripts/skill-docs.test.js
Not-tested: Live remote MCP endpoint connection against https://korean-law-mcp.fly.dev/mcp
Add a documentation-first korean-law-search skill and lock the repo docs around the rule that Korean law queries must go through korean-law-mcp rather than a new in-repo package.
The change updates install/setup/security guidance, publishes the new feature doc, and adds regression tests so future edits keep the LAW_OC + korean-law-mcp contract intact.
Constraint: Issue #41 requires korean-law-mcp for every Korean law lookup
Constraint: Must not add a new npm or python package in this repository
Rejected: Add a repo-local law package | violates the no-new-package requirement and duplicates upstream MCP work
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep Korean law lookup guidance pinned to korean-law-mcp unless issue requirements explicitly change
Tested: node --test scripts/skill-docs.test.js
Tested: npm install -g korean-law-mcp && korean-law list && korean-law help search_law
Tested: npm run ci
Tested: npx tsc --noEmit --pretty false --project /Users/jeffrey/Projects/k-skill/tsconfig.json
Not-tested: live search_law/get_law_text against a real LAW_OC credential
Coupang blocks unattended shopper queries in this environment, so the new package focuses on the durable pieces we can ship honestly: official URL builders, browser-captured HTML parsers, and explicit automation probes. The docs and skill now explain the seller-API limitation, the verified anti-bot behavior, and the browser-capture fallback expected by callers.
Constraint: No general shopper Open API surfaced in Coupang's official developer docs
Constraint: Headless/direct retrieval is anti-bot blocked in verified local probes
Rejected: Bundle a scraping bypass or hidden browser dependency | violates repo policy and would over-claim reliability
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep probe/docs behavior aligned with fresh live verification before claiming unattended Coupang access works
Tested: npm run ci; live probeAutomation(query=생수) with direct fetch + Playwright-core browserFetchHtml; LSP diagnostics on changed JS/test files
Not-tested: Headed/manual browser sessions with a human-authenticated Coupang context
The README already advertises OpenClaw/ClawHub, but the docs
regression only matched OpenClaw. Tighten the assertion to the
exact supported-client fragment so a future edit cannot silently
remove ClawHub while keeping the issue verification command stable.
Constraint: PR #39 already publishes a verification command keyed to the current test name
Rejected: Rename the test to mention ClawHub explicitly | would drift from the published verification command
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep this regression synchronized with the README supported-client line whenever that copy changes
Tested: node --test scripts/skill-docs.test.js --test-name-pattern 'README advertises OpenClaw among the supported coding agents'
Tested: npm run lint
Tested: npm run typecheck
Tested: npm run build
Tested: npm test
Tested: lsp diagnostics for scripts/skill-docs.test.js (0 errors)
Not-tested: N/A
The approved scope for issue #29 was narrowed to README support messaging,
so this change adds OpenClaw/ClawHub to the supported-client line and locks
that wording with a regression test in the docs suite.
Constraint: Issue #29 was approved as a README-only compatibility/docs change
Rejected: Broader install-doc updates | out of approved scope for this issue
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep OpenClaw support wording aligned with the supported-client README line and its docs regression test
Tested: Focused Node docs regression, npm run lint, npm run typecheck, npm run build, npm test
Not-tested: Live OpenClaw or ClawHub install flow (issue scope was documentation-only)
The Seoul subway proxy endpoint code is present locally, but the hosted public route is not live yet. This change turns the user-facing subway docs back into an explicit proxy configuration flow, replaces the misleading hosted default in setup examples, and keeps subway proxy examples on self-host/local URLs until rollout is verified.
Constraint: Hosted k-skill-proxy.nomadamas.org/v1/seoul-subway/arrival is not live yet
Rejected: Keep the hosted Seoul subway URL as the default path | would send default users to a 404 route
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Do not restore a hosted Seoul subway default until the public proxy route is deployed and smoke-verified
Tested: node --test scripts/skill-docs.test.js; npm run ci; local proxy smoke on 127.0.0.1:4120 with stubbed Seoul upstream (GET /health, GET /v1/seoul-subway/arrival?stationName=강남)
Not-tested: Live hosted proxy smoke after deployment
Route Seoul subway arrival lookups through k-skill-proxy so the
hosted proxy owns the Seoul Open Data upstream key and end users
only need the proxy base URL. Add proxy route coverage, update
skill/docs guidance, and align setup materials with the hosted
proxy flow used for fine dust.
Constraint: Must keep the proxy public, read-only, and dependency-free
Constraint: Must satisfy TDD-first verification and ship on feature/#35 targeting dev
Rejected: Add a separate client helper package | unnecessary extra layer for a single proxy route
Rejected: Keep SEOUL_OPEN_API_KEY as an end-user requirement | defeats the issue goal
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep the Seoul subway proxy surface limited to station-arrival passthrough unless tests/docs expand the contract
Tested: npm run ci; local proxy runtime on 127.0.0.1:4120 for /health and /v1/seoul-subway/arrival on 2026-03-31 with an invalid upstream key
Not-tested: Live success response with a valid Seoul Open API key
Related: #35
Agent environments (OpenClaw, Claude Code, Codex) assume users delegate
credentials to the agent. sops+age added setup friction without real
security benefit since the agent decrypts on every call anyway.
New model: skills declare required env var names; how they are supplied
is up to the agent (own vault, shell env, or ~/.config/k-skill/secrets.env
as the default fallback with 0600 permissions).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The toss-securities workspace was added without a root lockfile entry, so clean installs failed before CI could reach the otherwise-green test suite. This change adds a regression test that pins the required workspace/link metadata and regenerates the root package-lock so npm ci stays reproducible.
Constraint: GitHub Actions runs npm ci before npm run ci
Rejected: Rely on a manual lockfile refresh only | would allow the CI blocker to regress silently
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When adding a workspace, refresh package-lock.json and extend scripts/skill-docs.test.js to pin the required lockfile metadata
Tested: node --test scripts/skill-docs.test.js; npm ci --dry-run; npm run ci; brew tap JungHoonGhae/tossinvest-cli && brew install tossctl && tossctl version; node smoke via packages/toss-securities/src/index.js getQuote('TSLA'); LSP diagnostics on scripts/skill-docs.test.js and packages/toss-securities/src/index.js
Not-tested: No new runtime behavior beyond lockfile metadata refresh
Wrap the upstream tossinvest-cli binary behind a small read-only Node package,
add the matching skill/docs wiring, and lock the behavior with regression tests plus
publish/release metadata so the new workspace stays release-ready.
Constraint: Issue #25 required using JungHoonGhae/tossinvest-cli rather than reimplementing Toss APIs directly
Constraint: The public package surface must stay read-only and avoid wrapping trading mutations
Rejected: Direct HTTP client against unofficial Toss endpoints | issue explicitly required the upstream CLI and would widen maintenance risk
Rejected: Skill docs only with no package wrapper | repo convention is to ship reusable package + docs + release wiring together
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep toss-securities limited to read-only tossctl commands unless a future issue explicitly approves trading flows and adds stronger safeguards
Tested: npm run ci; brew tap JungHoonGhae/tossinvest-cli && brew install tossctl && tossctl version; node smoke calling packages/toss-securities/src/index.js getQuote('TSLA'); lsp diagnostics on affected files
Not-tested: Authenticated account/portfolio/order-history commands against a real Toss session
Merged dev into feature/#23 and resolved the overlapping root workspace
packaging and skill-doc coverage so the branch now carries both the
new kakao-bar-nearby work from dev and the kleague-results work from
this PR without dropping either surface.
Constraint: Root pack/test wiring must cover every publishable workspace
Rejected: Keep only the PR-side root script/docs updates | would discard newer dev branch coverage for kakao-bar-nearby
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: When adding a new workspace package, update root pack:dry-run coverage and skill-doc regressions in the same change to reduce future merge churn
Tested: npm run ci
Tested: node --input-type=module live smoke for getKLeagueSummary('2026-03-22', { leagueId: 'K리그1', team: 'FC서울', includeStandings: true })
Tested: node --input-type=module live smoke for getMatchResults('2026-03-22', { leagueId: 'K리그2' })
Not-tested: Remote GitHub Actions rerun after pushing the merge-resolution commit
The station-anchor fixes changed the documented sadang smoke snapshot, so this follow-up pins both published examples to the current 2026-03-29 live result and adds regression coverage to stop the docs from drifting again.
Constraint: Release/package docs must stay aligned with shipped live-smoke evidence
Rejected: Update only the markdown snapshots | would leave no regression guard against future drift
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When live Kakao smoke outputs change, update both the feature doc and package README together and keep the shared doc-regression assertion in sync
Tested: node --test packages/kakao-bar-nearby/test/index.test.js; node --test scripts/skill-docs.test.js; npm ci; npm run ci; live searchNearbyBarsByLocationQuery('사당', { limit: 3, panelLimit: 8 }) on 2026-03-29
Not-tested: Fresh-worktree clone after this doc-only follow-up
The K League site already exposes JSON schedule and standings endpoints, so this change wraps those official surfaces in a reusable workspace package and wires the new skill/docs flow into the repo.
The implementation keeps the fetch/parse boundary small, locks normalization with fixtures and regression tests, and documents the publish follow-up needed for the new npm package. Korean-language request headers are pinned so live payloads keep the expected team names and result labels.
Constraint: Must use official K League surfaces instead of adding scraping or third-party dependencies
Rejected: HTML scraping from schedule pages | official JSON endpoints already provide schedule and standings data
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep the accept-language header pinned to Korean unless team alias normalization is expanded for English payloads
Tested: npm run ci; live getKLeagueSummary('2026-03-22', { leagueId: 'K리그1', team: 'FC서울', includeStandings: true }); live getMatchResults('2026-03-22', { leagueId: 'K리그2' })
Not-tested: Live in-progress/postponed match statuses beyond the fixture-covered finished-game path
Kakao Map mobile search results and place panels provide enough public data to turn station/neighborhood queries into nearby bar summaries with live open status, menu hints, seating hints, and phone numbers. This adds a reusable workspace package plus docs/skill wiring, while keeping the flow keyless and grounded in TDD + live smoke verification.
Constraint: Must avoid paid/authenticated Kakao APIs and new dependencies
Constraint: Nearby bar results must use current live panel/open-hour data
Rejected: Kakao Local REST API | requires app key/setup and breaks the no-auth posture
Rejected: Naver Map scraping | public responses were more rate-limited in testing
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: If Kakao changes panel3/search HTML contracts, verify headers and anchor-selection heuristics before expanding fallback logic
Tested: node --test packages/kakao-bar-nearby/test/index.test.js
Tested: node --test scripts/skill-docs.test.js
Tested: lsp diagnostics on affected files (0 errors)
Tested: live smoke searchNearbyBarsByLocationQuery('사당') on 2026-03-29
Tested: npm run ci
Not-tested: Precise distance calculation when Kakao station panels omit coordinates
The fine-dust proxy now resolves natural-language region hints through
city-level station lists and only returns a report when a single station
can be justified. When the hint is ambiguous, the proxy returns a small
candidate list so callers can retry with an exact station name instead
of silently guessing.
The skill guidance was updated to match that runtime contract: region
hint first, then retry with stationName when candidate_stations are
returned. Coordinate-centric guidance was removed from the primary skill
surface so the default path stays lightweight and consistent with the
live proxy behavior.
Constraint: The current AirKorea key can access city-level and station-level measurement APIs but station-info lookups may still return 403
Constraint: Free-API proxy responses must stay safe to expose publicly, so ambiguous locations should not be auto-guessed
Rejected: Auto-pick the first city-level station for unmatched district hints | hides ambiguity and returns misleading air-quality data
Rejected: Keep coordinate-first language in the primary skill | no coordinate source exists in the default user flow
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Preserve the ambiguous_location contract; if you improve matching later, prefer evidence-backed narrowing over silent fallback guesses
Tested: node --test scripts/skill-docs.test.js; npm run test --workspace k-skill-proxy; python3 -m unittest discover -s scripts -p test_fine_dust.py; live curl for ambiguous regionHint=광주 광산구 and exact stationName=우산동(광주)
Not-tested: Broader region alias quality outside the manually checked examples
The fine-dust lane now treats the public proxy as the default surface,
keeps a simple summarized report endpoint, and also exposes a narrow
AirKorea passthrough shape so callers can reuse upstream query patterns
without carrying service keys on the client side.
The skill instructions were trimmed down so the default path is obvious,
region-name guidance stays visible, and detailed implementation notes
move into feature docs instead of bloating the primary skill surface.
Constraint: Free-API proxy endpoints are intentionally public and must avoid embedding upstream secrets in the repo
Constraint: AirKorea station-info access can return 403 even when measurement access succeeds, so the report path needs a measurement-only fallback
Rejected: Keep proxy auth via shared token | contradicts the intended public free-API proxy policy
Rejected: Force all callers onto the summary endpoint only | passthrough compatibility is useful for direct HTTP consumers
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep the proxy allowlist narrow; if new upstream routes are exposed, document them explicitly rather than turning this into a generic open proxy
Tested: node --test scripts/skill-docs.test.js; npm run test --workspace k-skill-proxy; python3 -m unittest discover -s scripts -p test_fine_dust.py; live curls against /health, /v1/fine-dust/report, and /B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty
Not-tested: Fresh reboot validation of PM2/cloudflared persistence after the latest code-only changes
Merged the latest dev branch into feature/#17 and resolved the overlapping install/test updates by preserving both the fine-dust additions from this branch and the newer Daiso workspace coverage from dev.
Constraint: Base branch dev advanced after PR #21 was opened
Rejected: Rebase and rewrite the feature branch history | a merge update restores mergeability with less risk
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When install docs or regression suites diverge across branches, keep both shipped skill entries and broaden shared assertions instead of dropping one side
Tested: npm run ci
Not-tested: Remote push / GitHub UI mergeability refresh
The fine-dust helper still broke the documented station-name path
when the station lookup API returned no rows, and it guessed a
KHAI label from PM10 whenever the upstream API omitted khaiGrade.
This change locks both behaviors with regressions, resolves station
selection to a direct measurement lookup when only the station name
is usable, and reports 정보없음 when the source omits the KHAI field.
The docs now match the implemented fallback and missing-field behavior.
Constraint: Keep the fix compatible with existing fetch_station_payload callers and PR #21 scope
Rejected: Recompute KHAI from other pollutant fields | official formula/data inputs are not implemented in this helper
Rejected: Fail hard when station lookup is empty despite --station-name | contradicts the documented direct station-name fallback
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Do not synthesize 통합대기등급 from PM10/PM2.5 surrogates unless the official KHAI formula and inputs are implemented end-to-end
Tested: python3 scripts/test_fine_dust.py; npm run ci; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --lat 37.5665 --lon 126.9780; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --region-hint '서울 강남구'; python3 scripts/fine_dust.py --help; env -u AIR_KOREA_OPEN_API_KEY python3 scripts/fine_dust.py report --lat 37.5665 --lon 126.9780; python3 -m py_compile scripts/fine_dust.py scripts/test_fine_dust.py
Not-tested: Live Air Korea API responses with a real service key
The validate job was failing because the KTX helper regression suite imported optional runtime dependencies directly from the module top level. GitHub Actions does not inherit the local user site-packages that made those imports succeed on the workstation, so the test suite died before it could exercise the helper logic.
This change makes the helper import-safe in minimal environments by deferring requests usage, providing lightweight fallbacks for optional Korail/Crypto imports during unit tests, and surfacing an explicit install command when the real runtime dependencies are actually needed. The docs now list pycryptodome alongside korail2, and the regression suite forces PYTHONNOUSERSITE=1 so CI keeps exercising the dependency-light path instead of accidentally relying on a developer machine.
Constraint: PR #19 must keep npm run ci green on GitHub Actions without assuming user-level Python packages
Constraint: The KTX helper still needs the real korail2 and pycryptodome packages for live reservation flows
Rejected: Installing ad-hoc Python packages in the CI workflow | hides the import-safety regression instead of fixing the helper/test contract
Rejected: Removing the Python regression suite from skill-docs coverage | would lose the guard on the train_id reservation flow
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep the KTX helper importable under PYTHONNOUSERSITE=1 and document every required runtime package in both the skill and install docs
Tested: PYTHONNOUSERSITE=1 python3 -m unittest discover -s scripts -p test_ktx_booking.py
Tested: node --test scripts/skill-docs.test.js
Tested: npm run ci
Not-tested: GitHub Actions validate rerun after push
The CLI previously inferred lookup provenance only from whether lat/lon
were supplied, which overstated precision when the Air Korea nearby
station lookup returned no rows and the command had to fall back to the
region/station search path. Carry the resolved lookup source through the
station-fetch step so rendered reports distinguish true coordinate hits
from fallback results, and lock that behavior with a JSON CLI regression.
Constraint: Keep the existing fetch_station_payload interface stable for current tests and callers
Rejected: Recompute provenance inside build_report from args alone | cannot observe live nearby-empty fallback outcome
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: If station lookup adds more branches, keep report provenance wired from the resolved fetch path rather than inferring from raw inputs
Tested: PYTHONPATH=scripts python3 -m unittest scripts.test_fine_dust.FineDustTests.test_cli_json_report_marks_region_fallback_when_nearby_lookup_is_empty; python3 scripts/test_fine_dust.py; npm run ci; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --lat 37.5665 --lon 126.9780; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --region-hint '서울 강남구'; python3 scripts/fine_dust.py --help; env -u AIR_KOREA_OPEN_API_KEY python3 scripts/fine_dust.py report --lat 37.5665 --lon 126.9780
Not-tested: Live Air Korea network responses with a real API key in this environment
PR #21 review found the live location path was sending raw WGS84 latitude/longitude into getNearbyMsrstnList. This updates the helper to convert WGS84 inputs into Air Korea's central-origin TM coordinates without adding a new dependency, adds regression coverage for the live request path plus fallback behavior, and aligns the published skill/docs examples with tmX/tmY usage.
Constraint: Air Korea nearby-station lookup requires TM coordinates and this repo should not add new runtime dependencies for a helper script
Rejected: Add pyproj/proj4 as a dependency | unnecessary dependency footprint for a deterministic coordinate transform
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep getNearbyMsrstnList on TM inputs only; if the upstream coordinate contract changes, verify against the official Air Korea docs before editing request params again
Tested: npm run ci; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --lat 37.5665 --lon 126.9780; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --region-hint '서울 강남구'; python3 scripts/fine_dust.py --help; env -u AIR_KOREA_OPEN_API_KEY python3 scripts/fine_dust.py report --lat 37.5665 --lon 126.9780; python3 -m py_compile scripts/fine_dust.py scripts/test_fine_dust.py
Not-tested: Live Air Korea API call with a real service key on this branch
Merged origin/dev into feature/#15 and resolved the shared docs, package script, and skill regression conflicts so the Daiso work and the Blue Ribbon work can ship together. The resolution keeps both publishable workspaces in the dry-run packaging check and aligns install/source docs plus regression coverage with the combined branch state.
Constraint: PR #19 targets dev and GitHub marked it CONFLICTING against origin/dev
Constraint: Release and packaging changes must continue to pass npm run ci after conflict resolution
Rejected: Dropping the Blue Ribbon additions during merge resolution | would lose newer dev branch work
Rejected: Keeping only one workspace in pack:dry-run | would leave the other publishable package unverified
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: If another publishable workspace is added, extend both package.json pack:dry-run and scripts/skill-docs.test.js together
Tested: npm install; npm run ci
Not-tested: GitHub mergeability refresh after push
Issue #17 approved an Air Korea two-API flow with a region fallback, so this change adds a new fine-dust skill, wires it into the repo docs/setup surfaces, and includes a runnable helper plus fixtures/tests for repeatable verification.
Constraint: Must use the approved official Air Korea APIs and secure secret registration flow
Rejected: Use an unofficial air-quality API | issue follow-up explicitly approved the Air Korea two-API flow
Rejected: Require coordinates only | issue discussion required a practical fallback when precise location is unavailable
Confidence: high
Scope-risk: moderate
Directive: Keep the helper and docs aligned with Air Korea endpoint names and fallback order if the official API contract changes
Tested: npm run ci; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --lat 37.5665 --lon 126.9780; python3 scripts/fine_dust.py report --station-file scripts/fixtures/fine-dust-stations.json --measurement-file scripts/fixtures/fine-dust-measurements.json --region-hint '서울 강남구'; python3 scripts/fine_dust.py --help; missing-secret error path without AIR_KOREA_OPEN_API_KEY
Not-tested: Live Air Korea API calls with a real AIR_KOREA_OPEN_API_KEY
Add a regression that pins the package README to the location-first guidance, 코엑스 alias example, and official Blue Ribbon surfaces, then document the search/zone and restaurants/map flow in the package README so the reusable workspace package stays aligned with the shipped skill behavior.
Constraint: Follow-up is scoped to issue #16 / PR #18 and must stay within approved docs and regression coverage
Rejected: Leave package README coverage implicit in the skill and feature docs tests | package docs could drift unnoticed
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep packages/blue-ribbon-nearby/README.md aligned with blue-ribbon-nearby/SKILL.md and docs/features/blue-ribbon-nearby.md when official surfaces or landmark aliases change
Tested: node --test scripts/skill-docs.test.js (failed first, then passed); node --test packages/blue-ribbon-nearby/test/index.test.js scripts/skill-docs.test.js; npm ci; npm run ci; live smoke searchNearbyByLocationQuery('광화문', { distanceMeters: 1000, limit: 5 }); live smoke searchNearbyByLocationQuery('코엑스', { distanceMeters: 1000, limit: 5 })
Not-tested: Separate publish automation beyond npm run ci pack:dry-run
Added a new Daiso skill and reusable workspace package around the
official Daiso Mall store search, product search, and pickup-stock
surfaces. The implementation stays stock-first because the official
surface clearly exposes store inventory but I could not verify any
official aisle/location endpoint for in-store placement.
Constraint: Must rely on official Daiso Mall surfaces before considering scraping
Constraint: Tests-first implementation and full repo CI must stay green
Rejected: Playwright-first scraping flow | official SearchGoods and selStrPkupStck endpoints were sufficient
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: If Daiso exposes an official in-store location endpoint later, extend this package instead of inferring shelf locations
Tested: node --test scripts/skill-docs.test.js
Tested: node --test packages/daiso-product-search/test/index.test.js
Tested: npm test
Tested: npm run ci
Tested: live smoke for searchStores/searchProducts/getStorePickupStock/lookupStoreProductAvailability against Daiso Mall on 2026-03-27
Not-tested: Logged-in-only variants beyond anonymous official search/store/stock surfaces
Issue #16 adds a new location-first Blue Ribbon skill, documents that 맛집 requests should route here by default, and ships a reusable npm workspace client over the official Blue Ribbon zone/search surfaces.
The package now parses official zone links, matches user-provided location text or coordinates, and queries the nearby JSON endpoint while filtering to certified ribbon venues only. Repo docs, install guidance, release metadata, and regression coverage were updated together so the new skill can ship cleanly.
Constraint: Must ask the user for current location before nearby search
Constraint: Blue Ribbon nearby calls require browser-like headers and official zone/search parameters
Rejected: Auto-detect user geolocation | violates the issue requirement to ask first
Rejected: Generic web search scraping without a reusable package | weaker reuse and less aligned with official Blue Ribbon surfaces
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep certified-ribbon filtering pinned to RIBBON_ONE/TWO/THREE unless the official Blue Ribbon contract changes
Tested: npm run ci; live smoke with searchNearbyByLocationQuery('광화문', { distanceMeters: 1000, limit: 5 })
Not-tested: Ambiguous location strings that require conversational disambiguation beyond current zone heuristics
The reserve replay search now auto-includes waiting-list candidates when users opt into --try-waiting, so the stable train_id chosen from search remains resolvable for docs-following flows. A regression test reproduces the prior stale-train failure and locks the corrected behavior.\n\nConstraint: Reserve must keep using the existing train_id replay flow without broad CLI redesign\nRejected: Require users to repeat --include-waiting-list on reserve | contradicts the documented flow and review feedback\nConfidence: high\nScope-risk: narrow\nReversibility: clean\nDirective: Keep --try-waiting and reserve replay search semantics aligned so waiting-list-only trains remain selectable\nTested: python3 -m py_compile scripts/ktx_booking.py; python3 -m unittest discover -s scripts -p test_ktx_booking.py; node --test scripts/skill-docs.test.js; npm run ci; python3 scripts/ktx_booking.py --help; python3 scripts/ktx_booking.py reserve --help; live search/reservations/reserve/cancel/reservations via sops-managed Korail credentials\nNot-tested: Live waiting-list reservation creation (no waiting-list-eligible train appeared in the smoke search output)