Skip to content

PR-004: Freeze Phantom Consumer Contract (contract-version 1.0.0)#1

Merged
wbuscombe merged 11 commits into
mainfrom
pr-004/phantom-contract-freeze
Jul 11, 2026
Merged

PR-004: Freeze Phantom Consumer Contract (contract-version 1.0.0)#1
wbuscombe merged 11 commits into
mainfrom
pr-004/phantom-contract-freeze

Conversation

@wbuscombe

Copy link
Copy Markdown
Owner

Summary

Freezes the interface consumer apps and the CI smoke job depend on, so Phantom can iterate freely behind it (PR-004 Step 2). No app/CI integration here — that is Step 3.

  • CONTRACT.md — frozen boot / config / health / artifact / versioning contract + non-goals, at contract-version: 1.0.0.
  • phantom.contract module — machine-checkable source of truth; exposes phantom.__contract_version__.
  • Intent fix: Phantom now guarantees PHANTOM_MODE=1 in every launched app's environment across all four runners (web / docker-compose / tui / desktop). Previously demo mode only activated if the shell or manifest set it, so a direct phantom run never triggered a consumer's demo mode.
  • tests/contract/ — 77 conformance tests verifying every contract promise.
  • docs/smoke-job-spec.md — spec for the reusable smoke job Step 3 will implement.
  • Version bump 0.4.0; CHANGELOG + README updated.

Test counts

  • Before: 567 collected (558 passed, 9 skipped).
  • After: 644 collected (635 passed, 9 skipped) — +77 contract tests.
  • ruff check / ruff format --check / mypy --strict all clean.

Note on branch history

This branch was based on fix/ci-commit-quality-gate (6 CI quality-gate commits that were ahead of main with no open PR), so this PR also lands that ready work. The final 3 commits are PR-004.

🤖 Generated with Claude Code

https://claude.ai/code/session_011aan6YK1rLYEUEfX2XB6QS

wbuscombe and others added 9 commits June 1, 2026 14:53
phantom run previously committed and pushed captures regardless of the
quality check ("warnings only, don't block"), so a blank/too-small/sensitive
frame could reach pushed git history with no gate. Orchestrator._publish now
blocks commit/push when any QualityReport has an error-severity issue, unless
--force (reuses the existing flag). Warning-severity issues stay advisory and
still publish. A blocked publish sets JobReport.blocked_by_quality and the CLI
exits non-zero. CI is unaffected (it runs with --skip-publish).

Adds 3 integration tests: block-on-failure (proving test), force-overrides,
warning-only-still-publishes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The reusable phantom-capture.yml uploaded docs/screenshots/ with if: always(),
making captured frames downloadable (world-readable on public repos, ~90d
retention) before any review and bypassing the pr gate. Now gated on success()
AND a new upload-screenshots-artifact input (default false).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Snapshots/rollback are a manual CLI tool, not an automatic safety net: the
orchestrator never auto-creates snapshots or rolls back, and rollback is
forward-only (git checkout into a new commit) so it cannot remove an
already-pushed frame. Corrects the SnapshotManager docstring, the 0.3.0
CHANGELOG entry, and the collaborator guide; adds Unreleased CHANGELOG notes
for the publish gate and artifact gating.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The publish quality gate from the prior PR does not run in CI, which uses
`phantom run --skip-publish` (the gate sits after the skip-publish early
return). So a bad/sensitive frame produced in CI was still committed by the
workflow's own git steps. New `--fail-on-quality-error` flag /
JobOptions.fail_on_quality_error: when set, an error-severity quality failure
marks the run blocked even in --skip-publish mode, so the CLI exits non-zero
(CI then skips its success-gated commit steps). --force overrides; warnings
never block; default behaviour (no flag) is unchanged for local inspection.

Adds orchestrator + CLI exit-code tests (block / no-flag-no-block / force).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…verride

phantom-capture.yml now runs `phantom run --skip-publish --fail-on-quality-error`
so an error-severity capture fails the job. The commit/push steps are made
explicitly `success()`-gated (direct + pr) so they skip on that failure —
nothing bad is committed. New `force-publish` input (default false) passes
--force to bypass the gate for intentionally low-signal frames.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CHANGELOG [Unreleased] Security entry for the CI commit-path quality gate and
the force-publish/--fail-on-quality-error overrides; collaborator guide Step 5
explains the CI quality gate and the force-publish escape hatch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nners

Add `phantom.contract` as the machine-checkable source of truth for the
Phantom Consumer Contract (contract-version 1.0.0): CONTRACT_VERSION,
PHANTOM_MODE detection, the app_env() guarantee, artifact/health defaults,
and the network allowlist helpers. Expose phantom.__contract_version__.

Guarantee PHANTOM_MODE=1 in every launched consumer app's environment across
the web, docker-compose, tui, and desktop runners via contract.app_env().
Previously demo mode activated only if the invoking shell or the manifest's
setup.run.env set the variable, so a direct `phantom run` never triggered a
consumer's demo mode. A manifest's explicit run.env may still override it.

Bump package to 0.4.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aan6YK1rLYEUEfX2XB6QS
77 tests mechanically verifying Phantom's side of every CONTRACT.md promise:
mode detection and PHANTOM_MODE=1 injection at all four runners' spawn points
(web/docker/tui/desktop, system tools mocked), schema parsing including
unknown-key forward-compat, ready-check timeout declaration, artifact path
safety, the network allowlist, and contract-version exposure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aan6YK1rLYEUEfX2XB6QS
…ersion 1.0.0)

- CONTRACT.md: frozen boot/config/health/artifact/versioning contract plus
  non-goals, at contract-version 1.0.0.
- docs/smoke-job-spec.md: specification for the reusable CI smoke job Step 3
  will implement (inputs, steps, pass/fail, consumer requirements).
- CHANGELOG: 0.4.0 release entry noting contract-version 1.0.0.
- README: Consumer Contract section, PHANTOM_MODE env var, pinning discipline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aan6YK1rLYEUEfX2XB6QS
@wbuscombe
wbuscombe enabled auto-merge July 11, 2026 17:27
wbuscombe and others added 2 commits July 11, 2026 12:36
Python 3.12 no longer auto-creates an event loop for the main thread, so the
codebase's `asyncio.get_event_loop().run_until_complete(...)` idiom crashes
with "no current event loop" on a fresh interpreter — breaking real CLI
commands (`phantom run/doctor/analyze/rollback`) and 56 tests under newer
pytest-asyncio. This was latent because CI only runs on main/PRs.

- cli.py: add `_run_async()` helper that ensures/reuses a current loop and
  route all 13 CLI call sites through it (preserves single-loop-per-invocation
  semantics so async clients bound to the loop stay valid).
- tests: replace the deprecated idiom with `asyncio.run(...)` (version-
  independent) across the affected unit + integration suites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aan6YK1rLYEUEfX2XB6QS
…b drift

CI's mypy flagged `# type: ignore` comments on croniter/playwright imports as
unused because those optional deps ship type info in some resolved versions but
not others, so the same ignore is "needed" locally and "unused" in CI. Disable
warn_unused_ignores so the type gate is deterministic across environments; the
ignores remain correct where the stubs are absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011aan6YK1rLYEUEfX2XB6QS
@wbuscombe
wbuscombe merged commit 2ca3827 into main Jul 11, 2026
2 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