tools/xdata/ 는 xvoice3 등 TTS 프로젝트들의 데이터(spk2path, context CSV)를
공통 스키마 플러그인 규약으로 파싱·관리한다. Primary 저장소는 NocoDB
tts-data/dataset_variants 테이블이며(ADR 0002), 파일 레지스트리(JSON) 는
오프라인 재조회·재생성용 스냅샷으로 병행 저장된다.
관련 결정:
| 경로 | 역할 |
|---|---|
tools/xdata/core.py | Registry / StorageRoot / 플러그인 레지스트리 |
tools/xdata/schemas/base.py | DataSchema ABC (프로젝트 스키마 인터페이스) |
tools/xdata/_plugins.py | setuptools entry_points xdata.schemas 기반 플러그인 발견 |
tools/xdata/cli.py | argparse CLI (parse/modify/update/export/query/view/schemas/migrate/fs-sync) |
tools/xdata/walkers/ | walker plugin 패키지 — register_walker / get_walker (xvoice3_main KOR/MAIN walker, aihub_emotion AI_HUB 015.emotion_style00 walker, en_us_main EN_US 4-service walker). 신규 source 는 본 패키지에 walker 추가. |
tools/xdata/fs_walker.py | backwards-compat shim — xvoice3_main 재export. 신규 코드는 walkers 패키지 직접 사용. 경로 규약: wiki/tech/fs-path-convention.md |
tools/xdata/preprocessing.py | 전처리 축 정본 + 디렉토리 suffix 파서 (PREPROC_TOKEN_MAP) |
tools/xdata/migrations/phase2_schema_v2.py | 6-field → ADR-0012 5-field natural_key 마이그레이션 |
tools/xdata/audit.py | dataset_variant_changes 이벤트 생성·diff·fail-soft emit |
tools/scripts/nocodb_backup.py | NocoDB 테이블 → JSONL atomic 백업 (fs-sync 전 스냅샷) |
tools/scripts/nocodb_bootstrap_audit.py | dataset_variant_changes 테이블 + _meta 등록 부트스트랩 |
tools/scripts/import_legacy_changelog.py | 과거 registry_changelog.jsonl → audit 이벤트 import |
tools/integrations/nocodb/writer.py | fail-soft write-through (실패 시 JSONL 스풀) |
tools/integrations/nocodb/replay.py | 스풀 → NocoDB 재전송 CLI |
{speaker_key}/{language}/{utterance_style}/{phoneme_source}/{variation}
예: bih/KOR/common/manual/vanilla (4번째 자리는 phoneme_source: manual/ip_info/ip_info2/raw 등 — phoneme_encoding(예: ko_ipa) 과 혼동 금지; 5번째 variation 은 variant 분할/처리 식별자 — vanilla default, 5h/10h/12h/20h ablation, 2023 maintenance — ADR-0012 catalog v1)
phoneme_encoding / phoneme_source_version / preprocessing / data_source
는 variant 내부의 processed_versions[] 로 내려가서 한 variant 아래 여러
전처리 조합을 병렬 관리한다.
모든 명령은 harness xdata … 또는 직접 xdata … 로 호출 가능.
harness xdata parse \
--project xvoice3 \
--registry /path/to/registry.v2.json \
--source spk2path \
--file /HDD0/data_list/spk2path.tsv \
--storage-root nfs_train:/HDD0/TRAIN_DATA_S98_01 \
--server server_162
harness xdata modify \
--project xvoice3 \
--registry /path/to/registry.v2.json \
--key bih/KOR/common/manual/vanilla \
--set inclusion_policy=excluded \
--set 'notes=["outlier"]'
--set field=value 는 값을 JSON 으로 파싱 시도 후 실패하면 문자열로 저장한다.
# 미리보기
harness xdata update --project xvoice3 --registry REG.json \
--source spk2path --file NEW.tsv --dry-run
# 적용
harness xdata update --project xvoice3 --registry REG.json \
--source spk2path --file NEW.tsv
harness xdata export --project xvoice3 --registry REG.json \
--format spk2path --out /tmp/spk2path.tsv --json
--json 은 {path, records, format} 메타만 stdout 으로 내보낸다 (본문은 파일).
harness xdata query --project xvoice3 --registry REG.json -f language=KOR --json
harness xdata view --project xvoice3 --registry REG.json --json
harness xdata schemas list --json
3 가지 모드 (정본 = metadata.sources[] 등록된 source 단위):
| 플래그 | 동작 | removed 처리 |
|---|---|---|
--all | metadata.sources[] 전 source reconcile | added/modified/removed 모두 |
--source NAME | 단일 source 만 reconcile (cross-source pv 보존) | added/modified 만 (removed 무시) |
--root TAG:/abs/path | 임시 root 1개 — deprecated, 신규 source 등록은 walker plugin + metadata.sources[] 권장 | added/modified/removed |
# 0) 백업 — 1회성 정정 시 .backups/{YYYY-MM-DD_label}/ 컨벤션
# (registry pre-snapshot + NocoDB JSONL pair). 정기 ingest 는 nocodb_backup 만.
python -m tools.scripts.nocodb_backup --table dataset_variants --out-dir backups/nocodb
# 1) dry-run — added/modified/removed/pending_count + pending_report 경로 + sources_summary
harness xdata fs-sync --project xvoice3 --all \
--registry /HDD1/raymond/xvoice3/data_list/registry.v3.json \
--dry-run
# 2) 적용 — accepted 만 upsert, pending 은 `pending_<ts>.json` 로 항상 분리
harness xdata fs-sync --project xvoice3 --all \
--registry /HDD1/raymond/xvoice3/data_list/registry.v3.json \
--reason "daily ingest 2026-05-20"
# 3) 단일 source 만 (다른 source 의 PV 는 보존, removed 무시)
harness xdata fs-sync --project xvoice3 --source xvoice3_main_local \
--registry /HDD1/raymond/xvoice3/data_list/registry.v3.json
경로 규약은 wiki/tech/fs-path-convention.md §5 (R1~R8) 정본.
알려진 한계 (2026-05-20): --all 또는 --root 의 removed 는 registry 에서는 즉시 제거되지만 NocoDB upsert 대상에는 포함되지 않아 (cli.py upsert_targets = added + modified) NocoDB row 가 stale 로 잔존한다. 1회성 정정 시 delete_records_bulk 수동 정리 필요. 코드 트랙 → plan §9.3 묶음 D 큐 D-vi.
harness xdata migrate --registry OLD.json --out NEW.json
parse/modify/update 는 tools.integrations.nocodb.writer.upsert_with_spool
로 dataset_variants (table_id: meeukod33twsor6) 에 natural_key 기준
write-through 한다 (GET-then-PATCH-or-POST, 중복 적재 차단 — fs-sync 와 동일 패턴).
$HARNESS_NOCODB_SPOOL_DIR (기본 ~/.harness/spool/) 에 JSONL 로 적재된다.
각 record 는 {"spooled_at", "row", "unique_column"} 형태로, unique_column
은 replay 가 upsert key 복원에 사용한다 (audit table 처럼 event_id 기준
스키마의 silent merge 방지).HARNESS_NOCODB=disabled 면 즉시 스풀로 direct.python -m tools.integrations.nocodb.replay [--table ID] [--json].
구 format record (pre-PR #41) 는 unique_column 메타가 없어 replay 가
_LEGACY_UNIQUE_COLUMN_FALLBACK (replay.py 정본) 매핑에 의존 — 등록 안 된
테이블의 record 는 skip+warn 으로 spool 에 보존된다.컬럼 매핑:
natural_key / project / speaker_key / inclusion_policy → 단순 필드context_profile → SingleSelect (운영자-curated, AI_HUB walker auto-emit + xvoice3 fs-sync preservation)processed_versions / derived_from / tags / notes / attrs → JSON 문자열(LongText)processed_versions[].paths 의 키 의미 (server_tag 정본, 2026-05-19 정정):
nfs_train — NFS 마운트의 절대 경로 (예: /HDD0/TRAIN_DATA_S98_01/...)server_162 — 162 서버 로컬 사본 경로 (예: /HDD1/raymond/data/...)dataset_variant_changesxdata 의 변경 이력은 NocoDB tts-data/dataset_variant_changes 테이블에 이벤트
row 로 기록된다 (table_id mryfgqxoe1643yg, unique=event_id). 현재 구현 기준
emit 경로는 fs-sync apply (sync_run 1건/실행) 만 배선되어 있으며,
parse/modify/update 등 다른 CLI write 경로는 추후 PR 에서 배선 예정 — 그 사이
개별 variant/pv timeline 은 dataset_variants 의 inverted
(variant|pv).last_change_event_id 로 추적한다.
설계 근거: docs/superpowers/plans/2026-04-24-registry-audit-log.md.
before / after 컬럼은 LongText 에 JSON 문자열로 저장. 빈 칸 = null (NocoDB
Text nullable 관례) — consumer 는 cell and json.loads(cell) 패턴으로 파싱.
| event_type | 트리거 | 현재 배선 |
|---|---|---|
variant_added / variant_removed | natural_key 신규·삭제 | 미배선 (audit.diff_variants 유틸만 제공) |
variant_field_changed | inclusion_policy/tags/notes/derived_from 등 variant 최상위 필드 변경 | 미배선 |
pv_added / pv_removed | processed_version 추가·삭제 (version_key 기준) | 미배선 |
pv_field_changed | preprocessing/paths/experimental 등 pv 필드 변경 | 미배선 |
schema_migration | 마이그레이션·스키마 변경 수동 기록 | 수동 (audit.emit) |
legacy_import | 과거 registry_changelog.jsonl import | import_legacy_changelog 1회성 |
sync_run | fs-sync 1회 batch 요약 (added_count/modified_count/removed[키]/removed_count/pending_count/root + sources_summary per-source added/modified/pending) | fs-sync apply (자동) |
apply 경로 — fs-sync 1회당 sync_run 배치 이벤트 1건을 audit.emit 으로 기록한다.
변경된 variant/pv 에는 동일 event_id 가 variant_last_change_event_id /
processed_versions[*].last_change_event_id 로 tagging 되어 timeline 추적이 가능하다.
--reason 옵션으로 이벤트 사유를 명시할 수 있다 (기본값: "fs-sync").
harness xdata fs-sync --project xvoice3 \
--root server_162:/HDD0/TRAIN_DATA_S98_01 \
--registry /HDD1/raymond/xvoice3/data_list/registry.v3.json \
--reason "daily ingest 2026-04-24"
fs-sync 자동 경로(sync_run) 밖에서 NocoDB 를 직접 변경(client.upsert_record/
delete_records, 1회성 정정·복구)하면 audit 이 자동으로 남지 않아 추적 공백이
생긴다. 직접 변경 시 같은 작업에서 audit 을 emit 한다:
from tools.xdata import audit
# 예: registry 에는 있으나 NocoDB 에 누락된 variant 를 복구 등록 (old=None → variant_added)
events = audit.diff_variants(None, variant,
reason="운영자 수동 복구 — …", source="cli:manual-restore")
audit.emit_all(events, client=client) # dataset_variant_changes 에 기록
variant_added, 삭제=variant_removed, 필드 변경=variant_field_changed
(diff_variants 가 old/new 비교로 자동 분기).event_type SingleSelect 옵션도 함께 추가해야
한다 — 코드 EVENT_TYPES 에만 있고 옵션에 없으면 write 가 거부되어 spool 에
적체된다 (variant_natural_key_migrated 838건 적체 사례).registry-manage SKILL §I 참조.python -m tools.scripts.import_legacy_changelog --dry-run
python -m tools.scripts.import_legacy_changelog --apply
event_id 는 (timestamp, natural_key, event_type, payload) 해시 → 재실행해도
upsert no-op. 기본 입력 경로는 /HDD1/raymond/xvoice3/data_list/registry_changelog.jsonl.
python -m tools.scripts.nocodb_bootstrap_audit --probe
python -m tools.scripts.nocodb_bootstrap_audit --apply
프로젝트 스키마는 배포 패키지의 [project.entry-points."xdata.schemas"]
섹션으로 등록한다. 예 (xvoice3/pyproject.toml):
[project.entry-points."xdata.schemas"]
xvoice3 = "xdata_plugin.schema"
해당 모듈은 탑레벨에서 @register_schema 를 호출해야 list_schemas() /
CLI --project 에서 인식된다. 허용 목록을 좁히려면 XDATA_SCHEMAS="xvoice3"
환경변수 사용. 테스트·실험용 명시 모듈 로딩은 XDATA_SCHEMAS_MODULES.
| 증상 | 원인 | 조치 |
|---|---|---|
unknown project | 스키마 플러그인 미등록 | pip install -e <worker-repo> 후 harness xdata schemas list 확인 |
| NocoDB 400 (SingleSelect) | inclusion_policy 에 정의 외 값 | default / opt_in / excluded 중 선택 |
| write-through 가 조용히 누락 | 네트워크 차단 | ~/.harness/spool/*.jsonl 확인 → replay |
harness xdata --help 가 harness 헬프만 | argparse REMAINDER 동작 | harness xdata <subcmd> --help 로 확인 |
--json 은 체이닝 수요가 있는 하위명령(query/view/schemas + export 메타) 에만 달렸다. 쓰기·마이그레이션은 체이닝 대상 아님 (플랜 Q9).pytest tests/xdata/ tests/test_nocodb_*.py.