Skip to content

PRODUCE-PY P1: the wrapper's core (typedstandards#135) - #1

Merged
npstorey merged 6 commits into
mainfrom
ts135/p1-core
Oct 3, 2026
Merged

npstorey merged 6 commits into
mainfrom
ts135/p1-core

Conversation

@npstorey

@npstorey npstorey commented Oct 3, 2026

Copy link
Copy Markdown
Owner

PRODUCE-PY phase P1 (npstorey/typedstandards#135; G0 record, comment 5970267273). This PR adds the
core of typedstandards, a thin Python package that runs @typedstandards/cli 0.2.0 as a child
process. The package holds no key, reads no seed, and computes none of the format's hashes.

Branch ts135/p1-core, head bb8c37d1e7f860e46b8fac888e6a78adb60ddccb, 6 commits on 66d44c0.
33 files changed, 3740 insertions. Blast zone: this repository, this branch only. typedstandards
(116882a) and the host template (70bfd18) were read with git show and not changed.

What lands

  • Scaffold. pyproject.toml (hatchling 1.32.4, requires-python >=3.11, runtime dependencies
    httpx and PyYAML declared for P2 and not imported), uv.lock, README.md, CHANGELOG.md
    (## Unreleased), CLAUDE.md, .claude/agents/impl.md and cold-read.md (each effort: high),
    .gitleaks.toml (the template's did:key allow rule), .github/workflows/ci.yml.
  • The vendored CLI (D1 = A). package.json and package-lock.json pin @typedstandards/cli
    0.2.0 exactly. hatch_build.py runs npm ci --omit=dev --ignore-scripts at every wheel build,
    standard or editable, drops npm's .bin symlinks, checks that each of the 6 packages carries a
    licence file, and ships the tree in the wheel (232 entries under _vendor/node_modules/; the
    sdist carries none). uv sync runs the same hook, so tests drive the tree a wheel ships.
  • The Node locator. TYPEDSTANDARDS_NODE, then node on PATH; node --version must be at
    least 20.19.0. Otherwise NodeLocatorError, whose message names 20.19 and TYPEDSTANDARDS_NODE.
  • Five pass-throughs: sign, withdraw, attest, view, verify. Each returns the CLI's
    stdout parsed as JSON. A mapping input goes on stdin (--input -); a str or PathLike is a
    path. view writes mappings to temporary files and removes them before it returns. verify
    passes --json unless full=False.
  • D9 = A. verify drops a top-level trustRegistry from a bundle (a document with
    packageHash) before the CLI sees it, and changes nothing else (typedstandards#136).
  • Exit codes. 1 to 4 raise VerificationError (with .document, the verdict verify prints),
    UsageError, SeedError and InternalError, under CliError (exit_code, stderr,
    command). Any other code raises the base class.
  • CLI_VERSION = "0.2.0", cli_version(), and __version__ = "0.1.0.dev0".

Acceptance

# Criterion Red Green
1 Golden replay: 9 envelope cases plus withdraws; serializedJson, contentHashSha256, envelopeHash or nodeId; TEST 1 seed for the self-certified case; fixture SHA-256 pinned v01-default's expected envelopeHash changed by one hex digit: 3 failed (the pinned SHA, the replay, the path replay) 14 passed
2 No seed, no env=, no hashlib: AST scans plus run-time capture an offending module for each scanner; env= added to the runner (static and run time fail); a concatenated seed-name read in the locator (only the run-time guard fails) 9 passed
3 Node floor: no Node, v20.18.0 stub, v20.19.0 stub red commit 9744147 (no locator) and the floor comparison removed: 4 failed 18 passed
4 Exits 1 to 4 to four classes, each driven (1, 2, 3 by the real CLI; 4 by a stub) red commit 9744147, and the mapping removed: 5 failed 7 passed
5 CLI_VERSION equals the vendored --version and package.json; D9 over the template's bundle CLI_VERSION = "0.2.1": 4 failed; the drop removed: 2 failed with the CLI's exit 2 4 and 6 passed
6 CI green on every cell; the wheel from a clean checkout passes the smoke check a wheel built without the hook: CliNotVendoredError smoke check passed

Locally, at the head: 75 passed on Python 3.11.15, 3.12.13 and 3.14.6 with Node 24.21.0, and on
3.12.13 with Node 22.23.1; ruff check and ruff format --check clean. The clean-checkout build
gave the same artifact hashes twice:

b206e2483e52ad815d4182267fe78e547aae6cee0d50c8a007710163415681ed  typedstandards-0.1.0.dev0-py3-none-any.whl
d5cc62e124e08b346454832c1ccf3340fd9872643d9509c373021e71dccf6483  typedstandards-0.1.0.dev0.tar.gz

CI runs only after the push, so this PR's runner results are read at the gate. Each criterion's
red and green were driven locally; the table above summarizes those transcripts.

CI checks, by name (for the ruleset)

  • test (py3.11, ubuntu-24.04, node 24)
  • test (py3.12, ubuntu-24.04, node 24)
  • test (py3.14, ubuntu-24.04, node 24)
  • test (py3.11, macos-15, node 24)
  • test (py3.12, macos-15, node 24)
  • test (py3.14, macos-15, node 24)
  • test (py3.12, ubuntu-24.04, node 22)
  • lint
  • wheel (build, install, smoke)

Fixture provenance

File Source Commit SHA-256 Byte-equal assertion
tests/fixtures/reference-golden.json typedstandards packages/produce-core/src/__fixtures__/reference-golden.json read at 116882a, last changed ea75a1d d2bcfc2bc017b07502b3b00c3aa16de402df134128a374b4582650b79fb501c1 test_golden.py::test_fixture_is_the_pinned_copy; each case asserts serializedJson byte for byte
tests/fixtures/first-note.bundle.json host template docs/bundles/first-note.bundle.json read at 70bfd18, last changed 26dff9b cb11d2a229c9695db6c7f4d6c9349ccee14f14af6c39844886480699ba2401ba test_d9.py::test_fixture_is_the_pinned_copy

Notes for review

  • On exit 0, the CLI's stderr (attention readings such as key_unbound) is logged at INFO on the
    typedstandards logger rather than dropped.
  • The two run-time guards fail with pytest.fail and name only argv and changed keys, so a
    failing run never prints environment values.
  • git log --format='%h %G? %s%n%(trailers:key=Signed-off-by)' main..HEAD: six commits, each G,
    each with one Signed-off-by: Nathan Storey <npstorey@users.noreply.github.com> equal to its
    author email.
  • gitleaks over main..HEAD: 6 commits scanned, no leaks found.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ru4PYga7Zf8HANgotZKs4u

npstorey and others added 6 commits October 3, 2026 11:12
pyproject.toml (hatchling, requires-python >=3.11, httpx and PyYAML declared
for P2), uv.lock, and package.json with package-lock.json pinning
@typedstandards/cli 0.2.0 exactly. hatch_build.py runs
npm ci --omit=dev --ignore-scripts at every wheel build, standard or editable,
and ships the tree with each package's licence inside the wheel.

The package runs the vendored entry file with node on PATH and returns the
CLI's stdout parsed as JSON: sign, withdraw, attest, view and verify. Not yet:
the Node floor and override, the exit-code mapping (every non-zero exit raises
the base CliError), and verify's trustRegistry drop (D9).

Fixtures: verbatim copies of typedstandards' reference golden (116882a) and the
host template's served bundle (70bfd18); tests/fixtures/README.md gives their
provenance. .gitleaks.toml is the template's did:key allow rule.

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>
Golden replay of the 9 envelope cases and the withdraws case; the static and
run-time guards (no seed variable, no env=, no digest module outside P2's pin);
the Node locator; exits 1-4; CLI_VERSION; D9's trustRegistry drop; the five
pass-throughs end to end.

Red at this commit, by design: the Node floor and override, the exit-code
mapping and the D9 drop are not implemented, so their tests fail at their
assertions. Every module imports and every test collects.

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 locator tries TYPEDSTANDARDS_NODE, then node on PATH, reads
node --version, and raises NodeLocatorError naming 20.19 and the override
when no Node is found or the one found is below 20.19.0.

Exits 1-4 raise VerificationError, UsageError, SeedError and InternalError,
which share the base CliError and carry the exit code and the CLI's stderr;
VerificationError also carries the verdict verify prints before exiting 1.
An unmapped code raises the base. On exit 0, the CLI's stderr (attention
readings) is logged at INFO on the typedstandards logger.

verify drops a top-level trustRegistry from a bundle and changes nothing else
(G0 D9 = A, typedstandards#136); the workaround goes when the wrapper pins a
CLI that accepts the key.

Two tests are corrected here: the malformed-seed case now sends a valid
withdraw input (the CLI checks the input before the seed), and the logging
case drives attest, whose key_unbound reading is printed with exit 0 (a
self-certifying sign prints nothing on stderr).

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>
A failing assertion's repr printed the recorded environment, which in a
developer's shell or on a CI runner can hold real secrets. The run-time guards
now fail with pytest.fail and name only argv and the keys that changed.

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>
ci.yml: test on Python 3.11, 3.12 and 3.14 x ubuntu-24.04 and macos-15 on
Node 24, plus ubuntu-24.04, 3.12, Node 22; lint; and a wheel job that builds
the sdist and the wheel from it, installs the wheel into a fresh environment
and runs scripts/smoke_wheel.py there. Actions pinned to commit SHAs measured
with git ls-remote; permissions: contents: read.

README: install and use, both Node floors, the key path, the D9 behaviour
with typedstandards#136, Windows untested. CLAUDE.md: this repository's
rules. .claude/agents/impl.md and cold-read.md adapted from typedstandards',
each pinning effort: high and naming this repository's checks.

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>
Both are declared for P2's helpers; P1 imports neither at module load.

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 3, 2026

Copy link
Copy Markdown
Owner Author

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

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

  • The PR API: head bb8c37d…, base main 66d44c0, 33 files, +3740 −0, 6 commits, merge state clean; equal to the three-dot diff.
  • Check runs on that exact head: 18, all success, the nine jobs of the push and pull_request runs, every one from GitHub Actions (15368).
  • Sign-off, read with the trailer parser: six commits, each signed (G) with one Signed-off-by equal to the author.
  • The load-bearing claims in the code: _cli.py:57 runs the CLI with the inherited environment (no env= anywhere under src/); nothing under src/ names the seed variable or imports hashlib; the D9 drop removes only trustRegistry, and only from a bundle (_commands.py:129-130).
  • tests/fixtures/reference-golden.json is byte-identical to typedstandards' at 116882a (SHA-256 d2bcfc2b…). The branch's .gitleaks.toml is byte-identical to typedstandards' and the template's. The outgoing diff names nothing of the adopter's.
  • The ORCH's G0 record (5970267273) carries the owner's rulings; the D6 hold is recorded (5970410764).

The owner merges with --match-head-commit bb8c37d1e7f860e46b8fac888e6a78adb60ddccb through the seat's tested script, which also tags the merge and creates D10's ruleset.

Also relayed by the owner on the seat's advice, to be recorded by the ORCH at its next gate: a short Fable cold read of P1 and P2 runs ahead of P2r, so 0.1.0 publishes after it.

@npstorey
npstorey merged commit 104448b into main Oct 3, 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