Skip to content

PRODUCE-PY P2f: the cold read's four fixes before 0.1.0 - #3

Merged
npstorey merged 3 commits into
mainfrom
ts135/p2f-coldread-fixes
Oct 4, 2026
Merged

npstorey merged 3 commits into
mainfrom
ts135/p2f-coldread-fixes

Conversation

@npstorey

@npstorey npstorey commented Oct 4, 2026

Copy link
Copy Markdown
Owner

This fixes four low findings from the cold read of P1 and P2 (npstorey/typedstandards#135, comment 5979743999): findings 1, 2, 3 and 4b. The owner ruled that these land before 0.1.0 is cut. No version heading is added and __version__ is unchanged; the release PR follows this one.

Branch: ts135/p2f-coldread-fixes · Head: 83c486853536777516b19da9741b71f18cd570fb · Base: main at 2a8ac5d
Size: git diff --numstat main...HEAD: 7 files, +246 −22.
Blast zone: this branch only. Files touched:

  • tests/guards.py
  • tests/test_guards.py
  • tests/test_badge.py
  • tests/test_readme.py (new)
  • src/typedstandards/_badge.py
  • README.md
  • CHANGELOG.md

_cli.py, _commands.py, _node.py, errors.py and the CI workflow are unchanged.

The fixes

  1. The digest guard catches re-import routes.
    • The static scanner now reports any route to a digest module outside pin.py: an imported alias (from .pin import hashlib), an attribute (pin.hashlib), a name, a string naming one, a sys.modules lookup that names one or uses a non-literal key, and import_module or __import__ with a non-literal name. The cold read's four routes are now in the offender tree.
    • A new run-time test records every hashlib constructor call by its calling frame while the five commands and every helper run. It fails on any typedstandards module other than pin; third-party frames are not counted. It also asserts that it saw pin's own call, so the recorder is shown to work.
    • hash_imports(PACKAGE, allow=frozenset()) still names only pin.py.
  2. The README's Node sentence is now true. Only sign, withdraw, attest, view, verify, cli_version(), and show without a result run the CLI and need Node. pin, badge_cell, comparison_cell, sidecar and show(record, result) run without it. A test pins both halves, with TYPEDSTANDARDS_NODE pointed at a missing file. The code was already correct, so this test passes before and after; the fix itself is the text.
  3. badge_cell accepts a host with a port. The date and time check now leaves out the URL's authority, so 192.168.1.10:8080 and records.example.org:8443 are accepted.
    • Measured on the way: the link carries the URL percent-encoded, which had hidden a time's colon. A time in the query (?t=12:30) or the fragment was accepted before this change, not refused.
    • The URL past its authority is now checked as written and percent-decoded, so those times are refused.
  4. The README has no relative links. The CLAUDE.md link is now absolute, and a test fails on any relative Markdown link or image (#anchor links are allowed).

Acceptance

Each red below was driven locally.

# Red Green
1 The old scanner returned [] over the four routes, and the offender-tree test failed on the four missing files. Temporary offending calls in _sidecar.py failed both guards. One of them, a route with names built at run time, failed only the run-time test. 4 guard tests passed
2 Documentation fix; the grep of the callers of locate_node and run is in the phase report test_the_helpers_need_no_node passed
3 https://192.168.1.10:8080/… was refused; a time in the query (raw and encoded) or the fragment was accepted 10 passed
4 The link test caught [CLAUDE.md](CLAUDE.md) 2 passed

Checks run locally on the head

From a clean clone at 83c4868, with UV_PYTHON set for each run:

  • Python 3.11.15, Node 24.21.0: 238 passed
  • Python 3.12.13, Node 24.21.0: 238 passed
  • Python 3.14.6, Node 24.21.0: 238 passed
  • Python 3.12.13, Node 22.23.1: 238 passed

ruff check and ruff format --check are clean. gitleaks git --log-opts="main..HEAD" scanned 3 commits and found no leaks. These runs were on macOS (arm64). CI has not run on this head yet: it runs once the branch is pushed.

There are three commits. Each is signed (G) and carries one Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>, equal to the author email.

Model: Claude Opus 5.5 (claude-opus-5-5).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ru4PYga7Zf8HANgotZKs4u

npstorey and others added 3 commits October 4, 2026 08:15
The static scanner now reports any route to a digest module outside
pin.py: an imported alias (from .pin import hashlib), an attribute
(pin.hashlib), a name, a string naming one, a sys.modules lookup that
names one or is not a literal, and import_module or __import__ with a
non-literal name. A run-time test records each hashlib constructor call
by its calling frame while the five commands and every helper run, and
fails on any typedstandards module but pin. The cold read's four routes
join the offender tree.

Red first: the old scanner returned [] over the four routes; temporary
offending calls in _sidecar.py failed the guards, including one with
names built at run time that only the run-time test caught.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru4PYga7Zf8HANgotZKs4u
Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
The date and time check now leaves out the URL's authority (the host
fact and the encoded host in the link), so 192.168.1.10:8080 no longer
reads as 10:80. It also checks the URL past its authority as written
and percent-decoded, so a time in the path, query or fragment is
refused: the cell carries the URL encoded, which had hidden a time's
colon from the check.

Red first: the IP-with-port URL was refused, and a time in the query
(raw or encoded) or fragment was accepted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru4PYga7Zf8HANgotZKs4u
Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
Only the five commands, cli_version() and show without a result run
the CLI; the README now says so, and that pin, badge_cell,
comparison_cell, sidecar and show(record, result) run without Node. A
test pins both halves with the Node override pointed at a missing file.
The CLAUDE.md link is absolute, and a test fails on any relative link
or image in the README. CHANGELOG entries under Unreleased.

Red first: the link test caught [CLAUDE.md](CLAUDE.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru4PYga7Zf8HANgotZKs4u
Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com>
@npstorey

npstorey commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

GO — CAT PLAN (the program seat), bound to head 83c486853536777516b19da9741b71f18cd570fb.

Read from GitHub and disk, 2026-10-04:

  • The PR API: head 83c4868…, base main 2a8ac5d (the P2 merge), 7 files, +246 −22, 3 commits, merge state clean; equal to the three-dot diff.
  • Check runs on that exact head: 18, all success from GitHub Actions (15368), nine distinct names, the nine protect-main requires.
  • Before the push: three commits, each signed (G) with one Signed-off-by read by the trailer parser; the rollback tag rollback/pre-produce-py-p2f annotated at 2a8ac5d; the guard's keyword scan over the 246 added lines found nothing, and gitleaks passed.
  • The load-bearing change, _badge.py: the host and port are left out of the date-or-time check, so 192.168.1.10:8080 is no longer read as 10:80; the part of the URL after the host is checked as written and percent-decoded, which closes the ?t=12:30 case the implementer found at 2a8ac5d. The cold read's four findings (record 5979743999) are each addressed: the digest guard catches the re-import routes and records digests at run time, the README names the calls that need Node, and its links are absolute.

The owner merges with --match-head-commit 83c486853536777516b19da9741b71f18cd570fb through the seat's tested script, which also tags the merge. Next: P2r, the version cut dated 2026-10-04, the publish script's dry run from a clean clone, and the release PR.

@npstorey
npstorey merged commit 36b59f3 into main Oct 4, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant