Skip to content

feat: prepare fable-session 0.3.0b1 public beta#1

Merged
askclaw-vesper merged 2 commits into
mainfrom
feat/0.3.0-beta1
Jul 14, 2026
Merged

feat: prepare fable-session 0.3.0b1 public beta#1
askclaw-vesper merged 2 commits into
mainfrom
feat/0.3.0-beta1

Conversation

@askclaw-vesper

Copy link
Copy Markdown
Collaborator

Summary

  • bump the package to 0.3.0b1 and remove the deprecated 0.1 console aliases;
  • add optional validated Claude max_budget_usd pass-through;
  • move task text off process argv into hash-verified 0600 payload files and stdin;
  • report bounded usage in watch/audit while preserving evidence-integrity precedence;
  • add clean-history, packaging, offline-install, CI and attested-prerelease gates;
  • document tested platform support, privacy limits and pre-PyPI installation.

Security and compatibility

  • no runtime dependencies;
  • no fallback, retry, resume, bypass or automatic stop behavior;
  • old 0.1 manifests and 0.2 project registries remain readable;
  • task content is absent from process argv; the brief remains same-UID/process-visible and is documented;
  • release workflow does not publish to PyPI.

Verification

  • python3.12 -m compileall -q src tests
  • 506 unit tests passed independently on the implementation host and access host
  • tracked-tree public-readiness scan passed
  • full reachable-history scan passed: 3 commits / 75 blobs
  • offline installation smoke passed
  • wheel/sdist build and clean installed CLI smoke passed
  • real Claude Code 2.1.208 run passed with stdin task delivery and --max-budget-usd 0.5; actual cost $0.154
  • independent Fable review: APPROVE_TO_PUSH
  • Fable Max simplification audit: SHIP_BETA_FIRST

Release sequence

After CI passes and this PR is merged: flip the repository public, immediately enable private vulnerability reporting, apply branch protection, then create tag v0.3.0-beta.1. The tag workflow must attest artifacts and create a GitHub prerelease. PyPI remains out of scope.

askclaw-vesper and others added 2 commits July 14, 2026 11:16
Budget pass-through: optional registry key max_budget_usd, validated as a
finite positive plain-decimal TOML number before command construction;
when set, exactly one --max-budget-usd argument is emitted, printed in the
redacted summary, and recorded as structural manifest metadata (truthful
null when absent). 0.2 registries without the key stay valid.

Command-length/privacy repair: prompt payloads no longer ride the tmux
command line. The runner writes run-scoped 0600 payload files
(brief.payload/task.payload) from its single scanned read via the held
reservation FD (new state.ReservedRun.write_private_file); the capture
wrapper verifies each payload's sha256 and delivers the brief via
exactly-one placeholder substitution and the task via Claude's stdin
(-p with no inline prompt). Fixes the verified 7,745-byte-task +
8,072-byte-brief `tmux: command too long` launch failure (regression
test bounds the tmux argv). Dry runs leave no payload material; hashes,
read-once bytes, redaction, and fail-closed state semantics preserved.

Privacy-safe usage reporting: bounded modelUsage aggregates (input/output/
cache-read/cache-creation tokens, web-search requests, total_cost_usd)
across models in audit --format json, audit text, and terminal watch
events when structurally available. Missing usage is an explicit null;
malformed usage is null plus integrity reason malformed_model_usage and
never falsely proves purity. No prompt/result/tool text in outputs,
events, or state.

Release mechanics: version 0.3.0b1 (prerelease tag v0.3.0-beta.1); the
three deprecated 0.1 console aliases are removed after their one
migration release (old-manifest reading still works); SPDX MIT metadata
with license-files, README long description, honest classifiers, and
setuptools>=77; README install section (pipx/uv tool/isolated pip,
prerequisites, Linux + Claude Code 2.1.208 as the tested combination),
updated privacy contract, release notes, and contribution guidance;
PUBLIC_RELEASE_CHECKLIST records the reviewed clean root commit, resolves
the export blocker, keeps the full-history gate, and corrects the
private-to-public order (vulnerability reporting is enabled and verified
immediately AFTER going public); new deterministic full-history readiness
scan reusing the tracked-tree policy; CI pinned to verified action SHAs
and extended (full history, wheelhouse-seeded offline smoke, build, wheel
install smoke, whitespace check); new tag-triggered attested prerelease
workflow (SHA256SUMS, GitHub artifact attestation, prerelease-only, PyPI
out of scope); offline smoke gains FABLE_SESSION_WHEELHOUSE and a
PEP-639-capable backend floor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pinned tooling, truthful payload state

Five approved follow-ups on the reviewed public-beta candidate:

- ci.yml: top-level least-privilege `permissions: contents: read`
  (no job widens it), enforced by a new YAML-parsing test.
