chore: 문서를 작성할 때 한국어의 표현 작성 스킬 추가 및 1인칭 관점의 글 작성 검증 테스트 추가

This commit is contained in:
DongHyeonka
2026-07-29 16:48:03 +09:00
parent c39406bbdd
commit 41501b5d06
520 changed files with 95494 additions and 2231 deletions
+185 -9
View File
@@ -1,15 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
export PYTHONPATH="$ROOT/src${PYTHONPATH:+:$PYTHONPATH}"
VERIFY_DIR="$ROOT/.verify"
CORPUS="$ROOT/examples/corpus/llm-wiki-mini"
GOLDEN="$ROOT/examples/golden/application-core-spring-di-boundary.md"
APP_BRIEF="$ROOT/examples/briefs/application-core-spring-di-blog.json"
DEMO_DIR="$ROOT/examples/output/retry-policy-demo"
python3 -m unittest discover -s "$ROOT/tests" -v
rm -rf "$VERIFY_DIR"
mkdir -p "$VERIFY_DIR"
printf '\n== Unit and integration tests ==\n'
if python3 -c 'import coverage' >/dev/null 2>&1; then
python3 -m coverage erase
python3 -m coverage run --source="$ROOT/src/claridoc" -m unittest discover -s "$ROOT/tests" -v
python3 -m coverage report -m | tee "$VERIFY_DIR/coverage.txt"
else
python3 -m unittest discover -s "$ROOT/tests" -v
printf 'coverage package unavailable; coverage report skipped\n' | tee "$VERIFY_DIR/coverage.txt"
fi
printf '\n== Contract and local-corpus validation ==\n'
python3 -m claridoc validate \
--brief "$ROOT/examples/briefs/retry-policy-blog.json" \
--sources "$ROOT/examples/sources/retry-policy-sources.json"
python3 -m claridoc collect \
--root "$CORPUS" \
--query 'application-core Spring DI 선택 이유 대안 비용 가드레일' \
--query 'TransactionPort spring-tx 금지 ArchUnit 검증' \
--top-k 24 \
--output "$VERIFY_DIR/application-core-sources.json"
python3 -m claridoc validate \
--brief "$APP_BRIEF" \
--source-root "$CORPUS"
python3 -m claridoc outline \
--brief "$APP_BRIEF" \
--source-root "$CORPUS" \
--output "$VERIFY_DIR/application-core-outline.json"
python3 -m claridoc lint "$GOLDEN" \
--brief "$APP_BRIEF" \
--source-root "$CORPUS" \
--json \
--output "$VERIFY_DIR/application-core-golden-lint.json"
printf '\n== Offline end-to-end pipeline ==\n'
bash "$ROOT/scripts/run-demo.sh"
python3 - "$ROOT" <<'PY'
printf '\n== Static, schema, provenance, and leakage checks ==\n'
python3 - "$ROOT" "$VERIFY_DIR" <<'PY'
from __future__ import annotations
import ast
@@ -20,22 +64,47 @@ import sys
from pathlib import Path
root = Path(sys.argv[1]).resolve()
verify_dir = Path(sys.argv[2]).resolve()
python_files = sorted((root / "src").rglob("*.py")) + sorted((root / "tests").rglob("*.py"))
for path in python_files:
ast.parse(path.read_text(encoding="utf-8"), filename=str(path), feature_version=(3, 10))
ignored_parts = {"build", "dist", "__pycache__", ".git", ".verify"}
json_files = [
path for path in sorted(root.rglob("*.json"))
if not any(part in {"build", "dist", "__pycache__"} for part in path.parts)
path
for path in sorted(root.rglob("*.json"))
if not any(part in ignored_parts for part in path.parts)
]
for path in json_files:
json.loads(path.read_text(encoding="utf-8"))
schema_instances = 0
try:
import jsonschema
except ImportError as exc: # pragma: no cover - verification environment diagnostic
raise SystemExit(f"jsonschema is required by scripts/verify.sh: {exc}")
for schema_path in sorted((root / "schemas").glob("*.schema.json")):
jsonschema.Draft202012Validator.check_schema(json.loads(schema_path.read_text(encoding="utf-8")))
pairs = [
("schemas/brief.schema.json", "examples/briefs/retry-policy-blog.json"),
("schemas/brief.schema.json", "examples/briefs/application-core-spring-di-blog.json"),
("schemas/source-pack.schema.json", "examples/sources/retry-policy-sources.json"),
("schemas/source-pack.schema.json", ".verify/application-core-sources.json"),
("schemas/outline.schema.json", ".verify/application-core-outline.json"),
]
for schema_rel, instance_rel in pairs:
schema = json.loads((root / schema_rel).read_text(encoding="utf-8"))
instance = json.loads((root / instance_rel).read_text(encoding="utf-8"))
jsonschema.Draft202012Validator(schema).validate(instance)
schema_instances += 1
link_pattern = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
local_links = 0
for path in sorted(root.rglob("*.md")):
if any(part in {"build", "dist", "__pycache__"} for part in path.parts):
if any(part in ignored_parts for part in path.parts):
continue
for target in link_pattern.findall(path.read_text(encoding="utf-8")):
target = target.strip().split("#", 1)[0]
@@ -46,14 +115,60 @@ for path in sorted(root.rglob("*.md")):
if not resolved.exists():
raise SystemExit(f"broken local Markdown link: {path.relative_to(root)} -> {target}")
run_dir = root / "examples" / "output" / "retry-policy-demo"
collected = json.loads((verify_dir / "application-core-sources.json").read_text(encoding="utf-8"))
sources = collected.get("sources", [])
if not sources:
raise SystemExit("local corpus collection produced no sources")
first = sources[0]
first_text = "\n".join(first.get("facts", []))
if "수동 등록" not in first_text or "D13" not in first_text:
raise SystemExit("decision-rationale chunk did not rank first")
if Path(first.get("path", "")).is_absolute() or "/home/" in json.dumps(collected, ensure_ascii=False):
raise SystemExit("collected evidence leaked an absolute path")
if not any(item.get("source_type") == "canonical-project" for item in sources):
raise SystemExit("local corpus did not retrieve canonical current-state evidence")
if not any("SLF4J" in "\n".join(item.get("facts", [])) and "설명하지 않는다" in "\n".join(item.get("facts", [])) for item in sources):
raise SystemExit("negative evidence boundary for unsupported SLF4J rationale is missing")
golden_lint = json.loads((verify_dir / "application-core-golden-lint.json").read_text(encoding="utf-8"))
material = [item for item in golden_lint.get("issues", []) if item.get("severity") in {"blocker", "error"}]
if material:
raise SystemExit(f"golden example has material lint issues: {material}")
reader_docs = [
root / "examples/golden/application-core-spring-di-boundary.md",
root / "examples/output/retry-policy-demo/final/document.md",
]
forbidden = {
"internal source marker": re.compile(r"\[(?:S|L)[A-Za-z0-9_-]+\]"),
"evidence-pack narration": re.compile(r"제공된\s*(?:근거|자료)|근거\s*팩|확인\s*대상으로\s*제시"),
"access-date boilerplate": re.compile(r"예시는\s*20\d{2}-\d{2}-\d{2}\s*기준"),
"repository path": re.compile(r"(?:raw/branch-notes/|wiki/projects/|repo:///|/home/[^\s`]+)"),
}
for path in reader_docs:
text = path.read_text(encoding="utf-8")
for label, pattern in forbidden.items():
if pattern.search(text):
raise SystemExit(f"reader-facing leakage ({label}) in {path.relative_to(root)}")
if "SLF4J" in (root / "examples/golden/application-core-spring-di-boundary.md").read_text(encoding="utf-8"):
raise SystemExit("golden reader-facing example invented or exposed unsupported SLF4J rationale")
run_dir = root / "examples/output/retry-policy-demo"
run = json.loads((run_dir / "run.json").read_text(encoding="utf-8"))
if not run.get("passed"):
raise SystemExit("demo quality gate did not pass")
if not any("synthetic" in warning for warning in run.get("warnings", [])):
raise SystemExit("mock-run synthetic-score warning is missing")
required_artifacts = {"document", "quality_report", "provenance", "evidence_map", "outline", "events"}
if not required_artifacts.issubset(run.get("artifacts", {})):
raise SystemExit("run.json is missing reader/provenance artifact separation")
manifest = json.loads((run_dir / "manifest.json").read_text(encoding="utf-8"))
manifest_paths = {item["path"] for item in manifest["files"]}
for required in {"final/document.md", "final/provenance.md", "final/evidence-map.json", "final/quality-report.md"}:
if required not in manifest_paths:
raise SystemExit(f"manifest missing required artifact: {required}")
for item in manifest["files"]:
artifact = run_dir / item["path"]
if artifact.stat().st_size != item["bytes"]:
@@ -63,9 +178,70 @@ for item in manifest["files"]:
raise SystemExit(f"manifest hash mismatch: {item['path']}")
print(
"VERIFIED: "
"STATIC VERIFIED: "
f"{len(python_files)} Python files parse with Python 3.10 grammar; "
f"{len(json_files)} JSON files parse; {local_links} local Markdown links resolve; "
f"demo PASS at {run['final_score']:.1f}/100 (synthetic mock score); manifest hashes match."
f"{len(json_files)} JSON files parse; 5 schemas are valid and "
f"{schema_instances} representative instances validate; "
f"{local_links} local Markdown links resolve; corpus rationale ranking, "
"reader/provenance separation, forbidden-phrase regression checks, and manifest hashes pass; "
f"mock demo PASS at {run['final_score']:.1f}/100 (synthetic score)."
)
PY
printf '\n== Wheel build and clean-install smoke test ==\n'
rm -rf "$ROOT/build" "$ROOT/dist" "$ROOT"/*.egg-info "$ROOT/src"/*.egg-info
mkdir -p "$ROOT/dist"
python3 -m pip wheel "$ROOT" \
--no-deps \
--no-build-isolation \
--wheel-dir "$ROOT/dist" \
>"$VERIFY_DIR/pip-wheel.log"
WHEEL="$(find "$ROOT/dist" -maxdepth 1 -type f -name 'claridoc_harness-0.2.0-*.whl' -print -quit)"
if [[ -z "$WHEEL" ]]; then
echo "0.2.0 wheel was not produced" >&2
exit 1
fi
(
cd "$ROOT/dist"
sha256sum "$(basename "$WHEEL")" > SHA256SUMS
)
SMOKE_DIR="$(mktemp -d)"
trap 'rm -rf "$SMOKE_DIR"' EXIT
python3 -m venv "$SMOKE_DIR/venv"
env -u PYTHONPATH "$SMOKE_DIR/venv/bin/python" -m pip install --force-reinstall --no-deps "$WHEEL" >"$VERIFY_DIR/pip-install.log"
"$SMOKE_DIR/venv/bin/claridoc" --version | tee "$VERIFY_DIR/installed-version.txt"
"$SMOKE_DIR/venv/bin/claridoc" validate \
--brief "$APP_BRIEF" \
--source-root "$CORPUS"
"$SMOKE_DIR/venv/bin/claridoc" collect \
--root "$CORPUS" \
--query 'application-core Spring DI 선택 이유' \
--top-k 8 \
--output "$SMOKE_DIR/installed-sources.json"
"$SMOKE_DIR/venv/bin/claridoc" lint "$GOLDEN" \
--brief "$APP_BRIEF" \
--source-root "$CORPUS" \
--output "$SMOKE_DIR/installed-golden-lint.md"
"$SMOKE_DIR/venv/bin/claridoc" run \
--brief "$ROOT/examples/briefs/retry-policy-blog.json" \
--sources "$ROOT/examples/sources/retry-policy-sources.json" \
--config "$ROOT/config/pipeline.mock.json" \
--output "$SMOKE_DIR/installed-demo"
printf '\n== Provider availability diagnostic ==\n'
set +e
python3 -m claridoc doctor \
--config "$ROOT/config/pipeline.multi-agent.example.json" \
>"$VERIFY_DIR/doctor.txt" 2>&1
DOCTOR_STATUS=$?
set -e
cat "$VERIFY_DIR/doctor.txt"
if [[ "$DOCTOR_STATUS" -ne 0 && "$DOCTOR_STATUS" -ne 3 ]]; then
echo "doctor returned unexpected status: $DOCTOR_STATUS" >&2
exit "$DOCTOR_STATUS"
fi
printf '\nVERIFICATION COMPLETE\n'
printf 'Wheel: %s\n' "$WHEEL"
printf 'SHA-256: %s\n' "$(cut -d' ' -f1 "$ROOT/dist/SHA256SUMS")"