update documents

This commit is contained in:
tao.chen
2026-06-18 14:10:16 +08:00
parent a849d0b3de
commit 213b4db423
2 changed files with 106 additions and 20 deletions
+47 -19
View File
@@ -4,7 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## What this repo is
`skill-factory` is a collection of Claude Code **skills** that together implement a deterministic pipeline for turning a Chinese-language business data requirement into a reviewed PySpark SQL string. Each subdirectory at the repository root is a self-contained skill:
`opencode-build` is a Docker image build for the [`opencode-ai`](https://github.com/sst/opencode) CLI. The image:
- Installs Node.js 22, `uv`-managed Python 3.11, and `ripgrep`.
- Installs Python data-stack deps from `requirements.txt` (PySpark, pandas, polars, clickhouse-connect, pymongo, redis, hdfs, ...).
- Installs `opencode-ai` globally via npm.
- Copies `skills/` into `/root/.config/opencode/skills/` so they are auto-discovered by Claude Code at runtime.
The `skills/` directory contains a deterministic Chinese-language business-data → reviewed PySpark SQL pipeline. See [Pipeline architecture](#pipeline-architecture) for that. Most edits touch the skills, not the image plumbing.
```
requirements-analysis → metadata-validator → logic-planner → sql-context-builder → (SQL gen) → pyspark-sql-guardrails → sql-review
@@ -16,28 +23,36 @@ Every stage has a fixed **input/output contract** with one of two statuses: a *g
Status names are fixed strings (`READY_FOR_VALIDATION`, `VALIDATED`, `PLANNED`, `READY`, `PASS`, `FAIL`); re-check the actual status text, do not pattern-match on memory.
## Skill file format
Every skill is a single `SKILL.md` with YAML frontmatter (`name`, `description`) describing when to invoke the skill. New skills must follow this format; do not introduce other conventions. The first paragraph of `description` is the trigger surface — keep it concrete (input contract + trigger phrases).
## Hard rule: the validation gate
`pyspark-sql-guardrails/SKILL.md` enforces a non-negotiable gate: **every generated SQL string must pass through `assert_select_or_insert()` before it is shown to the user**. The pipeline ordering and the gate are independent — even if `sql-review` already passed, the SQL still has to pass through this guardrail.
The forbidden-behaviors table in `pyspark-sql-guardrails/SKILL.md` enumerates every rationalization for skipping the validator; treat the list as closed.
## Commands
### Build the Docker image
```bash
docker build -t opencode-custom:latest .
```
The `Dockerfile` is two-stage: a builder that fetches Node.js 22 and a Python 3.11 tarball via `uv`, and a final image that copies the artifacts, installs `opencode-ai` via npm, and copies `skills/` to `/root/.config/opencode/skills/`. The `ripgrep-*.tar.gz` file in the repo root is unpacked into `/opt/ripgrep-15.1.0-x86_64-unknown-linux-musl/` and symlinked to `/usr/local/bin/rg`.
### Run the image
```bash
docker run --rm -it opencode-custom:latest
```
The image's `ENTRYPOINT` is `opencode`.
### Validate a SQL string (the gate)
The validator lives at `skills/pyspark-sql-guardrails/scripts/validate_sql.py` in this repo. **In the built image, it is at `/root/.config/opencode/skills/pyspark-sql-guardrails/scripts/validate_sql.py`** — the Dockerfile copies `skills/` to that path.
```bash
# Pipe multi-line SQL via stdin (preferred)
cat <<'EOF' | python3 pyspark-sql-guardrails/scripts/validate_sql.py
# From the repo root — pipe multi-line SQL via stdin (preferred)
cat <<'EOF' | python3 skills/pyspark-sql-guardrails/scripts/validate_sql.py
SELECT 1 FROM dual
EOF
# Or single-line as first argument
python3 pyspark-sql-guardrails/scripts/validate_sql.py "SELECT 1 FROM dual"
python3 skills/pyspark-sql-guardrails/scripts/validate_sql.py "SELECT 1 FROM dual"
```
Exit codes:
@@ -45,19 +60,21 @@ Exit codes:
- `1``FAIL - <reason>` — stop, fix the SQL, re-run
- `2` → usage error (no SQL provided)
> ⚠️ The bundled `SKILL.md` and `validate_sql.py` / `test_sql_guard.py` docstrings reference `.claude/skills/...` paths from an earlier layout. Those paths are stale for this repo — use the `skills/` (host) or `/root/.config/opencode/skills/` (image) paths above.
### Run the validator's standard test set
```bash
# From project root
python3 pyspark-sql-guardrails/scripts/test_sql_guard.py
python3 skills/pyspark-sql-guardrails/scripts/test_sql_guard.py
# Or from the scripts directory
cd pyspark-sql-guardrails/scripts && python3 test_sql_guard.py
cd skills/pyspark-sql-guardrails/scripts && python3 test_sql_guard.py
```
Expected output: 6 `[OK] EXPECT_OK` lines, 12 `[OK] EXPECT_RAISE: raised as expected` lines, terminated with `ALL TESTS PASSED`. Run this on **any** change to `sql_guard.py` / `validate_sql.py`.
The canonical validator lives in `pyspark-sql-guardrails/scripts/sql_guard.py` and exports `assert_select_or_insert(sql)` and `safe_spark_sql(spark, sql)`. Do not redefine the regex inline at call sites; import from this module.
The canonical validator lives in `skills/pyspark-sql-guardrails/scripts/sql_guard.py` and exports `assert_select_or_insert(sql)` and `safe_spark_sql(spark, sql)`. Do not redefine the regex inline at call sites; import from this module.
### Validating metadata files
@@ -65,7 +82,17 @@ The canonical validator lives in `pyspark-sql-guardrails/scripts/sql_guard.py` a
### Evals
`requirements-analysis/evals/evals.json` contains 3 prompt/expected-output pairs for the requirements-analysis skill. There is no test runner — the file documents expected behavior for manual or harness-driven eval.
`skills/requirements-analysis/evals/evals.json` contains 3 prompt/expected-output pairs for the requirements-analysis skill. There is no test runner — the file documents expected behavior for manual or harness-driven eval.
## Skill file format
Every skill is a single `SKILL.md` with YAML frontmatter (`name`, `description`) describing when to invoke the skill. New skills must follow this format; do not introduce other conventions. The first paragraph of `description` is the trigger surface — keep it concrete (input contract + trigger phrases).
## Hard rule: the validation gate
`skills/pyspark-sql-guardrails/SKILL.md` enforces a non-negotiable gate: **every generated SQL string must pass through `assert_select_or_insert()` before it is shown to the user**. The pipeline ordering and the gate are independent — even if `sql-review` already passed, the SQL still has to pass through this guardrail.
The forbidden-behaviors table in `skills/pyspark-sql-guardrails/SKILL.md` enumerates every rationalization for skipping the validator; treat the list as closed.
## Pipeline architecture
@@ -83,7 +110,8 @@ The two non-pipeline skills (`logic-planner` taxonomy details, `sql-review` chec
## Working with this repo
- **Adding a new skill** — create `<skill-name>/SKILL.md` with the YAML frontmatter and follow the same input/output/status contract used by existing skills. If the skill emits SQL, it must be downstream of `pyspark-sql-guardrails` (or use it as a library).
- **Adding a new skill** — create `skills/<skill-name>/SKILL.md` with the YAML frontmatter and follow the same input/output/status contract used by existing skills. If the skill emits SQL, it must be downstream of `pyspark-sql-guardrails` (or use it as a library).
- **Modifying `sql_guard.py`** — run `test_sql_guard.py` after every change. The 18-case test set is the contract; do not weaken it to make a SQL string pass.
- **Pipeline status changes** — never collapse gates without user confirmation. The pipeline's value comes from each stage being a separate, re-runnable hand-off; inlining them couples them.
- **Triggers in `description:` frontmatter** — these are what Claude Code matches against user prompts. Be specific (input contract + concrete trigger phrases). Vague triggers cause skill over-invocation.
- **Stale path references** — the bundled SKILL.md / scripts use `.claude/skills/...` paths from an earlier layout. When updating validator instructions, prefer the actual paths (`skills/...` on host, `/root/.config/opencode/skills/...` in image).