- release.yml: fail-closed tag/package-version consistency gate
  (tests/release_tag_check.py) runs before every gate/build/publish
  step; only v0.3.0-beta.1 may release 0.3.0b1, any unmapped version
  shape refuses outright. Step ordering is asserted on the parsed
  step list, not raw text (an intro comment mentioning SHA256SUMS
  previously shadowed the real gate position).
- README: pre-PyPI install wording — every install command is
  tag-pinned GitHub source, unqualified index installs are banned by
  test, and the warning (with a contiguous "tag exists" availability
  claim) precedes every command.
- CI/release Python tooling pinned to tested versions (PyYAML 6.0.2,
  build 1.5.1, setuptools 83.0.0, wheel 0.47.0; --no-isolation build).
  The pin test inspects only executable workflow lines, so comments
  documenting the pyproject `setuptools>=77` floor are not findings.
- runner: truthful backward-compatible `payload_files_created`
  manifest field — False for every dry-run/pending/payload-failure
  record, True only after BOTH run-scoped payload files exist; old
  manifests without the key stay fully readable.

Credential scanner stays fail-closed and unrelaxed; the two tracked-
tree false positives were fixed at the source (a `token:`-shaped CI
comment reworded; a `token` variable in the pin helper renamed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@askclaw-vesper askclaw-vesper merged commit efff572 into main Jul 14, 2026
2 checks passed
@askclaw-vesper askclaw-vesper deleted the feat/0.3.0-beta1 branch July 14, 2026 12:40

@adaptcom adaptcom Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confidence Score: 4/5

Summary

Well-executed beta-hardening PR. Verified independently in a sandbox: all 506 unit tests pass on the PR head, compileall is clean, the tracked-tree and full-history readiness scans pass (3 commits / 75 blobs), and the tag gate accepts only v0.3.0-beta.1. All five workflow action SHA pins were confirmed to match their claimed v4/v5/v3/v2 tags via the GitHub API. The payload redesign (0600 O_EXCL/O_NOFOLLOW files written through a held directory FD, sha256 verification in the capture wrapper, task via stdin, fail-closed exit codes) is sound and honestly documented, budget validation is strict and pre-argv, and usage reporting preserves evidence-integrity precedence. Remaining findings are minor: an avoidable verify-then-reread gap for the brief payload, a subtle invented-zero cost in usage aggregation, and hardcoded version strings in CI. No blockers to merge.

Important Files Changed

File Overview
.github/workflows/ci.yml Least-privilege permissions (contents: read), actions pinned to verified tag SHAs, full-depth checkout for history scan, new history/whitespace/offline-smoke/build gates, wheel-based CLI smoke asserting removed aliases. Hardcodes 0.3.0b1 in a grep; on: push + pull_request double-runs PR branches.
.github/workflows/release.yml New tag-triggered prerelease workflow: fail-closed tag/version gate first, re-runs all release gates, pinned build toolchain, SHA256SUMS, GitHub artifact attestation, always-prerelease gh-release. Pins verified; permissions scoped to what release needs. No PyPI publication, matching the stated scope.
PUBLIC_RELEASE_CHECKLIST.md Checklist advanced truthfully: clean-root export/review recorded as done, visibility flip and post-flip vulnerability-reporting enable kept as unchecked blockers, tag/attestation sequence documented.
README.md Adds install docs with a clear pre-PyPI warning against unqualified index installs, documents payload-file delivery and the remaining brief-in-argv same-UID visibility, max_budget_usd semantics, usage reporting rules, alias removal, and release notes. Accurate against the code.
SECURITY.md Supported-versions line updated to the 0.3.x line. Fine.
pyproject.toml 0.3.0b1, PEP 639 SPDX license + license-files, explicit empty dependencies, honest Linux-only classifier, 0.1 console aliases removed, setuptools>=77 floor. Consistent with CI's pinned toolchain.
src/fable_session/init.py Version bump to 0.3.0b1 with tag-mapping comment.
src/fable_session/capture.py Adds hash-verified payload delivery: O_NOFOLLOW regular-file opens, sha256 checks before any side effect, brief substituted for a strictly-counted placeholder, task attached as stdin, new EXIT_PAYLOAD, FD hygiene on all paths. One nit: the brief is hashed then re-read (see finding).
src/fable_session/config.py Optional max_budget_usd with strict validation (finite, positive, non-bool, plain-decimal round-trip) enforced before any command construction; 0.2 registries without the key stay valid. Solid.
src/fable_session/jsonl.py Bounded modelUsage aggregation with strict type checks; malformed usage is a sticky integrity failure that forfeits PURE; missing usage is explicit null. One nit: total_cost_usd can report an invented 0.0 (see finding).
src/fable_session/model_audit.py Legacy alias removed; text output renders usage or an explicit unavailability line. Clean.
src/fable_session/runner.py Command is payload-free by construction; budget flag emitted exactly once from the canonical string; payload files written from the once-read text before tmux with fail-closed error recording; payload_files_created flips to true only after both writes succeed. Ordering and failure paths are truthful.
src/fable_session/state.py New write_private_file: allowlisted single-component name, O_EXCL|O_NOFOLLOW 0600 create via the held run-dir FD, full write + fsync, fail-closed StateError. Correct.
src/fable_session/watchdog.py Legacy alias removed; terminal events carry the bounded usage aggregate only when structurally available. Consistent with jsonl semantics.
tests/full_history_readiness_check.py New full-history scan reusing the tracked-tree policy verbatim; dedupes blobs, skips binary. Passed here on the PR head.
tests/offline_install_smoke.py Wheelhouse-seeded setuptools>=77 path with no silent online fallback; asserts SPDX metadata, version, alias removal. Hardcodes 0.3.0b1 (same maintenance nit as CI).
tests/public_readiness_check.py Policy updated for 0.3.0b1: SPDX license form, empty-dependencies pin, alias-removal enforcement, checklist state transitions, and a narrow credential-regex exemption for workflow permission grants (write|read|none only, word-bounded — acceptable).
tests/release_tag_check.py Fail-closed beta-only tag mapping; refuses every unmapped version shape. Verified: accepts v0.3.0-beta.1, rejects v0.3.0.
tests/test_capture.py New coverage for payload parsing, placeholder discipline, hash mismatch, symlink/non-regular refusal, stdin delivery, and the runner/capture placeholder-constant pin.
tests/test_config.py Budget validation coverage: bools, negatives, zero, inf/nan, exponent forms, canonical formatting.
tests/test_full_history_readiness.py Unit contract for the history scanner including dirty-old-blob detection in a synthetic repo.
tests/test_jsonl_audit.py Usage aggregation coverage: bounded totals, malformed-usage integrity precedence, multiple-results ambiguity.
tests/test_migration_fable_session.py Updated for alias removal and 0.3.0b1 while pinning that 0.1 manifests and 0.2 registries stay readable.
tests/test_mit_release.py Packaging truth checks updated: SPDX form, readme, classifiers, single console script.
tests/test_model_audit_cli.py Audit CLI usage-output coverage and alias-removal assertions.
tests/test_public_readiness.py Extended contract tests for the updated policy including the permission-grant regex exemption staying narrow.
tests/test_release_tag_gate.py Thorough negative-space coverage of the tag mapping (post/dev/rc/whitespace/non-canonical forms all refused).
tests/test_runner.py Large new coverage: payload-free argv, budget flag emission, payload write failure recording, payload_files_created truthfulness across dry-run/pending/failed/launched records.
tests/test_skeleton.py Version pin bump.
tests/test_state.py write_private_file coverage: name allowlist, exclusive create, symlink refusal, 0600 mode.
tests/test_watchdog.py Terminal-event usage summary coverage under the availability rule.

View in Adapt

options["--brief-payload"], options["--brief-sha256"], "brief"
)
try:
brief_bytes = b""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The brief payload is read twice: _open_verified_payload hashes the file, then main re-reads the FD for the bytes actually delivered. A same-UID writer modifying the inode between the two passes gets unverified bytes substituted into the Claude argv despite the hash check. This gap is avoidable at zero cost: read the bytes once, hash that in-memory buffer against --brief-sha256, and deliver exactly those bytes (the task-payload stdin FD cannot get the same treatment, but the brief can). The module docstring acknowledges the window generally, yet the single-pass fix would close it entirely for the brief.

or not math.isfinite(value) or value < 0:
return None, True
cost += value
totals["total_cost_usd"] = cost

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

total_cost_usd is initialized to 0.0 and reported even when no modelUsage entry carries a costUSD field (or modelUsage is an empty dict), so a present-but-costless modelUsage yields an invented $0.00 — in tension with the README/docstring claim that zeroes are never invented. Consider tracking whether any costUSD was seen and emitting null (or omitting the key) when none was, mirroring the treatment of missing modelUsage.

Comment thread .github/workflows/ci.yml
python3 -m pip install --quiet .
python3 -m pip install --quiet dist/*.whl
fable-session --version
fable-session --version | grep -F "0.3.0b1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expected version '0.3.0b1' is hardcoded here (and twice in tests/offline_install_smoke.py), so every release bump requires touching CI and the smoke in lockstep or the pipeline goes red on an otherwise-correct bump. Consider deriving the expected version from pyproject.toml (e.g. python3 -c "import tomllib; ...") so the single source of truth is the file release_tag_check.py already gates on. Separately, 'on: push + pull_request' double-runs CI for PR branches pushed to this repo; restricting push to main (or tags) would halve CI noise without losing coverage.

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