mirror of
https://github.com/theclaymethod/unslop.git
synced 2026-09-19 01:45:09 +08:00
db7cc058c3
15 plans written against 217c218, executed by dispatched agents in isolated
worktrees, reviewed and approved individually, integrated and battery-proven
on advisor/integration. plans/README.md carries the full status ledger, the
findings surfaced during execution, and the one known follow-up (in-process
stdin shim needs a .buffer for 011's full speed win).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
190 lines
8.6 KiB
Markdown
190 lines
8.6 KiB
Markdown
# Plan 001: Make the advertised Python 3.8+ floor true, and test it in CI
|
|
|
|
> **Executor instructions**: Follow this plan step by step. Run every
|
|
> verification command and confirm the expected result before moving to the
|
|
> next step. If anything in the "STOP conditions" section occurs, stop and
|
|
> report — do not improvise. When done, update the status row for this plan
|
|
> in `plans/README.md`.
|
|
>
|
|
> **Drift check (run first)**: `git diff --stat 217c218..HEAD -- scripts/ evals/ README.md .github/workflows/evals.yml`
|
|
> If any in-scope file changed since this plan was written, compare the
|
|
> "Current state" excerpts against the live code before proceeding; on a
|
|
> mismatch, treat it as a STOP condition.
|
|
|
|
## Status
|
|
|
|
- **Priority**: P1
|
|
- **Effort**: S
|
|
- **Risk**: LOW
|
|
- **Depends on**: none
|
|
- **Category**: bug
|
|
- **Planned at**: commit `217c218`, 2026-07-06
|
|
|
|
## Why this matters
|
|
|
|
`README.md` promises "Python 3.8+ and the standard library" (lines 48 and 455),
|
|
but 17 Python files use PEP 604 (`str | None`) and PEP 585 (`dict[str, ...]`)
|
|
annotations in positions evaluated at import time, with no
|
|
`from __future__ import annotations`. On Python 3.8/3.9 the flagship scanner
|
|
`scripts/banned_phrase_scan.py` raises `TypeError` at import — the primary
|
|
entry point is broken on two advertised platforms. CI pins only Python 3.11,
|
|
so no gate can ever catch this. This plan makes the floor claim true and adds
|
|
a CI leg at the floor so it stays true.
|
|
|
|
## Current state
|
|
|
|
- `scripts/banned_phrase_scan.py:82` — module-level evaluated annotation:
|
|
```python
|
|
BANNED_PHRASES: dict[str, dict[str, str | None]] = {
|
|
```
|
|
and there is no `from __future__ import annotations` anywhere in the file.
|
|
- The full list of files with evaluated modern annotations and **no**
|
|
future-import (verified at 217c218):
|
|
`scripts/banned_phrase_scan.py`, `scripts/check_packs.py`,
|
|
`scripts/check_suggestions.py`, `scripts/diff_check.py`,
|
|
`scripts/extract_constraints.py`, `scripts/readability_metrics.py`,
|
|
`scripts/silhouette_scan.py`, `scripts/structure_scan.py`,
|
|
`scripts/suggest.py`, `scripts/validate_preservation.py`,
|
|
`scripts/voice_score.py`, `scripts/wiki_sync.py`,
|
|
`evals/build_shared_benchmark.py`, `evals/check_commands.py`,
|
|
`evals/check_gates_doc.py`, `evals/check_silhouette.py`, `evals/run_local.py`.
|
|
- The repo's own convention already exists: `scripts/harvest_samples.py`,
|
|
`scripts/harvest_classify.py`, `scripts/contribute.py`,
|
|
`scripts/calibrate_pairs.py`, `scripts/calibrate_score.py`, and
|
|
`evals/check_contrib.py` all begin with `from __future__ import annotations`
|
|
directly after the module docstring. Match that placement.
|
|
- `.github/workflows/evals.yml:15` — single CI leg, `python-version: "3.11"`.
|
|
- `README.md:267` and `README.md:374` say "439 deterministic cases" /
|
|
"439 cases"; the suite actually holds 440 `target=="script"` rows
|
|
(439 pass + 1 documented xfail, FP-06). `README.md:294` ("439 pass, 1 xfail")
|
|
is already correct. Fix the two imprecise sites while this plan is in the
|
|
README anyway.
|
|
|
|
## Commands you will need
|
|
|
|
| Purpose | Command | Expected on success |
|
|
|---------|---------|---------------------|
|
|
| Full suite | `python3 evals/run_adversarial.py` | exit 0; final line `PASS 439 XFAIL 1 ... FAIL 0` |
|
|
| Syntax check | `python3 -m py_compile scripts/*.py evals/*.py` | exit 0, silent |
|
|
| Scanner self-check on README | `python3 scripts/banned_phrase_scan.py README.md` | exit 0, `"total_violations": 0` |
|
|
|
|
## Scope
|
|
|
|
**In scope** (the only files you should modify):
|
|
- The 17 files listed above (one-line insertion each)
|
|
- `.github/workflows/evals.yml`
|
|
- `README.md` (two phrases only)
|
|
|
|
**Out of scope** (do NOT touch):
|
|
- Any annotation itself — do not rewrite `str | None` to `Optional[str]`;
|
|
the future-import fixes evaluation without churn.
|
|
- `docs/PRODUCT.md` and other references — no floor claim appears there
|
|
as of 217c218 (verify with `grep -rn "3\.8" docs/ references/`; if a claim
|
|
appears, update it identically).
|
|
- Anything in `evals/adversarial-evals.json`.
|
|
|
|
## Git workflow
|
|
|
|
- Branch: `advisor/001-python-floor` off `main`
|
|
- One commit; message style matches repo (`git log --oneline -5` shows
|
|
sentence-case imperative, e.g. "Add codex-jsonl harvester adapter…")
|
|
- Do NOT push or open a PR unless the operator instructed it.
|
|
|
|
## Steps
|
|
|
|
### Step 1: Add the future-import to the 17 files
|
|
|
|
In each listed file, insert `from __future__ import annotations` as the first
|
|
import, immediately after the module docstring (before any other import),
|
|
matching `scripts/harvest_samples.py`'s layout exactly.
|
|
|
|
**Verify**: `grep -L "from __future__ import annotations" scripts/*.py evals/*.py | xargs grep -l ": *\(dict\|list\|set\|tuple\)\[\|| None"` → no output
|
|
(every file that still lacks the import has no evaluated modern annotation).
|
|
|
|
### Step 2: Prove 3.8-compatible imports mechanically
|
|
|
|
Python 3.8 may not be installed locally; simulate the failure class instead:
|
|
for each of the 17 files, confirm the module compiles and that no *runtime*
|
|
subscripted generic remains outside annotation position:
|
|
|
|
**Verify**: `python3 -m py_compile scripts/*.py evals/*.py` → exit 0
|
|
**Verify**: `grep -n "get_type_hints" scripts/*.py evals/*.py` → no output
|
|
(nothing resolves annotations at runtime, so deferral is safe).
|
|
|
|
### Step 3: Add a floor leg to CI
|
|
|
|
In `.github/workflows/evals.yml`, convert the job to a matrix over
|
|
`python-version: ["3.8", "3.11"]`. Keep every existing step. The 3.8 leg
|
|
proves import-time compatibility via the existing
|
|
`python -m py_compile scripts/*.py evals/*.py` step **plus** add one new step
|
|
before it, so a plain compile pass can't hide import-time TypeErrors:
|
|
|
|
```yaml
|
|
- name: Import all scripts (annotation floor check)
|
|
run: |
|
|
for f in scripts/*.py evals/*.py; do python3 -c "import runpy, sys; sys.argv=['x','--help']" >/dev/null 2>&1; python3 - <<EOF
|
|
import importlib.util, pathlib, sys
|
|
p = pathlib.Path("$f")
|
|
spec = importlib.util.spec_from_file_location(p.stem, p)
|
|
m = importlib.util.module_from_spec(spec)
|
|
sys.path.insert(0, str(p.parent))
|
|
try:
|
|
spec.loader.exec_module(m)
|
|
except SystemExit:
|
|
pass
|
|
EOF
|
|
done
|
|
```
|
|
|
|
NOTE: some scripts execute argparse or read stdin at import only under
|
|
`if __name__ == "__main__"` — module import must not block. If any file hangs
|
|
or errors on import for a non-annotation reason, STOP (condition 3).
|
|
A simpler alternative acceptable here: a one-line
|
|
`python3 -c "import ast, pathlib; [compile(ast.parse(pathlib.Path(f).read_text()), f, 'exec') for f in ...]"`
|
|
does NOT catch evaluated annotations — the exec_module approach (or actually
|
|
running the suite on the 3.8 leg) is required. Running the full suite on both
|
|
legs is the simplest correct choice if runtime permits (~30s per leg).
|
|
|
|
**Verify**: `python3 -c "import yaml" 2>/dev/null || true; grep -c "3.8" .github/workflows/evals.yml` → at least 1
|
|
|
|
### Step 4: Fix the two README count phrasings
|
|
|
|
Change `README.md:267` "439 deterministic cases" →
|
|
"440 deterministic script cases (439 pass, 1 documented xfail)" and
|
|
`README.md:374` "Source of truth: 439 cases" → "Source of truth: 440 script
|
|
cases". Leave line 294 untouched.
|
|
|
|
**Verify**: `python3 scripts/banned_phrase_scan.py README.md` → `"total_violations": 0`
|
|
**Verify**: `grep -c "439 deterministic cases" README.md` → 0
|
|
|
|
## Test plan
|
|
|
|
No new eval rows: this is an import-time property no subprocess row can
|
|
express on a newer interpreter. The CI matrix leg IS the regression test.
|
|
Locally, the full suite run in Done criteria proves no behavior drift.
|
|
|
|
## Done criteria
|
|
|
|
- [ ] `python3 evals/run_adversarial.py` → exit 0, `PASS 439 XFAIL 1`, `FAIL 0`
|
|
- [ ] All 17 listed files contain `from __future__ import annotations`
|
|
- [ ] `.github/workflows/evals.yml` has a matrix including `"3.8"`
|
|
- [ ] `grep -rn "Python 3.8" README.md` still matches (the claim stands, now true)
|
|
- [ ] `git status` shows no files outside the in-scope list
|
|
- [ ] `plans/README.md` status row updated
|
|
|
|
## STOP conditions
|
|
|
|
- The "Current state" excerpt at `banned_phrase_scan.py:82` doesn't match.
|
|
- The suite fails after adding the future-imports (would mean some code
|
|
path resolves annotations at runtime — investigate, don't patch around).
|
|
- A module errors or hangs on bare import for a non-annotation reason.
|
|
- You find a `get_type_hints`/`typing.get_args` call site on these modules.
|
|
|
|
## Maintenance notes
|
|
|
|
- Every NEW script must carry the future-import until the floor is raised;
|
|
consider noting this in `AGENTS.md`'s conventions when next edited (out of
|
|
scope here).
|
|
- If the maintainer later decides 3.10+ is the real floor, delete the CI 3.8
|
|
leg and the README claim together in one commit — they are a pair.
|