Skip to content

v1.6.0: add minimum control-surface session summaries - #1098

Merged
jeffhuber merged 8 commits into
mainfrom
codex/921-control-surface-summary
Sep 21, 2026
Merged

jeffhuber merged 8 commits into
mainfrom
codex/921-control-surface-summary

Conversation

@jeffhuber

@jeffhuber jeffhuber commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Closes #921.

The Slack control surface needs lifecycle visibility without exporting commands, messages, identities, provider references, or raw output. This PR freezes a closed code_mower.controlSurfaceSessionSummary.v1 event inside the existing cloud envelope, with exact accepted/rejected fixtures and hosted retention/export/deletion expectations. Old clients remain compatible because the event type is additive. New clients fail closed until CodeMower.com advertises the exact capability version and fixture-manifest digest through GET /api/health.

After exact hosted acceptance, the client emits the first observation and only meaningful lifecycle, lifecycle-reason, operation-count, owner-action, PR, or terminal measurement transitions. Timestamp-only polling and changing nonterminal elapsed time or usage are suppressed. Slack-requested work also uses the existing local Board projection.

The replacement immutable hosted contract checkpoint is commit 99b657ae9822a689b46d21c41695a4cfb28a177d. The fixture-manifest SHA-256 is 9e87c52812a49a1c72d0e0d2448661a3ef17cb8539ca8e029c6669ea9738d62e. This supersedes every earlier checkpoint and digest. CodeMower.com #978 must vendor the five contract resources byte-for-byte and advertise this exact identity before production emission can occur.

The closed v1 contract now carries lifecycle_reason and accepts exactly 21 (state, lifecycle_reason, owner_action) triples. This preserves provider state while truthfully distinguishing routine status, owner waits, failures, unavailable providers, and collect results that are not ready or unavailable. It also keeps tool provenance closed to metadata-only fields.

Validation:

  • 13 focused summary contract tests, including real RemoteSessions.run("collect") integration coverage for all nine result-availability states
  • exhaustive semantic validation of all 500 state/reason/action combinations: 21 allowed, 479 rejected
  • 89 focused remote-session, control-surface, Board-observation, and cloud tests
  • all 21 accepted and 19 rejected fixtures through specialized and generic validators
  • Draft 2020-12 schema validation and fixture parity
  • exact source/wheel byte verification for the module and all five contract resources
  • Ruff, privacy scan, documentation lifecycle, release facts, and diff hygiene

The rebased exact head passed the full repository CI and an eligible independent Claude exact-head audit. Hosted exact-contract acceptance remains pending; production emission stays fail-closed until CodeMower.com advertises the exact capability and manifest digest.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex exact-head audit — BLOCKED

Head: 32f71d2821d1e46b8d15cbd97b2297bf5a4fc08c
Result: P0=0, P1=2, P2=0

[P1] The emitter rejects a real archived lifecycle produced by RemoteSessions.observe

remote_session.py deliberately projects an archived provider observation as state="archived", reason="result_not_ready" because archival does not prove completion. _owner_action() maps that reason to inspect_provider, while OWNER_ACTIONS_BY_STATE permits only none for archived; build_control_surface_summary() therefore raises CloudBundleError instead of producing telemetry for this supported lifecycle (src/code_mower/control_surface_summary.py:109-115,341-354,378-389). Reproduction at this exact head with a closed public lifecycle returned: invalid control-surface session summary: owner_action is inconsistent with lifecycle state. This leaves a normal provider state without the promised summary after capability acceptance and can surface an exception in the caller rather than a suppressed/local-only telemetry outcome. Add the real archived/result-not-ready projection to accepted fixtures and align the derived owner action/invariants (or explicitly normalize the state) with regression coverage.

[P1] The closed privacy boundary accepts forbidden content through tool

The event validator closes the root, dimensions, and metrics, but never closes or validates tool; the shared JSON Schema likewise declares tool only as an arbitrary object (src/code_mower/control_surface_summary.py:198-286, src/code_mower/control_surface_session_summary.schema.json:233). At this exact head, both validate_control_surface_summary() and validate_cloud_event() accepted otherwise-valid events whose tool was each of {"message":"private slack content"}, {"prompt":"private slack content"}, {"answer":"private slack content"}, {"path":"private slack content"}, and {"response_url":"private slack content"}. Those are fields the issue and contract documentation explicitly prohibit. Client normalization happens to discard unknown tool fields, but the frozen shared schema/validator is also the hosted acceptance boundary and currently permits them. Close tool to the exact provenance shape and safe constants/bounds (or exclude it from this event), add nested forbidden-field fixtures, and verify parity in both validators before the hosted service advertises acceptance.

Evidence

  • python3.14 -m unittest tests.test_control_surface_summary -v — 9/9 passed.
  • Targeted archived-lifecycle probe reproduced the first failure through build_control_surface_summary().
  • Targeted nested-content probes demonstrated acceptance through both specialized and generic cloud validators.
  • Branch was left unchanged; no workflow labels were modified, as requested for this dependency-held draft.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Codex exact-head audit — BLOCKED

