Skip to content

Conform Data Machine to the canonical flat approval envelope#2809

Merged
chubes4 merged 4 commits into
mainfrom
fix/converge-pending-action-envelope
Jun 28, 2026
Merged

Conform Data Machine to the canonical flat approval envelope#2809
chubes4 merged 4 commits into
mainfrom
fix/converge-pending-action-envelope

Conversation

@chubes4

@chubes4 chubes4 commented Jun 28, 2026

Copy link
Copy Markdown
Member

What

Converge Data Machine onto the single canonical approval_required envelope shape so producers emit consistently and consumers read one shape.

PendingActionHelper::stage() previously emitted a custom nested payload:

{ pending_action: { …canonical… }, resolve_with, resolve_params, instruction }

It now emits the canonical flat envelope via the Agents API pending-action value object. The payload is the full canonical pending-action shape (action_id, kind, summary, preview, status, expires_at, apply_input, audit fields, …) plus the standardized instruction and grants slots.

Why

There were two divergent approval-envelope shapes: Agents API's flat approval_required (from the ability lifecycle bridge) and Data Machine's nested wrapper. Agents API's flat shape is canonical; this PR makes Data Machine conform. As a bonus, the Agents API conversation loop reads payload.action_id from the flat shape, so the approval_required runtime event now carries a real action_id for Data Machine-staged actions instead of null.

Behavior preserved

All Data Machine behavior is intact — verified against the code, not assumed:

  • Resolver grants now ride the canonical grants slot (still also persisted in store metadata).
  • Durable datamachine_pending_actions table, datamachine_pending_action_handlers dispatch, multisite blog_id re-entry, and the generic agents/resolve-pending-action resolution are untouched — the resolver reads the store, never the envelope.
  • Top-level staged / action_id convenience fields are unchanged, so internal callers (content abilities, ToolExecutor, bundle/memory pending actions) and the frontend diff card keep working.
  • PendingActionInspectionAbility::normalize_action_row() already rebuilds resolve_with/resolve_params from store metadata, so the CLI/frontend inspection rows are unaffected.

Blast radius (verified)

No production code anywhere reads the old nested payload.pending_action wrapper. Checked Data Machine, frontend-agent-chat (reads the top-level action_id and the generic resolve route — handles both shapes), data-machine-events (uses action_id as a tool input, reads the store), and searched Automattic/wpcom (GitHub Enterprise) plus the Extra-Chill org — no nested-shape consumers. Big Sky / Odie and other Agents API consumers read the flat shape and are unaffected.

Dependency / sequencing

Depends on the Agents API canonical builder (WP_Agent_Pending_Action::to_approval_envelope()): Automattic/agents-api#375. Data Machine pins wordpress/agents-api: dev-main. stage() uses the canonical builder when present and otherwise builds the identical flat payload inline, so this PR is green now against the currently-vendored Agents API and adopts the canonical path automatically after #375 lands and composer update wordpress/agents-api runs. The inline path is version-transition only — it emits the same canonical flat shape, not the old nested one.

Tests

  • Rewrote pending-action-helper-approval-envelope-smoke to assert the flat shape (no nested pending_action/resolve_with/resolve_params), plus the instruction and grants slots. Verified passing on both the canonical builder path and the inline transition path.
  • Repointed two sibling smokes (memory-bundle-policy, agent-bundle-upgrade-planner) that read payload.pending_action to the flat payload.
  • Added the DATAMACHINE_PENDING_ACTION_TRANSIENT_FALLBACK guard to the envelope smoke so it runs in pure PHP like its siblings (it previously relied on an ambient $wpdb).
  • Remaining local failures in resolution-apply smokes are pre-existing (need a real $wpdb) and identical on main.

AI assistance

  • AI assistance: Yes
  • Tool(s): Claude Opus 4.8 (1M context) via Claude Code
  • Used for: Cross-repo blast-radius analysis, implementing the stage() change, fixing affected test reads, and verifying both dependency paths. Chris reviewed.

🤖 Generated with Claude Code

…tage()

Converge Data Machine onto the single canonical approval_required envelope
shape. stage() previously emitted a custom nested payload
({ pending_action: {…}, resolve_with, resolve_params, instruction }); it now
emits the canonical flat envelope via the Agents API pending-action value
object, whose payload carries the full canonical pending-action shape
(action_id, kind, summary, preview, status, expires_at, …) plus the
standardized instruction and grants slots.

