Merged origin/main into dev for PR #232 while preserving the dev-side contribution guide, KOSIS/Danawa CI coverage, and new workspace pack checks alongside main's Manus bundle workflow and docs.
Constraint: PR #232 targets main from dev and GitHub reported mergeable=false.
Rejected: choosing either side wholesale | would drop either dev's new skill validation or main's Manus bundle automation.
Confidence: high
Scope-risk: narrow
Directive: Keep root package scripts as the union of active workspace/package checks when resolving future branch integrations.
Tested: npm run ci
Not-tested: live GitHub mergeability after push before remote checks complete
Co-authored-by: OmX <omx@oh-my-codex.dev>
Add focused unit tests for JSON object validation, key=value decoding, and override merge behavior so the new wrapper has offline regression coverage.
Constraint: PR #229 introduces a public MCP wrapper whose live endpoint should not be required for CI coverage.
Rejected: Live MCP smoke as the only validation | upstream availability would make the regression path flaky.
Confidence: high
Scope-risk: narrow
Directive: Keep wrapper argument parsing covered without requiring network or mcp package installation.
Tested: python3 -m unittest scripts.test_myrealtrip_mcp; node --test scripts/skill-docs.test.js; ./scripts/validate-skills.sh
Not-tested: Live MyRealTrip MCP endpoint call.
Add regression coverage for missing NCard package handling and zero-based NCard selection so the KTX helper keeps clear failures around optional korail2-ncard support.
Constraint: PR #231 adds optional NCard behavior that must still be safe when korail2-ncard is not installed.
Rejected: Changing runtime NCard behavior now | existing implementation already returns explicit SystemExit messages and only lacked regression coverage.
Confidence: high
Scope-risk: narrow
Directive: Keep NCard fallback behavior tested separately from normal korail2 imports.
Tested: python3 -m pytest scripts/test_ktx_booking.py -q
Not-tested: Live Korail NCard reservation against production account.
Prefer N-card selection by list index so full card numbers are not echoed through JSON output or required in shell history. Keep direct card-number input as a compatibility escape hatch with an explicit warning.\n\nConstraint: PR #231 scope is limited to ktx-booking docs, helper, and tests.\nRejected: Require users to copy full card numbers from ncard-list | exposes sensitive identifiers in logs and shell history.\nConfidence: high\nScope-risk: narrow\nDirective: Keep N-card list outputs masked; prefer index-based selection for future reservation flows.\nTested: python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py; PYTHONPATH=scripts python3 -m unittest scripts.test_ktx_booking; npm run lint; npm run typecheck; npm test\nNot-tested: Live Korail N-card reservation; requires real user credentials and owned N-card.
- ncard-list: 보유 N카드 목록 조회 (owned_ncards)
- ncard-search: N카드 할인 열차 조회 (search_owned_ncard_trains)
- reserve --ncard-no: N카드 번호로 할인 승객 예약
- NCardPassenger는 별도 try/except ImportError 블록으로 분리해
표준 korail2 환경에서도 모듈이 정상 로드되도록 처리
- korail2-ncard 미설치 시 N카드 커맨드에서 설치 안내 출력
- 관련 테스트 7개 추가 (총 18개)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add Manus.ai GitHub skill import guide
Manus.ai의 'GitHub에서 프로젝트 스킬 가져오기' 기능은 폴더 루트에 SKILL.md(YAML frontmatter name/description 필수)가 있는 디렉토리 URL을 받는다. k-skill의 모든 스킬은 이미 이 포맷을 만족하므로 코드 변경 없이 문서만 추가한다.
- 사용자는 저장소 루트 URL(https://github.com/NomaDamas/k-skill) 대신 개별 스킬 폴더 URL(https://github.com/NomaDamas/k-skill/tree/main/<skill-name>)을 붙여 넣어야 한다.
- 기존 frontmatter(license, metadata.*)는 Manus가 무시하지만 다른 코딩 에이전트와의 호환을 위해 그대로 유지한다.
* feat: add build:manus-bundle for batch .skill upload to Manus.ai
Per-folder GitHub URL import is tedious for 61 skills, so add 'npm run build:manus-bundle' which emits one .skill (ZIP) per skill into dist/manus/, plus a single k-skill-manus-all.zip convenience bundle and an INDEX.md listing. Each archive nests its content under <skill-name>/ to match the public Anthropic skill-creator packager layout.
Manus does NOT support multi-skill bulk import in a single archive (verified against help.manus.im, manus.im/docs, and open.manus.ai API docs). The combined zip is purely a download convenience: users still drag-drop individual .skill files into Manus, but the file picker accepts multiple selections so it's still much faster than pasting 61 GitHub URLs.
- scripts/build-manus-bundle.js: discovers root-level skills (mirrors validate-skills.sh exclusions), shells out to system zip with -X for reproducible archives, excludes node_modules/__pycache__/.DS_Store.
- scripts/test_build_manus_bundle.js: validates discovery, frontmatter parsing, lockstep with validate-skills.sh, and docs coverage.
- scripts/validate-skills.sh: also skip dist/ and .sisyphus/ so the validator stays clean after a build.
- .gitignore: ignore dist/ and .sisyphus/.
- docs/install-manus.md: document both Method A (GitHub URL) and Method B (.skill bundle).
* ci: auto-publish Manus .skill bundle as rolling release on main push
Every push to main that touches a skill folder or the bundler now builds the .skill bundle and publishes it to the GitHub Releases tag 'manus-bundle-latest' (marked prerelease so it does not pollute the Latest release pointer used by the npm release flow).
Users get stable download URLs that always point to the latest build:
- https://github.com/NomaDamas/k-skill/releases/download/manus-bundle-latest/k-skill-manus-all.zip
- https://github.com/NomaDamas/k-skill/releases/download/manus-bundle-latest/INDEX.md
This removes the 'clone the repo and run npm' step for non-developers. The direct-build path remains documented as the developer fallback.
- .github/workflows/manus-bundle.yml: workflow_dispatch + push-to-main with paths filter, uses preinstalled gh CLI (no third-party release action), concurrency-grouped so overlapping pushes do not race on the same tag, --clobber upload to keep asset URLs stable.
- docs/install-manus.md: new 'quick path' section with the rolling-release URLs; existing local-build section reframed as a developer fallback.
- scripts/test_build_manus_bundle.js: 2 new tests pinning the doc URLs and key workflow invariants (trigger branch, build invocation, tag, asset name, prerelease flag, write permission).
* Help donors choose verified recipients by place and cause
Add a read-only donation-place search skill and npm helper that ranks Korean donation recipients by user-provided location/category while keeping final verification on official 1365 and recipient pages. The implementation avoids proxy routes because the chosen verification surface is public and does not require an API key.
Constraint: Issue #212 requested 기부처 조회 recommendations by place and category under TDD with a PR to dev.
Constraint: k-skill free API proxy policy allows proxying only when upstream requires API keys; 1365 verification links are public.
Rejected: Screen-scraping 1365 result pages | headless requests were slow/unstable and would be brittle for a recommendation helper.
Rejected: Treating general-purpose charities as matches for every requested category | architect review found it could return off-category results, so matching now requires explicit category tags.
Confidence: high
Scope-risk: narrow
Directive: Do not add automatic donation/payment submission; keep this skill read-only and require official-page verification before final donation decisions.
Tested: npm test --workspace donation-place-search
Tested: node smoke invocation of recommendDonationPlaces + formatDonationRecommendationReport for 서울 마포구/동물
Tested: npm run lint --workspace donation-place-search
Tested: npm run typecheck
Tested: npm run ci
Tested: architect verification approved after off-category regression fix
Not-tested: Live 1365 search result scraping; intentionally not used because the skill returns official verification links instead.
Co-authored-by: OmX <omx@oh-my-codex.dev>
* Keep donation recommendations on requested intent
Prioritize specific donation category keywords before broad general donation terms, and make item-level 1365 links candidate-specific while preserving the broad result search link.
Constraint: PR #214 review required TDD fixes for category normalization and per-candidate 1365 link semantics.
Rejected: Rewording item URLs as broad portal searches | the issue explicitly asks for candidate-specific verification links.
Confidence: high
Scope-risk: narrow
Directive: Keep item officialSearchUrl candidate-specific; use result officialSearchUrl for broad latest portal searches.
Tested: npm test --workspace donation-place-search; node smoke invocation; npm run lint --workspace donation-place-search; npm run typecheck; npm run ci; code-reviewer APPROVE; architect CLEAR.
Not-tested: Live 1365 HTTP availability, because the workflow only builds official read-only search links and prior review documented headless 1365 timeouts.
* Harden donation skill follow-up guarantees
Constraint: PR #214 review follow-up required TDD, empty category defaults, README discoverability, and release-pack coverage without pinning package versions.\nRejected: Static pack dry-run allowlist | it already missed a publishable workspace and would drift again.\nConfidence: high\nScope-risk: narrow\nDirective: Keep pack dry-run coverage dynamic over publishable workspaces; do not assert workspace package versions in tests.\nTested: npm test --workspace donation-place-search; node smoke for empty category URL/recommend/report; npm run lint --workspace donation-place-search; npm run typecheck; npm run ci; git diff --check; code-reviewer APPROVE; architect CLEAR.\nNot-tested: Live 1365 portal filtering semantics, by design; links remain read-only verification entry points.
* Clarify donation verification links
Reject misleading 1365 URL contracts and keep item search categories aligned with the candidate that is being recommended.
Constraint: PR #214 round-3 review required TDD fixes for multi-category candidate links, clean install docs, and evidence-safe 1365 wording.
Rejected: Keep broad first-request category on every item URL | It mislabels later-category candidates in multi-category requests.
Rejected: Preserve public baseUrl override | It conflicts with the official 1365 helper contract.
Confidence: high
Scope-risk: narrow
Directive: Keep 1365 URLs framed as best-effort verification assists unless browser-observed 1365 search parameters are documented.
Tested: npm test --workspace donation-place-search; node --test --test-name-pattern 'donation-place-search' scripts/skill-docs.test.js; npm run lint --workspace donation-place-search; npm run typecheck; npm run ci; node smoke for multi-category URLs, malformed limits, baseUrl rejection, and empty category.
Not-tested: Live 1365 parameter behavior; headless HTTP remains documented as unreliable.
Co-authored-by: OmX <omx@oh-my-codex.dev>
---------
Co-authored-by: OmX <omx@oh-my-codex.dev>
Add Workflow C for court-auction-notice-search with direct PGJ151 property search payload mapping, representative frozen code tables, CLI/docs coverage, and normalized item rows.
Constraint: Issue #184 requires Workflow C region/usage/price/date/area/flbd filters and release automation requires a Changeset.
Rejected: Proxy route | courtauction.go.kr property search is a public site endpoint and does not require an API key.
Confidence: high
Scope-risk: moderate
Directive: Keep code-table lookups fail-open and avoid tests that pin package versions or changeset file presence.
Tested: npm test --workspace court-auction-notice-search; npm run lint --workspace court-auction-notice-search; npm run ci
Not-tested: Live courtauction.go.kr property search, to avoid unnecessary upstream calls and potential anti-bot blocking.
* Align proxy defaults for hosted Korean routes
Constraint: Issue #205 requires unset or empty KSKILL_PROXY_BASE_URL to use the hosted proxy consistently while preserving explicit proxy overrides and server-side upstream keys.\nRejected: Keeping Seoul subway and Korea weather as self-host-only routes | it preserves the documented inconsistency and blocks zero-config usage.\nConfidence: high\nScope-risk: narrow\nDirective: Keep client docs pointing to hosted proxy defaults unless a route is intentionally removed from hosted service.\nTested: node --test scripts/skill-docs.test.js; npm run ci; hosted smoke curls for /v1/seoul-subway/arrival and /v1/korea-weather/forecast; architect verification approved.\nNot-tested: Private self-host proxy deployment.
* Preserve hosted proxy fallback in setup guidance
Make self-host proxy examples inactive by default so client setup no longer blocks the hosted proxy resolver contract for Seoul subway and Korea weather skills.
Constraint: PR #210 review required unset and empty KSKILL_PROXY_BASE_URL to fall back to https://k-skill-proxy.nomadamas.org while preserving explicit self-host overrides.\nRejected: Keep active https://your-proxy.example.com placeholder | It creates a non-empty override and prevents hosted fallback for users copying the default secrets file.\nConfidence: high\nScope-risk: narrow\nDirective: Keep upstream API keys documented as proxy-operator/server-side only; do not reintroduce active client-side proxy placeholders for hosted-default flows.\nTested: node --test scripts/skill-docs.test.js; npm run ci; hosted smoke checks for /v1/seoul-subway/arrival?stationName=강남 and /v1/korea-weather/forecast?lat=37.5665&lon=126.9780; resolver smoke for unset, empty, and custom KSKILL_PROXY_BASE_URL; git diff --check; Ralph architect verification CLEAR.\nNot-tested: none
* Clarify proxy guide override boundary
Document the hosted-client default in the proxy guide while keeping the self-host placeholder as an explicitly scoped override, so users do not mistake it for required setup.
Constraint: Issue #205 review round 2 left a WATCH concern on docs/features/k-skill-proxy.md client env-var wording.
Rejected: Leave the proxy guide unchanged | It preserved ambiguity between hosted-client defaults and self-host/operator overrides.
Rejected: Sentence-exact regression assertions | They were too brittle after code review; semantic assertions preserve wording flexibility while locking the policy.
Confidence: high
Scope-risk: narrow
Directive: Keep KSKILL_PROXY_BASE_URL examples inactive or clearly scoped unless documenting a self-host/alternate-proxy override.
Tested: node --test scripts/skill-docs.test.js; npm run ci; resolver smoke for unset empty and custom KSKILL_PROXY_BASE_URL; hosted Seoul subway and Korea weather smokes; git diff --check; code-reviewer APPROVE; architect CLEAR
Not-tested: none
* Keep proxy guide examples on the hosted-default path
Constraint: PR #210 issue #205 follow-up requires KSKILL_PROXY_BASE_URL unset/empty to resolve to hosted while preserving explicit self-host overrides.\nRejected: Labeling the existing 127.0.0.1 examples as operator-only | it would leave the general usage section less aligned with the hosted-client default.\nConfidence: high\nScope-risk: narrow\nDirective: Keep Seoul subway and Korea weather user-facing examples on the resolver pattern unless the section is explicitly scoped to local operator smoke tests.\nTested: node --test scripts/skill-docs.test.js; npm run ci; resolver smoke for unset empty custom KSKILL_PROXY_BASE_URL; hosted Seoul subway and Korea weather smoke; architect verification CLEAR.\nNot-tested: None.
* chore: version packages
* Merge dev into main (#197)
* fix(toss-securities): clarify session expiry and quote 403 handling
* Clarify toss empty-output session expiry
Portfolio and watchlist reads can exit successfully with empty payloads when the stored Toss session has expired. The empty-output path now verifies the session before JSON parsing and only promotes confirmed invalid auth doctor data into TossSessionExpiredError.
Constraint: Scope is limited to toss-securities issue #126 follow-up on PR #192
Rejected: Treat auth doctor execution failures as expired sessions | unsupported or failing doctor output is inconclusive without parsed session.valid=false
Confidence: high
Scope-risk: narrow
Directive: Keep empty-result session expiry classification tied to explicit auth doctor confirmation
Tested: npm run test --workspace toss-securities; npm run lint --workspace toss-securities; npm run ci; manual mock tossctl blank stdout invalid/inconclusive doctor checks
* Avoid false session-expiry labels for validation errors
The toss wrapper now treats bare validation_error text as an upstream command failure instead of a session-expired signal. Structured auth doctor JSON remains the source of truth for empty portfolio/watchlist invalid-session promotion, while known stored-session-invalid stderr still maps to TossSessionExpiredError.\n\nConstraint: PR #192 follow-up must stay scoped to issue #126 toss-securities behavior.\nRejected: Keep validation_error in the global regex | it mislabels auth doctor transport failures and quote 403 validation errors as session expiry.\nConfidence: high\nScope-risk: narrow\nDirective: Do not broaden the free-text session classifier without regressions for auth doctor and quote upstream validation failures.\nTested: npm run lint --workspace toss-securities; npm run test --workspace toss-securities; npm run ci; manual mock tossctl validation_error checks; architect verification CLEAR\nNot-tested: Live tossctl network/auth session against real Toss upstream
* Align court auction lookup with monthly site search (#196)
The court auction notice page posts a YYYYMM search key from its 조회 button and returns a month of rows. Keep day inputs as a compatibility filter over the monthly response and normalize the current nested detail payload shape.
Constraint: courtauction.go.kr has no public API and blocks bursty automated calls.
Rejected: querying every day independently | the upstream search surface is month-based and day calls return false empty results.
Confidence: high
Scope-risk: narrow
Directive: Preserve the site-observed YYYYMM notice search contract unless the PGJ143M01 XHR changes again.
Tested: npm --workspace packages/court-auction-notice-search test; npm run ci; live 서울중앙지방법원 2026-05 notice/detail smoke lookup.
Not-tested: PR CI after push.
Co-authored-by: OmX <omx@oh-my-codex.dev>
* Guide crawler skills toward reusable discovery (#195)
* chore: version packages
* Guide crawler skills toward reusable discovery
Constraint: User requested insane-search-style guidance for future crawling k-skills without unrelated implementation changes.
Rejected: Adding crawler code or a standalone template | too broad for a docs guidance change and risks dependency creep.
Confidence: high
Scope-risk: narrow
Directive: Keep site-specific access details inside individual skills after a site-agnostic discovery pass.
Tested: npm run ci
Not-tested: Live crawler behavior; documentation-only change.
* Clarify crawler skill discovery guidance
Constraint: Crawling k-skills need site-dependent recipes, but should derive them through a reusable discovery pass.
Rejected: Leaving guidance only in docs/adding-a-skill.md | AGENTS.md and CLAUDE.md also guide future agents.
Confidence: high
Scope-risk: narrow
Directive: Use site-agnostic discovery to find, then explicitly package, the target site's stable access path.
Tested: npm run ci
Not-tested: Live crawler behavior; documentation-only change.
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Ground corporate registration guidance in official form sources
Keep the consulting skill focused on draft/checklist support while pointing users to current IROS and law.go.kr form sources for submission-ready artifacts.
Constraint: official registry forms can change outside the repository and must be re-downloaded at use time
Rejected: committing copied official HWP/HWPX/PDF forms | they would become stale and risk misleading users
Confidence: high
Scope-risk: narrow
Directive: do not treat Markdown templates as substitutes for official registry submission forms
Tested: npm test
* Ground incorporation drafting in real HWP forms
Bundle official court incorporation forms plus public startup incorporation attachments, and make rhwp-filled HWP outputs the default drafting path for the corporate-registration skill. Replace the listed-company articles reference with a startup-suitable Ministry of Justice stock-company form and record source manifests for bundled binaries.
Constraint: user requires actual sourced HWP templates, not generated placeholder binaries.
Rejected: markdown-only drafting | it cannot produce submission-shaped Korean registry forms.
Rejected: listed-company standard articles as the default reference | it is mismatched for typical startup incorporation.
Confidence: high
Scope-risk: moderate
Directive: keep bundled HWP forms source-backed, sanitized, and edited only through copied working files.
Tested: node --test scripts/skill-docs.test.js; npm run lint; k-skill-rhwp info on bundled HWP files; kordoc conversion spot checks.
Not-tested: manual opening every HWP in Hancom Office and live registry submission.
Co-authored-by: OmX <omx@oh-my-codex.dev>
* Streamline corporate registration forms workflow
Prioritize saved HWP forms for ordinary stock-company promoter incorporations, make required court-registry receipts and director identity certificates explicit, and remove the redundant markdown articles template so the skill stays HWP-first.
Constraint: 법원등기소 기준 체크리스트 must include fee receipts, director seal/signature certificates, and resident-record documents.
Rejected: Keeping a separate markdown articles template | duplicated the stored HWP articles workflow and encouraged non-HWP drafting.
Confidence: high
Scope-risk: narrow
Directive: Keep corporate-registration-consulting focused on stored HWP form copies and explicit issued-document checklists.
Tested: node --test --test-name-pattern 'corporate-registration-consulting' scripts/skill-docs.test.js; node --check scripts/skill-docs.test.js; ./scripts/validate-skills.sh; git diff --check
Not-tested: Full npm run ci was not run because this is a skill documentation/template refactor, not release or package automation.
---------
Co-authored-by: galvaomica <galvaomica@galvaomicaui-MacBookAir.local>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: version packages (#198)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(realtyprice): add address parsing and sido code mapping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(realtyprice): use string sido codes for consistency with upstream API
* feat(realtyprice): add response normalization and buildResponse
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(realtyprice): add upstream cascade fetch functions with timeout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(realtyprice): add lookupGongsijiga orchestrator with region matching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(realtyprice): add simple in-memory cache with TTL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(proxy): register GET /realtyprice route with caching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add gongsijiga-search SKILL.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: add changeset for gongsijiga-search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(realtyprice): align with actual realtyprice.kr API response format
- Response wraps data in model.list (not bjdList/gsiList)
- Field names are code/name (not bjd_cd/bjd_nm)
- bun2 empty → send "0000" (not empty string)
- eupmyeondong matching: try full string match first (API returns
combined "면 리" names like "청계면 청천리")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(gongsijiga-search): align /realtyprice route with v1 API convention
- Change route from /realtyprice to /v1/realtyprice for consistency with other proxy endpoints.
- Add realtypriceConfigured flag to /health upstreams.
- Normalize address cache key by collapsing multiple whitespaces.
- Update SKILL.md and README.md to reflect the new v1 path.
* feat(gongsijiga-search): add Sejong special-city support
- parseAddress: allow 3-token minimum for Sejong (no sigungu) and set sigungu to empty string.
- lookupGongsijiga: skip sigungu lookup for Sejong (sidoCode 29), use fixed sggCode 36110.
- Add Sejong parseAddress and lookupGongsijiga test cases.
- Update SKILL.md with Sejong address format examples.
* refactor(gongsijiga-search): split realtyprice.kr lookup into standalone package
realtyprice.kr is a fully public endpoint that needs no API key, so per
the new k-skill-proxy inclusion rule (proxy is for keyed upstreams only)
the helper now ships as `gongsijiga-search` and is invoked directly from
the user's machine.
- new workspace package packages/gongsijiga-search/ following the
blue-ribbon-nearby/coupang-product-search convention (publishConfig,
files, repository, keywords)
- remove /v1/realtyprice route, realtyprice.js, realtyprice.test.js, and
the realtypriceConfigured health flag from k-skill-proxy
- document the inclusion rule in AGENTS.md and CLAUDE.md so future skills
default to direct calls when no key is required
- advertise the new skill in README.md, docs/install.md, and add
docs/features/gongsijiga-search.md
- drop the hardcoded toss-securities lockfile version assertion that
pinned a workspace version (would block changesets version-packages)
and document the anti-pattern in AGENTS.md / CLAUDE.md
- changesets: refresh the proxy refactor message and add a patch
changeset so the new gongsijiga-search package gets published
---------
Co-authored-by: Jeffrey (Dongkyu) Kim <vkehfdl1@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: galvaomica <galvaomica@galvaomicaui-MacBookAir.local>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(toss-securities): clarify session expiry and quote 403 handling
* Clarify toss empty-output session expiry
Portfolio and watchlist reads can exit successfully with empty payloads when the stored Toss session has expired. The empty-output path now verifies the session before JSON parsing and only promotes confirmed invalid auth doctor data into TossSessionExpiredError.
Constraint: Scope is limited to toss-securities issue #126 follow-up on PR #192
Rejected: Treat auth doctor execution failures as expired sessions | unsupported or failing doctor output is inconclusive without parsed session.valid=false
Confidence: high
Scope-risk: narrow
Directive: Keep empty-result session expiry classification tied to explicit auth doctor confirmation
Tested: npm run test --workspace toss-securities; npm run lint --workspace toss-securities; npm run ci; manual mock tossctl blank stdout invalid/inconclusive doctor checks
* Avoid false session-expiry labels for validation errors
The toss wrapper now treats bare validation_error text as an upstream command failure instead of a session-expired signal. Structured auth doctor JSON remains the source of truth for empty portfolio/watchlist invalid-session promotion, while known stored-session-invalid stderr still maps to TossSessionExpiredError.\n\nConstraint: PR #192 follow-up must stay scoped to issue #126 toss-securities behavior.\nRejected: Keep validation_error in the global regex | it mislabels auth doctor transport failures and quote 403 validation errors as session expiry.\nConfidence: high\nScope-risk: narrow\nDirective: Do not broaden the free-text session classifier without regressions for auth doctor and quote upstream validation failures.\nTested: npm run lint --workspace toss-securities; npm run test --workspace toss-securities; npm run ci; manual mock tossctl validation_error checks; architect verification CLEAR\nNot-tested: Live tossctl network/auth session against real Toss upstream
* Align court auction lookup with monthly site search (#196)
The court auction notice page posts a YYYYMM search key from its 조회 button and returns a month of rows. Keep day inputs as a compatibility filter over the monthly response and normalize the current nested detail payload shape.
Constraint: courtauction.go.kr has no public API and blocks bursty automated calls.
Rejected: querying every day independently | the upstream search surface is month-based and day calls return false empty results.
Confidence: high
Scope-risk: narrow
Directive: Preserve the site-observed YYYYMM notice search contract unless the PGJ143M01 XHR changes again.
Tested: npm --workspace packages/court-auction-notice-search test; npm run ci; live 서울중앙지방법원 2026-05 notice/detail smoke lookup.
Not-tested: PR CI after push.
Co-authored-by: OmX <omx@oh-my-codex.dev>
* Guide crawler skills toward reusable discovery (#195)
* chore: version packages
* Guide crawler skills toward reusable discovery
Constraint: User requested insane-search-style guidance for future crawling k-skills without unrelated implementation changes.
Rejected: Adding crawler code or a standalone template | too broad for a docs guidance change and risks dependency creep.
Confidence: high
Scope-risk: narrow
Directive: Keep site-specific access details inside individual skills after a site-agnostic discovery pass.
Tested: npm run ci
Not-tested: Live crawler behavior; documentation-only change.
* Clarify crawler skill discovery guidance
Constraint: Crawling k-skills need site-dependent recipes, but should derive them through a reusable discovery pass.
Rejected: Leaving guidance only in docs/adding-a-skill.md | AGENTS.md and CLAUDE.md also guide future agents.
Confidence: high
Scope-risk: narrow
Directive: Use site-agnostic discovery to find, then explicitly package, the target site's stable access path.
Tested: npm run ci
Not-tested: Live crawler behavior; documentation-only change.
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Ground corporate registration guidance in official form sources
Keep the consulting skill focused on draft/checklist support while pointing users to current IROS and law.go.kr form sources for submission-ready artifacts.
Constraint: official registry forms can change outside the repository and must be re-downloaded at use time
Rejected: committing copied official HWP/HWPX/PDF forms | they would become stale and risk misleading users
Confidence: high
Scope-risk: narrow
Directive: do not treat Markdown templates as substitutes for official registry submission forms
Tested: npm test
* Ground incorporation drafting in real HWP forms
Bundle official court incorporation forms plus public startup incorporation attachments, and make rhwp-filled HWP outputs the default drafting path for the corporate-registration skill. Replace the listed-company articles reference with a startup-suitable Ministry of Justice stock-company form and record source manifests for bundled binaries.
Constraint: user requires actual sourced HWP templates, not generated placeholder binaries.
Rejected: markdown-only drafting | it cannot produce submission-shaped Korean registry forms.
Rejected: listed-company standard articles as the default reference | it is mismatched for typical startup incorporation.
Confidence: high
Scope-risk: moderate
Directive: keep bundled HWP forms source-backed, sanitized, and edited only through copied working files.
Tested: node --test scripts/skill-docs.test.js; npm run lint; k-skill-rhwp info on bundled HWP files; kordoc conversion spot checks.
Not-tested: manual opening every HWP in Hancom Office and live registry submission.
Co-authored-by: OmX <omx@oh-my-codex.dev>
* Streamline corporate registration forms workflow
Prioritize saved HWP forms for ordinary stock-company promoter incorporations, make required court-registry receipts and director identity certificates explicit, and remove the redundant markdown articles template so the skill stays HWP-first.
Constraint: 법원등기소 기준 체크리스트 must include fee receipts, director seal/signature certificates, and resident-record documents.
Rejected: Keeping a separate markdown articles template | duplicated the stored HWP articles workflow and encouraged non-HWP drafting.
Confidence: high
Scope-risk: narrow
Directive: Keep corporate-registration-consulting focused on stored HWP form copies and explicit issued-document checklists.
Tested: node --test --test-name-pattern 'corporate-registration-consulting' scripts/skill-docs.test.js; node --check scripts/skill-docs.test.js; ./scripts/validate-skills.sh; git diff --check
Not-tested: Full npm run ci was not run because this is a skill documentation/template refactor, not release or package automation.
---------
Co-authored-by: galvaomica <galvaomica@galvaomicaui-MacBookAir.local>
Co-authored-by: OmX <omx@oh-my-codex.dev>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Prioritize saved HWP forms for ordinary stock-company promoter incorporations, make required court-registry receipts and director identity certificates explicit, and remove the redundant markdown articles template so the skill stays HWP-first.
Constraint: 법원등기소 기준 체크리스트 must include fee receipts, director seal/signature certificates, and resident-record documents.
Rejected: Keeping a separate markdown articles template | duplicated the stored HWP articles workflow and encouraged non-HWP drafting.
Confidence: high
Scope-risk: narrow
Directive: Keep corporate-registration-consulting focused on stored HWP form copies and explicit issued-document checklists.
Tested: node --test --test-name-pattern 'corporate-registration-consulting' scripts/skill-docs.test.js; node --check scripts/skill-docs.test.js; ./scripts/validate-skills.sh; git diff --check
Not-tested: Full npm run ci was not run because this is a skill documentation/template refactor, not release or package automation.
Bundle official court incorporation forms plus public startup incorporation attachments, and make rhwp-filled HWP outputs the default drafting path for the corporate-registration skill. Replace the listed-company articles reference with a startup-suitable Ministry of Justice stock-company form and record source manifests for bundled binaries.
Constraint: user requires actual sourced HWP templates, not generated placeholder binaries.
Rejected: markdown-only drafting | it cannot produce submission-shaped Korean registry forms.
Rejected: listed-company standard articles as the default reference | it is mismatched for typical startup incorporation.
Confidence: high
Scope-risk: moderate
Directive: keep bundled HWP forms source-backed, sanitized, and edited only through copied working files.
Tested: node --test scripts/skill-docs.test.js; npm run lint; k-skill-rhwp info on bundled HWP files; kordoc conversion spot checks.
Not-tested: manual opening every HWP in Hancom Office and live registry submission.
Co-authored-by: OmX <omx@oh-my-codex.dev>
Keep the consulting skill focused on draft/checklist support while pointing users to current IROS and law.go.kr form sources for submission-ready artifacts.
Constraint: official registry forms can change outside the repository and must be re-downloaded at use time
Rejected: committing copied official HWP/HWPX/PDF forms | they would become stale and risk misleading users
Confidence: high
Scope-risk: narrow
Directive: do not treat Markdown templates as substitutes for official registry submission forms
Tested: npm test
* Protect explicit Korail train-type replay
Issue #171 added explicit train-type selection for non-KTX Korail routes. The merged implementation already wires search and reserve through TRAIN_TYPE_MAP; this follow-up locks that behavior with command-level and parser regression coverage, removes stale test import noise, and aligns the skill shortlist wording with the broader train_type output.
Constraint: The ktx-booking skill keeps ktx as the default train type for backward compatibility.
Rejected: Encode train type into train_id in this follow-up | larger selector schema change already marked as non-blocking UX follow-up.
Confidence: high
Scope-risk: narrow
Directive: Do not hardcode command_search or command_reserve back to TrainType.KTX; keep parser choices derived from TRAIN_TYPE_MAP.
Tested: python3 scripts/ktx_booking.py search --help
Tested: python3 scripts/ktx_booking.py reserve --help
Tested: PYTHONPATH=scripts python3 -m unittest scripts.test_ktx_booking
Tested: python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py
Tested: PYTHONNOUSERSITE=1 PYTHONPATH=scripts python3 -S -c 'import ktx_booking; print(ktx_booking._KORAIL_IMPORT_ERROR); print(ktx_booking.TRAIN_TYPE_MAP["itx-cheongchun"])'
Tested: PYTHONNOUSERSITE=1 PYTHONPATH=scripts python3 -m unittest discover -s scripts -p 'test_ktx_booking.py'
Tested: python3 -S scripts/ktx_booking.py search --help; python3 -S scripts/ktx_booking.py reserve --help
Tested: npm run lint
Tested: npm run typecheck
Tested: npm test
Not-tested: Live Korail search/reserve requiring credentials and external availability
* Guard KTX search default during train-type regression
Issue #171 locked explicit train-type choices for Korail search and reserve flows. The follow-up review found reserve default coverage but no direct search default assertion, so this adds the narrow parser regression without changing runtime behavior.\n\nConstraint: Issue #171 requires search and reserve train-type behavior to stay regression-covered.\nRejected: Broaden command-level tests for every train type | parser choices already loop over TRAIN_TYPE_MAP and command forwarding is covered for the non-KTX regression route.\nConfidence: high\nScope-risk: narrow\nTested: PYTHONPATH=scripts python3 -m unittest scripts.test_ktx_booking\nTested: python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py\nTested: npm run lint\nTested: npm run typecheck\nTested: npm test\nNot-tested: live Korail search/reserve requiring credentials and external service availability
* Tighten KTX train-type regression tests
The Issue #171 follow-up already locked the train-type CLI behavior. This pass addresses the remaining review cleanup in the modified test file by narrowing the normalized train_id before reuse and tidying formatting without changing behavior.
Constraint: Keep the PR scoped to ktx-booking regression coverage and documentation
Rejected: Encode train_type into train_id in this follow-up | broader selector migration is outside the approved regression scope
Confidence: high
Scope-risk: narrow
Tested: PYTHONPATH=scripts python3 -m unittest scripts.test_ktx_booking
Tested: python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py
Tested: pyright scripts/test_ktx_booking.py
Tested: npm run lint
Tested: npm run typecheck
Tested: npm test
Not-tested: live Korail search/reserve requiring credentials and external service availability
The fallback TrainType class only defined ALL/KTX, but TRAIN_TYPE_MAP
references ITX_SAEMAEUL, MUGUNGHWA, NURIRO, TONGGUEN, ITX_CHEONGCHUN,
and AIRPORT at import time, so the module crashed with AttributeError
in environments where korail2 is missing. This bypassed the controlled
guidance from ensure_runtime_dependencies() and broke even `--help`.
Mirror upstream korail2.TrainType numeric IDs in the fallback so the
import-time TRAIN_TYPE_MAP construction succeeds and the helper can
surface its install message. Add regression tests that exercise the
missing-korail2 path via subprocess to lock the behavior in.
Addresses round 2 review on #172.
Promoted upstream jangbu skills now preflight every Claude and agents top-level destination before the installer mutates home skill directories. This keeps unrelated user-authored skills from causing a mixed partial discovery state, while preserving the existing managed-marker overwrite path and the explicit override escape hatch.
The installer also prints the namespace re-sync warning next to the upstream runtime install command so users know to restore wrapper-managed top-level skills after running upstream's Claude-only installer.
Constraint: Upstream skill contents must be checked out before collision preflight can validate promoted SKILL.md files.
Rejected: Roll back cache checkout on collision | cache writes are outside the advertised home skill discovery namespace and are needed to inspect pinned upstream content.
Confidence: high
Scope-risk: narrow
Directive: Keep promoted-skill collision checks before install_wrapper_payload and sync_dir calls for home skill roots.
Tested: bash -n korean-jangbu-for/scripts/install.sh
Tested: node --test scripts/skill-docs.test.js --test-name-pattern='korean-jangbu-for'
Tested: temp HOME real pinned upstream install and .agents jangbu-tax collision preflight smoke
Tested: npm run ci
The promoted upstream subskills need to be discoverable without making home installs destructive or incomplete. The wrapper installer now copies its support payload into both home skill roots, allows installed-wrapper reruns, and refuses to overwrite unrelated top-level jangbu-* skills unless explicitly overridden.
Constraint: PR #181 review requires top-level subskill discovery under both Claude and agents roots.
Constraint: Home installs must remain re-runnable without a source checkout.
Rejected: Continue using generic sync_dir for promoted skills | it silently deletes unrelated user-authored skills.
Confidence: high
Scope-risk: narrow
Directive: Do not bypass the promoted-skill ownership check without preserving unrelated home skill directories.
Tested: node --test scripts/skill-docs.test.js --test-name-pattern='korean-jangbu-for'
Tested: bash -n korean-jangbu-for/scripts/install.sh
Tested: bash korean-jangbu-for/scripts/install.sh plus installed ~/.claude and ~/.agents wrapper reruns
Tested: bash ~/.claude/skills/korean-jangbu-for/upstream/scripts/install.sh and verify.sh with Python 3.11 shim
Tested: npm run ci
Tested: Architect verification APPROVED
Not-tested: Live CODEF collection requiring user BYOK credentials and external authentication
The wrapper advertised upstream /jangbu-* routing for both Claude and agent-compatible installs, but only nested the pinned upstream checkout. The installer now registers the upstream subskills at top level in both home skill roots while appending the wrapper attribution and accounting disclaimer policy to direct subskill use.
Constraint: PR review required top-level discovery for ~/.claude/skills and ~/.agents/skills
Constraint: korean-jangbu-for responses must retain original link, @kimlawtech (SpeciAI), Apache-2.0, and accounting/tax disclaimer
Rejected: Copy raw upstream subskills unchanged | direct /jangbu-* use would bypass mandatory wrapper response policy
Confidence: high
Scope-risk: narrow
Tested: node --test scripts/skill-docs.test.js --test-name-pattern='korean-jangbu-for'
Tested: bash korean-jangbu-for/scripts/install.sh plus top-level Claude/agents subskill marker checks
Tested: upstream runtime install and verify with Python 3.11 shim
Tested: npm run ci
Issue #173 needs a k-skill entry point centered on kimlawtech/korean-jangbu-for while preserving upstream attribution and accounting/tax disclaimers. Add a thin wrapper with pinned upstream install, bundled Apache license/disclaimer/notice, docs, and regression coverage for the required original link and @kimlawtech (SpeciAI) mention.\n\nConstraint: Upstream implementation remains in kimlawtech/korean-jangbu-for and is installed by pinned SHA instead of vendoring runtime payload into k-skill\nConstraint: Generated accounting/tax outputs are reference drafts, not official audits or tax filings\nRejected: Reimplement ledger automation locally | would duplicate upstream and widen maintenance scope\nConfidence: high\nScope-risk: narrow\nDirective: Keep responses for this skill citing https://github.com/kimlawtech/korean-jangbu-for and @kimlawtech (SpeciAI); do not remove tax/accounting disclaimers\nTested: node --test scripts/skill-docs.test.js --test-name-pattern='korean-jangbu-for'\nTested: bash korean-jangbu-for/scripts/install.sh\nTested: bash ~/.claude/skills/korean-jangbu-for/upstream/scripts/install.sh with Python 3.11 PATH shim\nTested: bash ~/.claude/skills/korean-jangbu-for/upstream/scripts/verify.sh\nTested: npm run ci\nNot-tested: CODEF live collection flow; requires user BYOK credentials and external auth
The cleanup helper now streams local logs, reports which evidence sources were merged, and keeps README table coverage tied to the central skill-name fixture so the documented cleanup signal stays trustworthy for large local histories and mixed imported counts.
Constraint: Follow-up addresses PR #178 review comments without changing the non-destructive recommendation model.
Rejected: Filtering imported usage JSON by --days inside the helper | imported counts are already aggregated and lack per-record timestamps.
Confidence: high
Scope-risk: narrow
Directive: Keep --usage-json documented as pre-windowed unless the input schema gains timestamped per-record events.
Tested: PYTHONPATH=scripts python3 -m unittest scripts.test_k_skill_cleaner
Tested: node --test scripts/skill-docs.test.js
Tested: npm run lint
Tested: npm run typecheck && npm test
Tested: npm run ci
The standalone helper is advertised from inside the k-skill-cleaner directory, so --skills-root . now resolves that self-skill directory to its parent skills root before scanning siblings. A subprocess regression locks the installed-skill layout that previously returned skill_count 0.\n\nConstraint: Preserve the documented standalone command without requiring users to switch to --skills-root ..\nRejected: Documentation-only fix | would make the advertised command more brittle and leave existing users with empty reports\nConfidence: high\nScope-risk: narrow\nDirective: Keep standalone helper invocation from inside k-skill-cleaner covered when changing root detection\nTested: PYTHONPATH=scripts python3 -m unittest scripts.test_k_skill_cleaner\nTested: Standalone temp-layout smoke from inside k-skill-cleaner with --skills-root .\nTested: npm run lint && npm run typecheck && npm test && npm run ci
The cleanup helper is now shipped inside the k-skill-cleaner skill payload while the repo-root script remains as a compatibility wrapper. The CLI also enforces the interview-selected usage window with --days/--since and reports the effective cutoff so recommendations match the documented contract.\n\nConstraint: Selective skill installs only receive files under the skill directory.\nRejected: Remove k-skill-cleaner from selective install docs | the feature is intended to be installable standalone.\nConfidence: high\nScope-risk: narrow\nDirective: Keep the skill-local helper as the canonical implementation; the root script should stay a thin wrapper.\nTested: PYTHONPATH=scripts python3 -m unittest scripts.test_k_skill_cleaner\nTested: node --test scripts/skill-docs.test.js\nTested: CLI smoke via k-skill-cleaner/scripts/k_skill_cleaner.py with --days 90\nTested: npm run lint\nTested: npm run typecheck && npm test\nTested: npm run ci\nTested: Architect verification APPROVED\nNot-tested: Live agent transcript schemas beyond fixture-style local log samples
Add a conservative k-skill-cleaner workflow that interviews users, scans best-effort agent trigger logs, and reports deletion or review candidates without mutating skill directories automatically.
Constraint: Trigger-count storage differs by coding agent and may be absent or rotated locally
Rejected: Auto-delete low-usage skills | cleanup recommendations need explicit user approval because log signals are incomplete
Confidence: high
Scope-risk: narrow
Directive: Keep deletion behavior recommendation-only unless a future issue explicitly approves mutation with stronger safeguards
Tested: PYTHONPATH=scripts python3 -m unittest scripts.test_k_skill_cleaner; helper CLI smoke; npm run lint; npm run typecheck && npm test; npm run ci; architect verification
Not-tested: Live exports from OpenClaw/ClawHub or Hermes Agent because no stable public local trigger-count schema is assumed
The IROS docs now make the corp-number happy path produce the company-name list that pinned upstream iros_download.py opens after payment, and route the customer workbook excel_path into the same private workdir boundary as other sensitive inputs and outputs.
Constraint: Live IROS login/payment smoke requires user credentials, certificate/authentication, and card payment authority
Rejected: Rely on upstream data/ defaults | leaves real customer workbook and company list paths inside the cloned repository
Confidence: high
Scope-risk: narrow
Tested: node --test --test-name-pattern='iros-registry-automation' scripts/skill-docs.test.js
Tested: ./scripts/validate-skills.sh
Tested: npm run lint && npm run typecheck && npm test
Tested: npm run ci
Tested: cloned pinned upstream SHA and verified companies_list, excel_path, and configured paths resolve under private temp workdir
Not-tested: Live IROS login/payment smoke; requires user credentials/authentication/payment authority
The IROS skill delegates sensitive browser automation to an upstream Playwright implementation, so the execution guide now checks out a reviewed SHA and keeps real inputs and generated files in a private workdir instead of the clone. Regression coverage locks the pin and privacy-path contract to prevent future docs drift.\n\nConstraint: PR #177 review required an enforceable upstream execution boundary before merge\nConstraint: Live IROS login and payment smoke requires user credentials and card authority\nRejected: Continue documenting mutable upstream HEAD | unsafe for authenticated legal-document/payment-adjacent flows\nConfidence: high\nScope-risk: narrow\nDirective: Do not update iros-registry-automation/scripts/upstream.pin without reviewing the new upstream diff and updating the documented checkout SHA\nTested: node --test --test-name-pattern='iros-registry-automation' scripts/skill-docs.test.js\nTested: ./scripts/validate-skills.sh\nTested: npm run lint && npm run typecheck && npm test\nTested: npm run ci\nTested: cloned upstream, checked out pinned SHA, rewired config.json to a private temp workdir, and verified selected paths stay under that workdir\nNot-tested: Live IROS login/payment smoke; requires user credentials, certificate/authentication, and payment authority
Issue #175 needs a 등기부등본 skill grounded in the challengekim reference implementation while preserving user control over IROS login, authentication, and payment. Add a docs-first skill with regression coverage that locks the upstream credit, safety boundaries, and repository documentation wiring.
Constraint: Original author link must be mentioned in documentation.\nConstraint: IROS login, certificate authentication, and card payment must remain user-controlled.\nRejected: Add a packaged automation wrapper | no dependency or executable implementation was required and credential/payment flows are high-risk.\nConfidence: high\nScope-risk: narrow\nDirective: Do not remove the challengekim upstream credit or weaken the manual login/payment boundary without a new review.\nTested: node --test --test-name-pattern='iros-registry-automation' scripts/skill-docs.test.js; ./scripts/validate-skills.sh; npm run ci\nNot-tested: Live IROS smoke with real login/payment, intentionally not run without user credentials and payment authority
The new reserve train-type argument is consumed by direct command_reserve
calls as well as the CLI parser, so the existing unit helpers need to
model the parser default and assert non-KTX replay behavior explicitly.
Constraint: PR #172 is now retargeted to dev and must be mergeable without weakening the train_id stability tests
Confidence: high
Scope-risk: narrow
Tested: python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py
Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_ktx_booking
Tested: npm run lint
The CLI hardcoded TrainType.KTX in command_search and command_reserve,
which silently excluded ITX-청춘, ITX-새마을, 무궁화호, etc. Routes
served only by non-KTX trains (e.g. 남춘천→용산 via ITX-청춘) returned
zero results with no error.
Add an explicit --train-type flag (default: ktx) so the skill keeps
its KTX-first identity but lets users opt into other Korail train
types when needed:
ktx, itx-saemaeul, mugunghwa, nuriro, tonggeun,
itx-cheongchun, airport, all
Default stays as ktx — fully backward compatible. SKILL.md updated
with usage examples for both search and reserve.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Merged origin/dev into feature/#168 and resolved the README table conflict by preserving dev's skill-name column while keeping the corporate-registration-consulting row. The README docs regression mapping now includes the new skill so future table checks cover it.
Constraint: PR #169 must remain reviewable after updating from dev without merging the PR itself
Rejected: Keep the old four-column README row | it would drop dev's issue #165 skill-name column contract
Confidence: high
Scope-risk: narrow
Tested: node --test --test-name-pattern='corporate-registration-consulting' scripts/skill-docs.test.js
Tested: node --test --test-name-pattern='README skill table' scripts/skill-docs.test.js
Tested: npx --yes k-skill-rhwp create-blank <tmp>/blank.hwp and info <tmp>/blank.hwp
Tested: npm run ci
Tighten the Korean corporate registration skill after round-3 review by keeping filled HWP examples outside the repository, turning the investigation-report conclusion into a user/expert-confirmed placeholder, and locking human-only final action prohibitions with regression coverage.
Constraint: Legal-document workflow must avoid collecting or committing filled PII artifacts
Constraint: Agent may prepare drafts and checklists only, not final legal/tax conclusions or filings
Rejected: Add out/ to .gitignore | safer to teach non-repo private output for sensitive generated forms
Confidence: high
Scope-risk: narrow
Tested: node --test --test-name-pattern='corporate-registration-consulting' scripts/skill-docs.test.js
Tested: npx --yes k-skill-rhwp create-blank <tmp>/blank.hwp && npx --yes k-skill-rhwp info <tmp>/blank.hwp
Tested: npm run ci
Round-two review showed that the new consulting skill still needed explicit stop gates for non-standard incorporation paths and tighter documentation regression locks. The follow-up keeps the user-facing flow beginner-friendly while routing materially different legal/tax structures to official or professional review before draft generation.
Constraint: Legal-document skill must remain reference-only and avoid implying filing, tax, or governance determinations for non-standard cases
Rejected: Keep broad regex alternations for human-only boundaries | they allowed prior safety requirements to regress while tests still passed
Confidence: high
Scope-risk: narrow
Directive: Do not collapse the separate safety-boundary assertions back into alternations without a stronger replacement
Tested: node --test --test-name-pattern='corporate-registration-consulting' scripts/skill-docs.test.js
Tested: npx --yes k-skill-rhwp create-blank <tmp>/blank.hwp && npx --yes k-skill-rhwp info <tmp>/blank.hwp
Tested: npm run ci
The review follow-up closes legal-document safety gaps before merge by locking the expected guidance in the docs regression test and updating the skill surfaces that users and agents rely on. The representative-director clause now avoids board-resolution wording for the common two-director/no-board case, registration-license tax points to the direct 지방세법 제28조 anchor, and PII plus human-only filing boundaries are repeated where filled document workflows can expose sensitive data.
Constraint: PR review requested TDD coverage for legal/tax safety wording and privacy guidance
Rejected: Keep a single generic disclaimer only | document templates also need local warnings where filled artifacts are produced
Confidence: high
Scope-risk: narrow
Directive: Do not remove the repeated privacy and human-only boundaries unless every user-facing generation surface keeps equivalent guidance
Tested: node --test --test-name-pattern='corporate-registration-consulting' scripts/skill-docs.test.js
Tested: npx --yes k-skill-rhwp create-blank <tmp>/blank.hwp && npx --yes k-skill-rhwp info <tmp>/blank.hwp
Tested: npm run ci
Add a reference-only corporate registration consulting skill for first-time Korean incorporation workflows. The skill keeps user decisions explicit, provides conservative articles/document templates, and routes HWP automation through existing rhwp/kordoc surfaces instead of introducing new machinery.
Constraint: Issue #168 requires legal-disclaimer wording, tax pitfall guidance, rhwp-based form support, TDD, and PR delivery to dev.
Rejected: Add a new package or external automation dependency | existing skill docs and rhwp tooling are enough for this documentation/template workflow.
Confidence: high
Scope-risk: narrow
Directive: Do not present generated registration documents as legal or tax advice; keep official-source verification and professional review caveats visible.
Tested: node --test --test-name-pattern='corporate-registration-consulting' scripts/skill-docs.test.js; npx --yes k-skill-rhwp create-blank/info smoke; npm run ci
Not-tested: Live court registry submission or live tax payment, intentionally outside reference-skill scope
Surface every skill's directory identifier (e.g. `kbl-results`,
`real-estate-search`) directly in the "어떤 걸 할 수 있나" table so users
can search README and copy-paste the exact name into install commands or
agent frontmatter without leaving the page.
- README.md: insert `스킬 이름` column after `할 수 있는 일` for all 54
rows (5-column header, separator, and per-row inline-code identifier);
apply matching strikethrough to the deprecated blue-ribbon-nearby cell.
- scripts/skill-docs.test.js: add 4 regression tests pinning the header
shape, the 53 active label↔skill-name mappings, the strikethrough on
the deprecated row, and a cross-check that every advertised skill
identifier matches a real on-disk SKILL.md whose frontmatter `name`
agrees (validate-skills.sh invariant).
Round 2 review flagged a latent Unicode safety bug: when replaceAll's
caseSensitive=false branch encounters characters whose toLowerCase()
changes UTF-16 length (e.g. Turkish İ U+0130 → i + U+0307 combining dot
above), offsets taken in the lowercased haystack drift by the expansion
delta for every subsequent match and silently corrupt the document.
Reviewer repro: 'ABCİABCİXYZ' + case-insensitive İ→Z reported
{ok:true,count:2} but rendered 'ABCZABCİZYZ' instead of 'ABCZABCZXYZ'
(the X at index 8 was corrupted while the second İ survived).
Surface a descriptive error rather than silently drift:
- findAllMatchOffsets: in the case-insensitive branch, verify that the
paragraph text and the query each preserve UTF-16 length under
toLowerCase; otherwise throw with an actionable message pointing the
user to --case-sensitive or input normalization.
- This is strictly a safety guard: the 2025→2026 headline workflow,
ASCII, Hangul, and every existing test are unaffected.
Tests (TDD red → green, net +4 in packages/k-skill-rhwp):
- 'replaceAll refuses case-insensitive matching when source text
contains case-folding length-changing chars (e.g. Turkish İ U+0130)'
reproduces the exact reviewer input and asserts rejection + no output
file
- 'replaceAll refuses case-insensitive matching when the query itself
contains case-folding length-changing chars' covers the query-side path
- 'replaceAll with --case-sensitive succeeds on inputs containing İ'
confirms the guard only fires in the case-insensitive path and that
case-sensitive produces ABCZABCZXYZ with no X corruption
- 'replaceAll case-insensitive still works for normal ASCII/Hangul'
regression-guards against the fix over-rejecting the common case
Doc disclosure in all 4 surfaces called out by the reviewer:
- rhwp-edit/SKILL.md: new failure-mode bullet naming U+0130 specifically
- docs/features/rhwp-edit.md: Unicode 대소문자 무시 주의 paragraph
under scenario 3 (replace-all)
- packages/k-skill-rhwp/README.md: extended Scope section
- packages/k-skill-rhwp/src/cli.js: USAGE 'Scope note' appended
- scripts/skill-docs.test.js: 2 new assertions locking the SKILL.md and
feature-doc disclosure so they can't be silently removed
- .changeset: note the guard in the pending v0.1.0 release notes
Manual QA (end-to-end via the published CLI):
$ k-skill-rhwp replace-all … --query İ --replacement Z
→ exit 1 + 'case-insensitive matching is unsafe because case folding
changes the UTF-16 length …'
→ no output file written
$ k-skill-rhwp replace-all … --query İ --replacement Z --case-sensitive
→ {ok:true,count:2}, render shows 'ABCZABCZXYZ', search İ ⇒ found:false
$ replace-all '2025'→'2026' on '2025 2025 2025' ⇒ {ok:true,count:3}
$ replace-all 'hello'→'hi' (case-insens.) on 'hello WORLD 안녕 HELLO'
⇒ {ok:true,count:2}
Verification:
- npm test --workspace k-skill-rhwp: 35 pass / 0 fail (+4 vs Round 2)
- node --test scripts/skill-docs.test.js: 114 pass / 0 fail
- npm run ci: exit 0 (lint + typecheck + all workspace tests +
pack:dry-run + validate-skills.sh all green)
Refs PR #162 Round 2 review 'Non-blocking residual risk — Unicode
case-insensitive offset drift'.
Upstream @rhwp/core HwpDocument.replaceAll returns {ok:true, count:N} but
does not persist the mutation into exportHwp() serialization, so the output
bytes are byte-identical to the input. This is confirmed against
@rhwp/core@0.7.3 with SHA diffing and round-trip searchText.
Rewrite the Node wrapper replaceAll to compose engine primitives that do
persist: for each body paragraph, read the full text via getTextRange,
compute all non-overlapping match offsets in JS, then apply replaceText
right-to-left so earlier offsets are unaffected by length changes. This
restores the documented '2025 → 2026 일괄 치환' headline workflow.
Guard rails in the new replaceAll:
- Reject replacements containing newline or paragraph-break characters
(\n, \r, U+2028, U+2029) with a descriptive error. Splitting a paragraph
via replaceText would invalidate subsequent offsets.
- Non-overlapping semantics against the original text, so
--query a --replacement aa against 'aaa' yields 'aaaaaa' (3 replacements)
instead of looping on the freshly inserted 'a' characters.
Tighten the regression tests to assert content, not just length:
- Same-length replacement: output SHA must differ from input, searchText
must find the replacement and must NOT find the original query.
- Longer-length replacement: paragraph length must grow by the correct
amount and output SHA must differ.
- Shorter-length replacement: paragraph length must shrink by the correct
amount and output SHA must differ.
- Empty replacement: deletes every match and output no longer contains
the query.
- Replacement contains query (a→aa on aaa): expects count 3 and length 6.
- Zero matches: count 0, output still written.
- Case-sensitive flag skips mismatched case.
- Newline replacement is rejected synchronously.
Document the body-only scope of search and replace-all in the SKILL.md
routing policy, failure-modes, CLI USAGE text, feature doc, and package
README so users know to use set-cell-text for cell content. This matches
the upstream searchText contract, which does not descend into table cells,
headers, footers, or footnotes.
Add a matching regression assertion to scripts/skill-docs.test.js so the
body-only scope note cannot be silently removed from SKILL.md or the
feature doc.
Closes review round 1 for PR #162.
Implements the three non-blocking observations from PR #161 round-3 review:
1. Numbered-h2 gate (reviewer-flagged fragility):
Refactored _extract_first_section_between_h2 to extract h2 inner text
(stripping nested tags) and filter by '^\\s*\\d+(?:\\.\\d+)*\\.\\s+\\S'.
Sidebar widgets like <h2>관련 문서</h2> or <h2>외부 링크</h2> can no longer
anchor the extractor - only numbered section headers (1., 1.2., 2.3.4.) do.
Handles live Namu Wiki structure where the number sits inside an <a> tag
(<a>1.</a> <span>개요</span>), which the round-3 suggested regex-only gate
missed. All 29 seed pages continue to produce valid summaries on live
fetches.
2. Category-nav template strip (reviewer-flagged long-page noise):
a. CATEGORY_NAV_RE strips the inline '[펼치기 · 접기]' marker plus its
same-line aftermath (the category list items on the same line).
b. DETAILS_PELCHIGI_RE strips the entire <details> block whose <summary>
contains 펼치기. Namu Wiki today wraps category nav in exactly this
structure, so the strip removes the full noise block (not just the
marker line).
꿀잼 summary drops from 3482 chars of category dump to 562 chars
starting with the real definition '무언가가 매우 재미있다는 의미의 인터넷
유행어'. Non-category <details> blocks (spoilers, footnotes) are
preserved.
3. TDD + mutation coverage:
6 new tests total: 2 numbered-h2 gate tests, 2 inline category-nav tests,
1 <details>-block strip test, 1 <details>-keep test (negative case).
All 6 were written first and confirmed RED against the round-2 baseline,
then made GREEN after the implementation landed. Each fix path was also
mutation-tested (revert regex, remove .sub line) to confirm the tests
genuinely catch the target bug class.
Suite grows from 45 to 51 tests. All pass. npm run ci exits 0.
Namu Wiki's current HTML layout uses build-time-obfuscated CSS class
names (e.g. _36R8DWTn, OZVChh+l) and has no <article>/<main>/<section>
tags, so all six MAIN_CONTENT_CLASSES anchors fail to match and
extract_summary() returned empty with a 'Main content region not
detected' warning on every live page.
Replace the single class-based strategy with a three-tier fallback
chain that pins to progressively weaker but more structurally stable
anchors:
1. First h2 section boundary. Namu Wiki articles consistently open
with '<h2>1. 개요[편집]</h2>' and mark subsequent sections with
numbered h2 headings. Extracting text between the first and
second h2 reliably captures the overview section on every page
sampled (중꺾마, 갓생, 럭키비키, 어쩔티비).
2. MAIN_CONTENT_CLASSES / <article> - kept as a legacy fallback
for older Namu Wiki layouts and for third-party fixtures.
3. og:description meta tag - final safety net before returning
empty, gives the agent at least a ~64-char preview when the
article has unusual structure.
Strip '[편집]' edit-affordance markers and numbered section prefixes
(e.g. '1.2.') from the extracted text so headings don't leak through
as noise.
Live verification (text format):
slang_lookup.py 중꺾마 -> Title + 286-char summary
slang_lookup.py 갓생 -> Title + 96-char summary
slang_lookup.py 럭키비키 -> Title + 59-char summary
slang_lookup.py 어쩔티비 -> Title + 20-char summary
All previously-empty. Not-found / blocked / upstream-error paths and
exit codes are unchanged.
Reviewer flagged 4/30 seed namuwiki_url values returning HTTP 404 on live
Namu Wiki. These URLs are part of the documented response contract and get
surfaced directly to agents, so broken links are a functional bug, not a
cosmetic one.
Root causes per entry:
- 중꺾마: wrong 꺾 codepoint (U+AFFA 꿺 instead of U+AEBE 꺾).
- 아아: typo in aliased title (아이스 아메리칸노 instead of 아메리카노).
- 어쩔티비: missing 받침 (어쩌티비 instead of 어쩔티비).
- 당모치: encoding correct but no live Namu Wiki article exists; dropped.
Also fixes two separately-broken 중꺾마 example URLs in SKILL.md
(U+AFBE 꾾 instead of U+AEBE 꺾) — these were discovered while auditing
the seed and would have surfaced as 404 to agents following the example
snippets.
Adds two regression tests:
- test_each_seed_url_decodes_to_term_or_alias: decodes every seed URL's
path segment and asserts it equals the term or one of its aliases.
Catches Hangul-codepoint typos offline (no network dependency) and
would have caught all 3 encoding bugs in this PR.
- test_no_seed_entry_points_at_known_missing_namuwiki_page: locks the
당모치 drop so nobody re-adds an entry pointing at a page that does
not exist on Namu Wiki.
Fixes the existing LookupNetworkTest assertion that was hard-coding the
broken URL — it now derives the expected URL via build_namuwiki_url()
so the test cannot drift out of sync with the helper again.
Verification:
- PYTHONPATH=.:scripts python3 -m unittest scripts.test_korean_slang_writing -> 40/40 pass
- Live GET with browser headers against all 29 remaining seed URLs -> 29/29 return 200
- npm run ci -> exit 0
- Manual QA: slang_search on 중꺾마, 어쩔티비, 아이스 아메리카노 returns
correct URLs; slang_lookup live-fetches 중꺾마 and extracts the
canonical title '중요한 것은 꺾이지 않는 마음'.
Pins the HWP table row rename to 'HWP 문서 조회/변환', asserts the new
'HWP 문서 편집' and 'HWP 레이아웃·IR 디버깅' README rows and their linked
feature docs, pins the new SKILL.md routing policy for rhwp-edit and
rhwp-advanced (k-skill-rhwp CLI + @rhwp/core for editing vs upstream
Rust CLI for layout/IR debugging), and asserts the k-skill-rhwp
package.json wiring (bin mapping, @rhwp/core dependency, Node 18+
engines, wasm-init shim + CLI bin files).
Per AGENTS.md rule, no assertion is added on the presence of any
.changeset/*.md file so the changeset release flow can consume the
rhwp-edit-skill.md entry without breaking CI at version-bump time.
Also captures the package-lock.json delta introduced by adding the
k-skill-rhwp workspace (pulls @rhwp/core@0.7.3 and its WASM binary).
Refs #155.
Direct probes against https://a.retn.kr/v1/public/assist confirmed that
X-OpenClaw-Client-Id: coupang-mcp-fallback returns HTTP 403 Client is not
allowlisted, while the upstream default openclaw-skill returns HTTP 200.
The default wrapper path already works because upstream falls back to
openclaw-skill, but the explicit recommendation in SKILL.md and the
feature doc was luring users to a 403 path.
Remove the dead recommendation and lock in the working configuration:
- Docs describe openclaw-skill as the upstream-allowlisted default and
note that k-skill does not override OPENCLAW_SHOPPING_CLIENT_ID.
- Wrapper --help epilog drops the Suggested k-skill value line and
documents openclaw-skill as the allowlist value in play.
- New skill-docs regression asserts coupang-mcp-fallback is absent from
SKILL.md, the feature doc, the wrapper, and docs/sources.md while
openclaw-skill is documented across all three narrative surfaces.
- New Python wrapper regression asserts --help drops the dead value and
surfaces openclaw-skill so the constraint stays locked.
- Existing env-forwarding test uses openclaw-skill as the pass-through
sentinel so the repo no longer ships the non-allowlisted string at all.
retention-corp/coupang_partners#1 is merged, so upstream now transparently falls back to the Retention Corp hosted backend at https://a.retn.kr/v1/public/assist when Coupang Partners API credentials are missing. The k-skill wrapper already passes environment variables through unchanged, so this commit lines up the documented contract with the actual two-path behavior without changing runtime logic.
- SKILL.md and docs/features/coupang-product-search.md describe both execution paths (operator local HMAC vs credentialless hosted fallback), the honored OPENCLAW_SHOPPING_* env vars, the allowlist client-id convention including the k-skill-specific coupang-mcp-fallback value, and the mandatory affiliate disclosure when a.retn.kr/s/ shortlinks or lptag=AF deeplinks appear in responses.
- docs/sources.md adds the hosted assist endpoint and the merged upstream PR so the source surface stays truthful.
- README.md reflects the 선택사항 semantics for the 쿠팡 상품 검색 row and extends the column legend so 선택사항 is distinct from 불필요.
- coupang_partners_mcp.py expands its --help epilog so operators discover the honored upstream env vars without reading the wrapper source; no runtime behavior change.
- scripts/test_coupang_partners_mcp_wrapper.py locks env pass-through as a regression, asserts the new --help contract, and adds an opt-in K_SKILL_COUPANG_SMOKE=1 live smoke test that verifies the credentialless hosted path returns a Coupang deeplink.
- scripts/skill-docs.test.js extends the docs regression to require the hosted assist URL, OPENCLAW_SHOPPING_* env prefix, affiliate disclosure wording, and hosted fallback concept while keeping the yuju777 HF Space negative assertion.
Verified: npm run ci exits 0, live smoke test (K_SKILL_COUPANG_SMOKE=1) returns a.retn.kr/s/ shortlinks via credentialless wrapper, and manual env -u COUPANG_ACCESS_KEY -u COUPANG_SECRET_KEY call returns isRocket+lptag=AF3727577 responses through the hosted fallback.
Refs: #134
Close Round 3 NICE TO HAVE from PR #149. The LICENSE.upstream
regression block asserted preamble, Version 2.0, Redistribution,
END OF TERMS, and Copyright 2026 kimlawtech but not the APPENDIX
anchor at LICENSE.upstream:179, even though the Round 1 follow-up
and Round 2 review collectively described 'APPENDIX structure
verification'. Adding this one assertion closes that claim/test
parity gap and acts as tamper-detection if upstream reformats
LICENSE later.
Verified with TDD: temporarily stripped APPENDIX line from
LICENSE.upstream, confirmed test 108 FAILS with the expected
regex mismatch, then restored and re-confirmed 109/109 GREEN.
Byte-for-byte identity with upstream LICENSE still holds
(SHA256 35ef947614c2f14df01c5fc553f987f644f0c9f6b011adda397bd788a87f1510).
npm run ci exit 0 with 357 ok subtests (unchanged baseline,
additive assertion within existing test block).