Quote the long Korean description so the `예:` segment is parsed as a scalar
instead of an invalid YAML mapping key. Also ignore local `.gjc/` runtime state.
Verified with scripts/validate-skills.sh and downstream benchmark eligibility
classification.
* feat(srt-booking): SRT 좌석 확인과 탐색 우선순위 개선 (#305)
* feat(srt): 좌석 조회와 탐색 우선순위 추가
SRT search 결과의 stable train_id로 객차별 좌석을 조회하고, 특정 호차/좌석 확인과 탐색 우선순위 옵션을 제공한다.
Constraint: SRT와 KTX는 별도 upstream 표면이므로 SRT HTML 파서와 테스트를 분리함
Rejected: KTX 좌석 helper 공유 | Korail API와 SRT 웹 좌석선택 HTML 계약이 달라 혼용하면 파서 안정성이 낮아짐
Confidence: medium
Scope-risk: moderate
Directive: SRT 좌석선택 HTML에서 노출되지 않는 속성은 추정하지 말고 명시적으로 처리할 것
Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_srt_booking scripts.test_ktx_booking; python3 -m py_compile scripts/srt_booking.py scripts/srt_seats.py scripts/test_srt_booking.py
Not-tested: 실제 예약 API에 우선순위 좌석 선택을 연결하는 흐름
* fix(srt): 좌석 조회 JSON 출력 안정화
SRT 대기열 메시지가 stdout에 섞여 seats JSON을 깨는 실제 표면 문제를 막고, 누락된 좌석 방향/위치 속성을 unknown으로 정규화한다.
Constraint: issue #303 범위는 예약 부작용이 없는 좌석 조회 보조 흐름으로 제한됨
Rejected: 실제 예약 subcommand 추가 | 좌석 선점/예약은 외부 부작용이라 이번 acceptance criteria에 포함되지 않음
Confidence: high
Scope-risk: narrow
Directive: SRTrain upstream 출력이 추가되더라도 helper stdout은 JSON 전용으로 유지할 것
Tested: RED→GREEN in .omo/ulw-loop/evidence/srt-c002-red-green-tests.txt; live SRT tmux QA in .omo/ulw-loop/evidence/srt-c001-live-search-seats.txt; npm run ci in .omo/ulw-loop/evidence/srt-c003-regression-ci.txt
Not-tested: 실제 예약/결제/취소 부작용 흐름
* test(srt): split seat helper regression coverage
---------
Co-authored-by: Jeffrey (Dongkyu) Kim <vkehfdl1@gmail.com>
* feat: add korean-humanizer skill
AI가 쓴 티가 나는 한국어 글을 자연스러운 사람 글로 고치는 프롬프트 기반 스킬.
blader/humanizer의 구조·방법론(패턴 카탈로그 + draft→audit→final 루프 +
false positive 가이드)을 한국어에 맞게 재구성했다.
- 한국어 특화 33개 패턴: 번역체(직역 조사·무생물 주어·"~들"·"가지다"·이중피동·
명사화), AI 상투어, 3의 법칙, 과장된 의의 부여, 마무리 상투구, 챗봇 잔재,
줄표·가운뎃점·곡선따옴표 등
- Triage(최소 개입) 원칙: 서식만 문제면 산문은 그대로 두어 과교정 방지
- Length control: 목표 글자수 지정 시 ±5% 내로 맞추고 공백 포함/제외 수치 보고,
korean-character-count 스킬과 연동
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(korean-humanizer): rebuild v2 on im-not-ai framework
Build on happy-nut's PR #311 korean-humanizer skill (cherry-picked,
authorship preserved) by re-centering it on the epoko77-ai/im-not-ai
(Humanize KR, MIT) methodology:
- 4대 철칙 (의미 불변 · 근거 기반 · 장르 유지 · 과윤문 금지 30%/50% 가드)
- S1/S2/S3 severity tiers and A~D quality grades
- A~J taxonomy with Korean-specific patterns (A-16 그/그녀 강박,
A-18 관계절 좌향 수식, A-19 이중 조사, C-11 연결어미 뒤 쉼표, E-7 경어법)
- detect -> rewrite -> audit -> grade loop with self-check checklist
- references/ai-tell-taxonomy.md full A~J table
- docs/features/korean-humanizer.md crediting im-not-ai and happy-nut
- README row + link, regenerated plugin.json, docs regression test
Co-authored-by: happy-nut <happynut.dev@gmail.com>
* docs(korean-law-search): document official precedent API evidence (#313)
Enhance the existing korean-law-search skill and feature doc with the
official 법제처 Open API precedent endpoints and detail retrieval, without
adding a new skill, package, workspace, or changeset.
- Document 판례 목록 조회 (lawSearch.do?target=prec) and 판례 본문 조회
(lawService.do?target=prec&ID=...) as official evidence behind the
korean-law-mcp search_precedents/get_precedent_text path.
- Add supported precedent filters (query, court, case number, source
name, date, sort) and precedent-specific failure modes (missing LAW_OC,
upstream unavailable/rate-limit/timeout, empty results, body
unavailable for some sources) plus the legal-advice boundary.
- Keep korean-law-mcp first and Beopmang as the only post-failure
fallback; lawService.do?target=prec is official detail retrieval, not a
Beopmang-style fallback.
- Extend the skill-docs regression test with stable endpoint/tool
literals and concept-level filter/failure-mode/legal-boundary checks.
Closes#308
* feat(toss-securities): add official read-only OpenAPI client (#312)
Add an official Toss Securities Open API client alongside the existing
unofficial tossctl wrapper. The package ships read-only helpers backed by
the official REST API (https://openapi.tossinvest.com): OAuth2
client_credentials token issuance with an in-memory token cache, bearer +
X-Tossinvest-Account header handling, TossApiError/TossCredentialsError
with secret/token redaction, and 429 Retry-After/backoff retry.
Credentials are read from TOSSINVEST_CLIENT_ID/TOSSINVEST_CLIENT_SECRET
(optional TOSSINVEST_ACCOUNT/TOSSINVEST_API_BASE_URL) and sent directly to
Toss, never through a shared proxy. Order mutation remains out of scope;
the tossctl path is retained as a documented fallback.
Closes#306
* Revert "docs(korean-law-search): document official precedent API evidence (#313)"
This reverts commit 5faec8bb2a.
* feat(k-skill-proxy): fold Korean law lookups into k-skill-proxy, drop Beopmang (#315)
Add hosted korean-law proxy routes and make the korean-law-search skill
proxy-first, removing the unstable Beopmang fallback from the support list.
- proxy: new src/korean-law.js wrapping official 법제처 DRF lawSearch.do /
lawService.do, injecting LAW_OC + browser User-Agent/Referer (the real
cause of "사용자 정보 검증 실패") and retrying empty/HTML responses.
- proxy: /v1/korean-law/search and /v1/korean-law/detail routes + lawOc
config + koreanLawConfigured health flag; 17 module + 6 route tests.
- skill/docs: korean-law-search becomes proxy-first (no per-user LAW_OC,
no local CLI). Drop Beopmang everywhere; credit chrisryugj/korean-law-mcp
as design reference and 법제처 open.law.go.kr as official source.
- ops: LAW_OC added to deploy doc KEYS, secret accessor loop, and the
Cloud Run deploy workflow set-secrets.
- changeset: k-skill-proxy minor.
---------
Co-authored-by: iamiks <rmstjr1030@naver.com>
Co-authored-by: happy-nut <happynut.dev@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add hosted korean-law proxy routes and make the korean-law-search skill
proxy-first, removing the unstable Beopmang fallback from the support list.
- proxy: new src/korean-law.js wrapping official 법제처 DRF lawSearch.do /
lawService.do, injecting LAW_OC + browser User-Agent/Referer (the real
cause of "사용자 정보 검증 실패") and retrying empty/HTML responses.
- proxy: /v1/korean-law/search and /v1/korean-law/detail routes + lawOc
config + koreanLawConfigured health flag; 17 module + 6 route tests.
- skill/docs: korean-law-search becomes proxy-first (no per-user LAW_OC,
no local CLI). Drop Beopmang everywhere; credit chrisryugj/korean-law-mcp
as design reference and 법제처 open.law.go.kr as official source.
- ops: LAW_OC added to deploy doc KEYS, secret accessor loop, and the
Cloud Run deploy workflow set-secrets.
- changeset: k-skill-proxy minor.
사업자등록번호로 "이 사업자 실제 문제 없나"를 무료 공공 데이터로 교차 조회하는
스킬군을 기여한다. 점수·등급·"위험" 라벨 없이 사실+출처+조회시각만 병렬한다.
단품 스킬:
- national-pension-workplace 국민연금 가입 사업장 (proxy, 3046071)
- nts-tax-delinquency 국세 체납 명단공개 (무인증 직접)
- fsc-corporate-info 금융위 기업기본정보 (proxy, 15043184)
- g2b-sanctioned-supplier 조달청 부정당제재 (proxy, 15129466)
- localdata-business-status 지방행정 인허가 영업상태 208업종 (무인증 직접)
복합 스킬:
- biz-health-check 위 5종 + 기존 nts-business-registration을 한 번에 호출
proxy(packages/k-skill-proxy):
- keyed route 3개 추가 — 키는 서버의 DATA_GO_KR_API_KEY로만 주입(사용자 시크릿 없음)
- 연금 route는 basic+detail+monthly 3콜 오케스트레이션 + 월별중복 dedup
- server.test.js에 route 테스트 10건 추가 (정상/503 미설정/400/403 forbidden)
무인증 스킬은 stdlib(urllib)만 사용해 의존성 없이 직접 호출한다.
문서: docs/features ×6, README 표·링크, docs/sources.md 갱신, plugin.json 재생성.
활용신청(프록시 운영 서버 등록 필요): 3046071·15043184·15129466
(15081808 국세청 상태조회는 nts-business-registration용으로 이미 등록, 키 공유).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an official Toss Securities Open API client alongside the existing
unofficial tossctl wrapper. The package ships read-only helpers backed by
the official REST API (https://openapi.tossinvest.com): OAuth2
client_credentials token issuance with an in-memory token cache, bearer +
X-Tossinvest-Account header handling, TossApiError/TossCredentialsError
with secret/token redaction, and 429 Retry-After/backoff retry.
Credentials are read from TOSSINVEST_CLIENT_ID/TOSSINVEST_CLIENT_SECRET
(optional TOSSINVEST_ACCOUNT/TOSSINVEST_API_BASE_URL) and sent directly to
Toss, never through a shared proxy. Order mutation remains out of scope;
the tossctl path is retained as a documented fallback.
Closes#306
Enhance the existing korean-law-search skill and feature doc with the
official 법제처 Open API precedent endpoints and detail retrieval, without
adding a new skill, package, workspace, or changeset.
- Document 판례 목록 조회 (lawSearch.do?target=prec) and 판례 본문 조회
(lawService.do?target=prec&ID=...) as official evidence behind the
korean-law-mcp search_precedents/get_precedent_text path.
- Add supported precedent filters (query, court, case number, source
name, date, sort) and precedent-specific failure modes (missing LAW_OC,
upstream unavailable/rate-limit/timeout, empty results, body
unavailable for some sources) plus the legal-advice boundary.
- Keep korean-law-mcp first and Beopmang as the only post-failure
fallback; lawService.do?target=prec is official detail retrieval, not a
Beopmang-style fallback.
- Extend the skill-docs regression test with stable endpoint/tool
literals and concept-level filter/failure-mode/legal-boundary checks.
Closes#308
AI가 쓴 티가 나는 한국어 글을 자연스러운 사람 글로 고치는 프롬프트 기반 스킬.
blader/humanizer의 구조·방법론(패턴 카탈로그 + draft→audit→final 루프 +
false positive 가이드)을 한국어에 맞게 재구성했다.
- 한국어 특화 33개 패턴: 번역체(직역 조사·무생물 주어·"~들"·"가지다"·이중피동·
명사화), AI 상투어, 3의 법칙, 과장된 의의 부여, 마무리 상투구, 챗봇 잔재,
줄표·가운뎃점·곡선따옴표 등
- Triage(최소 개입) 원칙: 서식만 문제면 산문은 그대로 두어 과교정 방지
- Length control: 목표 글자수 지정 시 ±5% 내로 맞추고 공백 포함/제외 수치 보고,
korean-character-count 스킬과 연동
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(srt): 좌석 조회와 탐색 우선순위 추가
SRT search 결과의 stable train_id로 객차별 좌석을 조회하고, 특정 호차/좌석 확인과 탐색 우선순위 옵션을 제공한다.
Constraint: SRT와 KTX는 별도 upstream 표면이므로 SRT HTML 파서와 테스트를 분리함
Rejected: KTX 좌석 helper 공유 | Korail API와 SRT 웹 좌석선택 HTML 계약이 달라 혼용하면 파서 안정성이 낮아짐
Confidence: medium
Scope-risk: moderate
Directive: SRT 좌석선택 HTML에서 노출되지 않는 속성은 추정하지 말고 명시적으로 처리할 것
Tested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_srt_booking scripts.test_ktx_booking; python3 -m py_compile scripts/srt_booking.py scripts/srt_seats.py scripts/test_srt_booking.py
Not-tested: 실제 예약 API에 우선순위 좌석 선택을 연결하는 흐름
* fix(srt): 좌석 조회 JSON 출력 안정화
SRT 대기열 메시지가 stdout에 섞여 seats JSON을 깨는 실제 표면 문제를 막고, 누락된 좌석 방향/위치 속성을 unknown으로 정규화한다.
Constraint: issue #303 범위는 예약 부작용이 없는 좌석 조회 보조 흐름으로 제한됨
Rejected: 실제 예약 subcommand 추가 | 좌석 선점/예약은 외부 부작용이라 이번 acceptance criteria에 포함되지 않음
Confidence: high
Scope-risk: narrow
Directive: SRTrain upstream 출력이 추가되더라도 helper stdout은 JSON 전용으로 유지할 것
Tested: RED→GREEN in .omo/ulw-loop/evidence/srt-c002-red-green-tests.txt; live SRT tmux QA in .omo/ulw-loop/evidence/srt-c001-live-search-seats.txt; npm run ci in .omo/ulw-loop/evidence/srt-c003-regression-ci.txt
Not-tested: 실제 예약/결제/취소 부작용 흐름
* test(srt): split seat helper regression coverage
---------
Co-authored-by: Jeffrey (Dongkyu) Kim <vkehfdl1@gmail.com>
라이브 불변식 검증(예비 제외·dedup·useDt 범위)이 통과한 뒤,
사용자 대면 출력 함수인 print_text가 테스트 0개였던 갭을 메운다.
- PrintTextTest 2개: 결과 렌더링 / 빈 결과 메시지
- stub_fetch는 fetch_one의 미사용 인자를 **_ 로 흡수해 lint 경고 제거
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
월별예약조회 API가 srchDate 단일 일자 요청에도 5일 윈도우를 반환하고,
"예비"로 표기된 운영자 보유분이 raw 응답에 포함되며, 같은 객실이 다른
goodsId로 중복 표시되는 세 가지 문제를 한꺼번에 fix한다.
수정:
- collect_results 안에 strict useDt gate 추가 (today~last_day 범위 밖 행 차단)
- is_reserve_room() helper로 goodsNm에 "예비" 포함 객실 제외
- (forest_id, use_dt, name) 단위 dedup으로 중복 행 제거
- is_available()는 시그니처/로직 변경 없이 booking-state predicate 유지
추가:
- foresttrip-vacancy/tests/ 18개 단위 테스트 (mock + fixture 기반)
- IsReserveRoomTest, IsAvailableTest, CollectResultsFilterTest,
StrictUseDtGateTest, GroundTruthTest 다섯 클래스
- 거제·구재봉 fixture로 사용자 라이브 검증 결과 회귀 보호
- package.json lint·test 스크립트에 등록
문서:
- SKILL.md: API 5일 윈도우/예비 객실/중복 dedup 자동 처리 명시 + 회복 시나리오 보강
- docs/features/foresttrip-vacancy.md: 기본 흐름 6단계와 주의할 점 보강
사용자 라이브 검증 ground truth (2026-05-12 기준):
- 거제자연휴양림 5/13 ~9개, 5/16 0개, 5/17 19개, 5/23 0개, 5/24 0개
- 구재봉자연휴양림 5/16 1개 (206호 쑥부쟁이방)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fail closed when Korail reports remaining seats but returns malformed, sentinel-only, or schema-incomplete detail rows, and keep the seat research endpoints inside the same Dynapath/Sid request boundary as other protected mobile calls.\n\nConstraint: PR #298 review required TDD regressions for false empty-seat success and raw external payload failures.\nRejected: Preserve lenient normalization of partial seat rows | it can mislead automation with authoritative empty results.\nConfidence: high\nScope-risk: narrow\nDirective: Keep KTX seat detail parsing fail-closed unless live Korail evidence proves a specific empty or partial shape is valid.\nTested: PYTHONPATH=.:scripts python3 -m unittest scripts.test_ktx_booking; node --test scripts/skill-docs.test.js; npm run typecheck; python3 -m compileall -q scripts/ktx_booking.py scripts/test_ktx_booking.py; ruff check scripts/ktx_booking.py scripts/test_ktx_booking.py; shellcheck scripts/validate-skills.sh; bash scripts/validate-skills.sh; PYENV_VERSION=3.11.9 npm run ci\nNot-tested: live Korail seat-detail network flow with production credentials
Boundary validation now distinguishes unavailable/malformed Korail car and seat-detail payloads from valid zero-seat results, so automation does not consume false successful empty-seat JSON.\n\nConstraint: PR #298 follow-up for issue #293 required TDD, clear errors for malformed details/metadata, and branch feature/#293.\nRejected: Per-car partial error payloads | broader interface change than the existing fail-fast command contract.\nRejected: Missing Korail car counts defaulting to zero | masks unknown external summary data as authoritative no-seat state.\nConfidence: high\nScope-risk: narrow\nDirective: Keep seat-detail and car-summary schema checks at the command boundary before normalizing rows.\nTested: Targeted malformed payload probe; PYTHONPATH=.:scripts python3 -m unittest scripts.test_ktx_booking; node --test scripts/skill-docs.test.js; npm run typecheck; python3 -m compileall -q scripts/ktx_booking.py scripts/test_ktx_booking.py; ruff check scripts/ktx_booking.py scripts/test_ktx_booking.py; shellcheck scripts/validate-skills.sh; bash scripts/validate-skills.sh; PYENV_VERSION=3.11.9 npm run ci.\nNot-tested: Live Korail seat-detail endpoints requiring user credentials.