All Data Machine behavior is preserved: resolver grants now ride the canonical
grants slot, the durable datamachine_pending_actions table, the
datamachine_pending_action_handlers dispatch, multisite re-entry, and the
generic agents/resolve-pending-action resolution are untouched (the resolver
reads the store, not the envelope). Top-level staged/action_id convenience
fields are unchanged, so internal callers and the frontend diff card keep
working.

Uses WP_Agent_Pending_Action::to_approval_envelope() when present (Agents API
PR for the canonical builder); a transition path builds the identical flat
payload inline for an older vendored Agents API so this stays green until the
dependency bumps.

Updates the approval-envelope smoke to assert the flat shape (and the grants
slot), repoints two sibling smokes that read payload.pending_action to the flat
payload, and adds the transient-fallback guard so the envelope smoke runs in
pure PHP like its siblings.

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

homeboy-ci Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Homeboy Results — data-machine

Lint

lint — passed

ℹ️ Full options: homeboy docs commands/lint
Deep dive: homeboy lint data-machine --changed-since 62ce11a

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-lint-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-lint-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/28323987166

Test

test — passed

ℹ️ Auto-fix lint issues: homeboy refactor data-machine --from lint --write
ℹ️ Collect coverage: homeboy test data-machine --coverage
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy docs commands/test
Deep dive: homeboy test data-machine --changed-since 62ce11a

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-test-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-test-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/28323987166

Audit

audit — passed

  • audit — 50 finding(s)
  • Total: 50 finding(s)

Deep dive: homeboy audit data-machine --changed-since 62ce11a

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-audit-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-audit-quality-Linux-node24 contains exported Homeboy run history for deeper queries.
  • Drill-down: download the observation artifact, then run homeboy runs import <dir>, homeboy runs list, and homeboy runs findings <run-id>.
  • Artifacts are attached to the workflow run: https://github.com/Extra-Chill/data-machine/actions/runs/28323987166
Tooling versions
  • Homeboy CLI: homeboy 0.267.1+496abc128ee5+5285075d
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: 8b68b5fc
  • Action: unknown@unknown

chubes4 and others added 3 commits June 28, 2026 09:12
The homeboy CI recipe runs changed tests/**/*-smoke.php through the host-smoke
backend, which loads a real $wpdb but does not run the plugin's deferred
migrations — so the durable datamachine_pending_actions table is absent while
PendingActionStore::has_database() still reports true, and the transient
fallback never engages. Any smoke that stages a pending action then hits a
missing-table DB error.

Ensure the durable table exists when a real database is present (mirrors the
existing agent-bundle-artifact-store smoke pattern); pure-PHP runs with no real
$wpdb keep using the transient fallback. This exercises the real store
end-to-end on the host-smoke backend, matching the durable path the WP Codebox
PHPUnit backend already covers on main.

Applies to the three staging smokes touched by the canonical-envelope change:
approval-envelope, memory-bundle-policy, and agent-bundle-upgrade-planner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The host-smoke backend loads a real WordPress (Playground/PHP WASM) but does not
run plugin bootstrap, so these standalone smokes have to wire up what a booted
plugin provides. Building on the durable-table fix:

- Register the canonical store + resolver adapters in the bundle-upgrade smoke
  (the memory smoke already did) so agents/resolve-pending-action dispatches
  instead of returning "no resolver registered".
- Stage the bundle-upgrade action as the resolving user so the action's creator
  matches the resolution scope; otherwise the resolver denies on owner mismatch.
- Pass user_id explicitly when staging in the approval-envelope smoke so the
  recorded creator is deterministic (real WP overrides the stubbed
  get_current_user_id() with the unauthenticated 0).
- Define STDERR in the memory smoke: Playground/PHP WASM does not provide the
  stream constant, so the assert helper crashed on failure instead of reporting.

All three smokes pass standalone (pure PHP) and now exercise the real durable
store + resolver on the host-smoke backend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The host-smoke backend runs as an unauthenticated user, so the pending-action
resolver denies on authorization (the pure-PHP stubs simulate an authorized
operator with current_user_can => true, which does not apply under real WP).
Act as an administrator before staging so the bundle-upgrade and memory
resolution smokes share an authorized operator identity for staging and
resolution. Guarded behind function_exists so pure-PHP runs are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chubes4 chubes4 merged commit 6038b2c into main Jun 28, 2026
5 checks passed
@chubes4 chubes4 deleted the fix/converge-pending-action-envelope branch June 28, 2026 13:41
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