Head: 2d57351d484235e1292005d4ff20412f3c64d6e8
Result: P0=0, P1=1, P2=0

[P1] Normal collect lifecycle results still make the summary emitter raise

The archived/result_not_ready regression is fixed, but the same reason is produced by the maintained RemoteSessions.run("collect") path for every non-complete provider state, and result_unavailable is produced for a complete session with no structured result (src/code_mower/remote_session.py:481-507). _owner_action() maps both reasons to inspect_provider, while OWNER_ACTIONS_BY_STATE permits that action only for archived and uncertain; pending, running, waiting, complete, failed, suspended, and terminated therefore fail validation (src/code_mower/control_surface_summary.py:109-116,394-407,428-483).

I reproduced this through a real RemoteSessions + FakeProvider lifecycle, not a hand-built invalid event. Calling collect and then build_control_surface_summary() failed with invalid control-surface session summary: owner_action is inconsistent with lifecycle state for:

  • pending / result_not_ready
  • running / result_not_ready
  • waiting_for_user / result_not_ready
  • waiting_for_approval / result_not_ready
  • complete / result_unavailable
  • failed / result_not_ready
  • suspended / result_not_ready
  • terminated / result_not_ready

Only the newly covered archived / result_not_ready case passed. This is part of the released Slack completion mapping (completion -> collect), so a routine early completion poll or unavailable result can raise instead of producing the promised metadata summary. Derive the owner action with state/reason precedence (or align the state/action invariants another explicit way), and cover the actual RemoteSessions.run("collect") terminal/state matrix rather than only one fixture per state.

Verified at this head

  • The prior archived/result_not_ready defect is fixed through both specialized and generic validators.
  • tool is now a closed metadata-only object in code and Draft 2020-12 schema; unknown/nested message, prompt, answer, path, response URL, and unsupported runtime fields fail through both validators.
  • 123 focused/adjacent tests plus 57 subtests passed.
  • All 10 accepted and 17 rejected fixtures passed the specialized and generic validator expectations; Draft 2020-12 schema structure validated.
  • Fixture-manifest byte counts and hashes match source; manifest SHA-256 is 1079bc322faeae668af5332fc713ccf990ed92ea301f390cced6e4d8282d494b.
  • A built wheel contains the module and all five contract resources byte-for-byte.
  • Ruff, privacy scan, release-hygiene privacy, committed package inventory, and diff hygiene passed.
  • The replacement hosted checkpoint/digest in the PR and v1.6.0: minimum control-surface telemetry contract and OSS emitters #921 matches the packaged bytes. Hosted v1.6.0: minimal CodeMower.com Slack telemetry ingestion and aggregates #978 acceptance/deployment remains an explicit pending dependency and is not claimed by this audit.

@jeffhuber

Copy link
Copy Markdown
Contributor Author

Replacement exact-head evidence

Head: 58df8adf7d07a92545288d0ed90ba59fb6923509
Fixture-manifest SHA-256: 9e87c52812a49a1c72d0e0d2448661a3ef17cb8539ca8e029c6669ea9738d62e

This supersedes 2d57351d484235e1292005d4ff20412f3c64d6e8 / 1079bc322faeae668af5332fc713ccf990ed92ea301f390cced6e4d8282d494b and every earlier contract identity.

The remaining P1 is closed by carrying lifecycle_reason in the event and validating one exact (state, reason, owner_action) policy. Real RemoteSessions.run("collect") outputs now validate for pending, running, both owner-waiting states, complete without a structured result, failed, suspended, terminated, and archived. The producer preserves the provider state and derives inspect_provider specifically from result_not_ready or result_unavailable; arbitrary actions remain rejected.

Evidence at this head:

  • 13 focused contract tests; 89 remote/control/Board/cloud tests
  • full 500-combination policy matrix: 21 accepted, 479 rejected
  • 21 canonical accepted and 19 rejected fixtures through both semantic validators
  • Draft 2020-12 schema validation and accepted/rejected fixture parity
  • exact source/wheel bytes for the module and all five contract resources
  • Ruff, privacy, docs lifecycle, release facts, and diff hygiene pass

CodeMower.com #978 must discard the superseded contract bytes, vendor this exact checkpoint, and advertise this digest before OSS production emission.

@jeffhuber
jeffhuber force-pushed the codex/921-control-surface-summary branch from 58df8ad to 99b657a Compare September 21, 2026 23:48
@jeffhuber
jeffhuber marked this pull request as ready for review September 21, 2026 23:53
@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown

Claude audit (merge-authority lane)

Head SHA: 99b657ae9822a689b46d21c41695a4cfb28a177d
Verdict: PASS
Review details remain in the local audit artifact.
Publication workflow: .github/workflows/local-audit-publication.yml at 29e6e2d4a7a53219a292fdf7a14e8a94317de581

@jeffhuber
jeffhuber merged commit 9f6a89d into main Sep 21, 2026
23 checks passed
@jeffhuber
jeffhuber deleted the codex/921-control-surface-summary branch September 21, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builder:codex Code Mower generated label claude-audit-done

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v1.6.0: minimum control-surface telemetry contract and OSS emitters

1 participant