Skip to content

feat: agent bundle manifest reconciliation — slug-based diff, status drift, upgrade (#2860)#2862

Open
chubes4 wants to merge 2 commits into
mainfrom
agent-bundle-reconcile
Open

feat: agent bundle manifest reconciliation — slug-based diff, status drift, upgrade (#2860)#2862
chubes4 wants to merge 2 commits into
mainfrom
agent-bundle-reconcile

Conversation

@chubes4

@chubes4 chubes4 commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #2860

Agent bundle manifests shipped by integration plugins were only applied to the live agent DB row at install/adopt time. Subsequent manifest changes deployed with the plugin but never reconciled into the row, causing silent drift. Production case: a default_model bump (gpt-5.4-nanogpt-5.5) shipped in the roadie repo on June 30 but the live agent row kept nano for 8+ days, causing real session failures.

Compounding factor: wp datamachine agent diff <slug> and agent upgrade <slug> errored with "Bundle path not found: <slug>" because those commands required an explicit filesystem path — operators only knew the agent slug.

Investigation findings

Why "Bundle path not found" happens

agent diff, agent upgrade, agent inspect, agent validate, agent adopt, and agent rebase all take a <path> positional argument. The source string flows through BundleSource::resolve() (inc/Engine/Bundle/BundleSource.php:64), which treats any non-remote string as a local path and checks file_exists($source). When the operator passes a slug like roadie, file_exists("roadie") is false → WP_Error "Bundle path not found: roadie".

Why there was no bundle discovery mechanism

There was no filter or registry for integration plugins to declare where their on-disk bundle directories live. The datamachine_agent_bundle_directories filter did not exist. Integration plugins (e.g. extrachill-roadie) ship bundles at bundles/<slug>/manifest.json but had no way to tell Data Machine where they were — installation was a manual wp datamachine agent install <path> invocation with no automated discovery or reconcile-on-activate path.

Why source_ref/source_revision didn't help

The stored datamachine_bundle.source_ref/source_revision fields on the agent row are provenance metadata (URL / git SHA for remote sources), not resolvable local paths. They are empty for locally-installed bundles and were never designed to be read back as a filesystem path for diff/upgrade.

The existing precedence system was correct but unreachable

The 3-way artifact ledger (installed_hash / current_hash / target hash) already implements the right precedence:

  • Clean artifacts (current == installed, target differs) → auto-apply.
  • Locally modified artifacts where target also differs → staged as conflict for approval (don't clobber).
  • Locally modified artifacts where target matches current → no conflict, no-op (operator already fixed it).
  • preserve_local paths (model, provider, allowed_redirect_uris) → operator values always survive via AgentConfigArtifactProjector::preserve_local_paths().

The problem was purely that this system was unreachable without slug-based discovery — nobody could run agent upgrade <slug> to trigger it.

What changed

1. Bundle directory discovery — AgentBundleDirectoryRegistry

New class (inc/Engine/Bundle/AgentBundleDirectoryRegistry.php) with a datamachine_agent_bundle_directories filter. Integration plugins register their bundle directories:

add_filter( 'datamachine_agent_bundle_directories', function ( $dirs ) {
    $dirs['roadie'] = __DIR__ . '/bundles/roadie';
    return $dirs;
} );

Stale/non-existent directories are silently dropped. The registry resolves by bundle slug, by agent row, or by raw slug (tries direct match, then falls back to resolving through an installed agent's stored bundle_slug).

2. Slug resolution in load_bundle_from_input

AgentBundleAbilityService::load_bundle_from_input() now checks: if the source is neither a remote URL nor an existing local path, treat it as a slug and resolve it through the registry. This makes all path-based commands (diff, upgrade, inspect, validate, adopt, rebase) accept slugs:

wp datamachine agent diff roadie       # was: "Bundle path not found: roadie"
wp datamachine agent upgrade roadie    # now works
wp datamachine agent inspect roadie    # now works

3. Manifest drift detection in status

agent status <slug> now loads the on-disk manifest (when discoverable) and compares bundle_version + the tracked agent_config payload against the installed row. Returns:

  • has_manifest_drift (bool) — quick signal
  • manifest.bundle_version.drift — version mismatch
  • manifest.config_drift — per-field {key, installed, manifest} entries

Table output shows a drift column and prints warnings for each drifted field.

4. Reconcile verb — agent upgrade <slug> and --all

agent upgrade now accepts either a slug or a path. When given a slug, it resolves the on-disk bundle directory and runs the existing upgrade machinery (3-way merge via the artifact ledger). The --all flag iterates every installed bundle-backed agent and reconciles each one against its registered on-disk manifest.

5. Tests

New smoke test (tests/agent-bundle-manifest-reconcile-smoke.php, 34 assertions):

  • Registry resolves registered directories, normalizes slugs, drops stale paths
  • config_drift_fields detects changed/added/removed keys, ignores key order
  • tracked_payload excludes datamachine_bundle from drift comparison
  • Operator override survives via preserve_local_paths (model key)
  • Non-preserve_local keys are not restored (three_way merge applies)
  • Hash comparison is order-independent (no false drift)
  • No drift when operator already set the same value as the manifest
  • CLI/ability surfaces reference the new entry points

Precedence rule (existing, documented here for clarity)

The reconcile path uses the existing 3-way artifact ledger. No new precedence system was built:

Scenario installed (base) current (live) target (manifest) Behavior
Operator didn't touch A A B Auto-apply (clean → target wins)
Operator touched, manifest didn't A B A No-op (target == installed, no drift)
Operator touched, manifest changed same way A B B No conflict (target == current, no-op)
Operator touched, manifest changed differently A B C Conflict (staged for approval, don't clobber)
preserve_local key (model, provider) A B C Operator wins (always preserved)

This is the correct shape: operator overrides survive unless explicitly reset, manifest changes reach the row when the operator hasn't diverged, and genuine conflicts are surfaced for human review rather than silently clobbered.

Integration plugin follow-up

Integration plugins (extrachill-roadie, extrachill-event-bundles, etc.) need to hook datamachine_agent_bundle_directories to register their bundle directories. This is a one-line change per plugin — out of scope for this Data Machine PR.

…drift detection, and upgrade (#2860)

Agent bundle manifests shipped by integration plugins were only applied
at install/adopt time. Later manifest changes deployed with the plugin
but never reconciled into the live agent row, causing silent drift
(e.g. a model bump shipped in the repo but production kept the old
model for 8+ days).

Root cause: agent diff/upgrade required an explicit filesystem path
but operators only knew the agent slug. BundleSource::resolve() treated
a slug as a path and failed with "Bundle path not found: <slug>".

Fix:
- AgentBundleDirectoryRegistry: new datamachine_agent_bundle_directories
  filter lets integration plugins register their on-disk bundle
  directories (bundle_slug => path). Stale/non-existent dirs are dropped.
- AgentBundleAbilityService::load_bundle_from_input: when the source is
  neither a remote URL nor an existing local path, resolve it as a slug
  through the registry. This makes diff/upgrade/inspect/validate/adopt/
  rebase all accept slugs, not just paths.
- status(): manifest drift detection — loads the on-disk manifest and
  compares bundle_version + tracked agent_config against the installed
  row, surfacing has_manifest_drift + config_drift fields.
- reconcile_all(): iterates every installed bundle-backed agent and runs
  the standard upgrade path against its registered on-disk manifest.
- CLI: agent diff/upgrade now accept <slug-or-path>; upgrade --all
  reconciles every installed bundle-backed agent; status shows a drift
  column and warnings.

Precedence rule (existing, now reachable): the 3-way artifact ledger
compares installed (base) vs current (live) vs target (manifest).
Clean artifacts auto-apply; locally modified artifacts where the target
differs are staged as conflicts for approval. Operator overrides on
preserve_local paths (model, provider, allowed_redirect_uris) survive
via AgentConfigArtifactProjector::preserve_local_paths(). No new
precedence system — the existing one was just unreachable without
slug-based discovery.
@homeboy-ci

homeboy-ci Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Homeboy Results — data-machine

Lint

review lint — passed

ℹ️ Full options: homeboy self docs commands/lint
Deep dive: homeboy review lint data-machine --changed-since c174857

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-review-lint-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-review-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/29021787139

Test

review test — failed

ℹ️ No tests ran — the runner failed before producing results. See raw_output.stderr_tail / raw_output.stdout_tail for the underlying error (bootstrap failure, missing deps, DB connection, etc.).
ℹ️ To run specific tests: homeboy test data-machine -- --filter=TestName
ℹ️ Auto-fix lint issues: homeboy refactor data-machine --from lint --write
ℹ️ Collect coverage: homeboy test data-machine --coverage
ℹ️ Analyze failures: homeboy test data-machine --analyze
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy self docs commands/test
Deep dive: homeboy review test data-machine --changed-since c174857

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-review-test-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-review-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/29021787139

Audit

review audit — passed

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

Deep dive: homeboy review audit data-machine --changed-since c174857

Artifacts and drill-down
  • CI results artifact: homeboy-ci-results-data-machine-review-audit-quality-Linux-node24 contains immediate command JSON for this action invocation.
  • Observation artifact: homeboy-observations-data-machine-review-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/29021787139
Tooling versions
  • Homeboy CLI: homeboy 0.281.20+123145ee082b+f8a2fa4a
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: f89b4297
  • Action: unknown@unknown

…le code

Fixes 7 Generic.Formatting.MultipleStatementAlignment.NotSameWarning
findings flagged by CI phpcs on PR #2862:
- AgentBundleAbilityService: lines 82, 83, 1000, 1073, 1074
- AgentBundleCommand: lines 251, 252
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.

Agent bundle manifest changes never reconcile into live agent rows — model bump shipped in repo but production kept the old model

1 participant