Skip to content

refactor(workspace): generalize worktree session-attribution schema (#416)#418

Merged
chubes4 merged 3 commits into
mainfrom
feat/416-generic-worktree-attribution
May 17, 2026
Merged

refactor(workspace): generalize worktree session-attribution schema (#416)#418
chubes4 merged 3 commits into
mainfrom
feat/416-generic-worktree-attribution

Conversation

@chubes4
Copy link
Copy Markdown
Member

@chubes4 chubes4 commented May 17, 2026

Summary

Generalizes the worktree origin_session envelope so DMC no longer hardcodes vendor-specific field names (kimaki_*, opencode_*). The integration layer (wp-coding-agents, etc.) now declares runtime IDs and env-var bindings via a new filter, restoring layer purity per RULES.md.

Target envelope shape:

{
  "primary_id": "<opaque or null>",
  "ids": {
    "<runtime-id>": {
      "session_id": "<opaque or null>",
      "thread_id":  "<opaque or null>",
      "thread_url": "<opaque or null>",
      "run_id":     "<opaque or null>"
    }
  }
}

primary_id resolution scans registered runtimes in registration order (first non-empty session_id wins; falls back to the first non-empty subkey of the first runtime with data; honors an explicit primary_id on the stored envelope if present). DMC enumerates zero runtime IDs and zero subkeys.

Mirrors the architectural model established by CliChannelTransport in v0.44.0.

Closes #416.

Files

File Change
inc/Abilities/WorkspaceAbilities.php Replace branded schema properties on session with primary_id + free-form ids (additionalProperties map).
inc/Workspace/WorktreeContextInjector.php Document filter contract + envelope shape in file header; rewrite resolve_origin_session() to iterate registered runtimes generically with URL-suffix validation for *_url subkeys; rewrite summarize_session() to normalize the envelope and resolve primary_id via registration order; add isolated migrate_legacy_origin_session() helper.
inc/Environment.php Drop enumerated brand list from docblock prose per RULES.md guidance.
tests/smoke-worktree-agent-session-lifecycle.php Register two synthetic test runtimes via the new filter; rewrite session-attribution assertions against the generic envelope; add a dedicated legacy-migration coverage block and an explicit-primary_id-override coverage path.
tests/smoke-worktree-inventory-store.php Wire apply_filters shim, register a synthetic test runtime, exercise store/load of the new envelope through the DB-backed inventory repository.
tests/smoke-worktree-lifecycle-metadata.php Register a synthetic runtime via the existing filter shim; drive env-driven capture through DMC_SMOKE_LIFECYCLE_RUN_ID instead of vendor-specific env vars.

Filter contract (for the wp-coding-agents sibling PR)

add_filter( 'datamachine_code_worktree_runtime_signatures', function ( array $signatures ): array {
    $signatures['<runtime-id>'] = array(
        'session_id' => '<ENV_VAR_NAME>',
        'thread_id'  => '<ENV_VAR_NAME>',
        'thread_url' => '<ENV_VAR_NAME>',
        'run_id'     => '<ENV_VAR_NAME>',
        // ...integration-defined subkey => env var
    );
    return $signatures;
} );
  • Runtime IDs are opaque. DMC does not validate against a closed set. Convention: lowercase ASCII identifier (e.g. kimaki, opencode).
  • Subkeys are opaque. DMC does not validate against a closed set. Convention: session_id, thread_id, thread_url, run_id for cross-runtime renderer parity, but integrations may add their own (e.g. channel_id, guild_id).
  • *_url subkeys (any subkey ending in _url, plus literal url) are validated as http(s)://... and dropped if malformed. No other subkey-level validation.
  • Precedence is registration order — earlier registrations win when DMC needs to pick a single primary_id.

The sibling PR Extra-Chill/wp-coding-agents#131 will register the kimaki and opencode signatures via this filter once this refactor ships. After that, DMC's grep will be clean by construction — all vendor names live in the integration plugin where they belong.

Migration approach

Chosen: transparent on-read normalization, isolated helper, future-delete-gated.

WorktreeContextInjector::migrate_legacy_origin_session() is the single approved location where legacy brand-shaped keys are referenced. It is structural, not enumerated: it splits any top-level <runtime>_<subkey> key on the first underscore and projects the result into the runtime-keyed envelope. Adding a new runtime never requires touching this helper, because it doesn't know any runtime by name.

Why on-read, not a one-shot migration job: legacy rows live in two stores (the datamachine_worktree_metadata site option and the DB-backed wp_datamachine_code_worktrees inventory). A read-time normalizer is one code path, zero schema-version coordination, and zero risk of partial migration leaving stores out of sync. The downside — a permanent helper — is mitigated by isolation and a clear deletion plan.

Cleanup plan: the helper can be deleted in a follow-up PR once a backfill task (or a long-enough grace period) has rewritten all legacy rows into the new envelope. Gate the deletion on the site option datamachine_code_worktree_attribution_legacy_migrated_v2 (set true once the backfill completes). The helper's docblock explicitly calls this out.

Why this does not constitute an ongoing layer violation: the helper does not name any vendor. It treats the prefix as opaque runtime-id and the suffix as opaque subkey. It is reading historical data, not declaring policy about which runtimes exist. The structural split is the minimum compatibility shim required to avoid breaking existing inventory.

Verification

Grep — zero brand-name matches in inc/ and tests/

$ git grep -nE 'kimaki|discord|telegram|slack|whatsapp|cc-connect|opencode_session_id|opencode_run_id' inc/ tests/
$ echo $?
1

Zero matches. The only remaining opencode reference in the repo is in the plugin header description in data-machine-code.php, which is the explicitly allowed prose exception per the layer-purity rule.

Smoke tests

All session-attribution-touching smoke tests pass:

  • tests/smoke-worktree-agent-session-lifecycle.php58/58 PASS (includes new legacy-migration coverage)
  • tests/smoke-worktree-inventory-store.php12/12 PASS
  • tests/smoke-worktree-lifecycle-metadata.php57/57 PASS
  • tests/smoke-worktree-inventory-repository.phpPASS
  • tests/smoke-cli-channel-transport.phpPASS (sanity check on the sibling generic-runtime pattern)

Other worktree smoke failures observed during verification (smoke-worktree-cleanup-github-cache.php, smoke-worktree-cleanup-merged-obsolete-dirty.php, smoke-worktree-cleanup.php) reproduce identically on main and are pre-existing, out of scope per AGENTS.md.

PHP lint

All edited PHP files pass php -l. Phpcs is not installed in this checkout (no vendor/bin/phpcs); per AGENTS.md, pre-existing lint debt is out of scope.

Scope hygiene

  • No edits to docs/CHANGELOG.md.
  • No version constant bumps in data-machine-code.php — homeboy owns versioning via conventional commits (refactor: does not bump).
  • No release. No deploy. PR only.

cc <@532385681268408341>

Closes #416

chubes4 added 2 commits May 17, 2026 18:12
Replace the vendor-specific kimaki_*/opencode_* field names in the
worktree origin_session envelope with a runtime-agnostic shape:

    {
        "primary_id": "<opaque or null>",
        "ids": {
            "<runtime-id>": { "<subkey>": "<opaque or null>", ... }
        }
    }

DMC no longer enumerates runtime IDs or env-var names. The integration
layer registers what to capture via the new filter
'datamachine_code_worktree_runtime_signatures', mirroring the pattern
established by CliChannelTransport (v0.44.0).

Changes:
- inc/Abilities/WorkspaceAbilities.php: replace branded schema properties
  with primary_id + ids (free-form additionalProperties map).
- inc/Workspace/WorktreeContextInjector.php:
  * Document the filter contract and envelope shape in the file header.
  * resolve_origin_session() iterates registered runtimes generically and
    drops non-URL values for *_url subkeys.
  * summarize_session() normalizes ids, supports an explicit primary_id
    on stored envelopes, and falls back to runtime-precedence scanning.
  * migrate_legacy_origin_session() transparently projects pre-#416
    branded top-level keys (<runtime>_<subkey>) into the new envelope on
    read. The helper is isolated, structural (prefix/suffix split — no
    enumerated allowlist), and gated for future deletion via the
    documented 'datamachine_code_worktree_attribution_legacy_migrated_v2'
    option.
- inc/Environment.php: drop the enumerated brand list from the file
  header docblock per RULES.md layer-purity prose guidance.

Refs #416
Refactor worktree session-attribution smoke tests to register synthetic
test runtimes via the new 'datamachine_code_worktree_runtime_signatures'
filter and assert against the generic primary_id + ids envelope. No test
asserts against vendor-specific field names.

- smoke-worktree-agent-session-lifecycle.php: register two synthetic
  runtimes (alpha-runtime, beta-runtime) with synthetic env vars,
  exercise both registration-order primary_id resolution and *_url
  subkey URL validation. Add a dedicated legacy-migration coverage
  block that feeds pre-#416 branded top-level keys to summarize_session()
  and asserts the new envelope is produced without data loss. Add a
  coverage path for an explicit primary_id on a stored envelope
  overriding runtime-scan precedence.
- smoke-worktree-inventory-store.php: wire the apply_filters shim,
  register a synthetic test runtime, and store/load the new envelope
  shape end-to-end through the DB-backed inventory repository.
- smoke-worktree-lifecycle-metadata.php: register a synthetic runtime
  via the existing filter shim and drive the env-driven capture path
  through DMC_SMOKE_LIFECYCLE_RUN_ID instead of vendor-specific env
  vars, while keeping the OPENCODE_PID assertion behavior untouched
  (PID capture is unrelated to session-attribution).

Refs #416
@homeboy-ci
Copy link
Copy Markdown
Contributor

homeboy-ci Bot commented May 17, 2026

Homeboy Results — data-machine-code

Lint

lint — failed

  • formatting — 21 finding(s)
  • phpstan — 11 finding(s)
  • other — 3 finding(s)
  • wp-alternatives — 3 finding(s)
  • Total: 38 finding(s)

ℹ️ Auto-fix: homeboy lint data-machine-code --path /home/runner/work/data-machine-code/data-machine-code --changed-since 55946d8 --fix (or homeboy refactor data-machine-code --path /home/runner/work/data-machine-code/data-machine-code --changed-since 55946d8 --from lint --write)
ℹ️ Some issues may require manual fixes
ℹ️ Full options: homeboy docs commands/lint
ℹ️ Save lint baseline: homeboy lint data-machine-code --baseline
Deep dive: homeboy lint data-machine-code --changed-since 55946d8

Test

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-code -- --filter=TestName
ℹ️ Auto-fix lint issues: homeboy refactor data-machine-code --from lint --write
ℹ️ Collect coverage: homeboy test data-machine-code --coverage
ℹ️ Analyze failures: homeboy test data-machine-code --analyze
ℹ️ Pass args to test runner: homeboy test -- [args]
ℹ️ Full options: homeboy docs commands/test
Deep dive: homeboy test data-machine-code --changed-since 55946d8

Audit

audit — passed

  • dead_code — 35 finding(s)
  • intra-method-duplication — 6 finding(s)
  • requested_detectors — 3 finding(s)
  • test_coverage — 3 finding(s)
  • structural — 2 finding(s)
  • parallel-implementation — 1 finding(s)
  • repeated_literal_shape — 1 finding(s)
  • Total: 51 finding(s)

Deep dive: homeboy audit data-machine-code --changed-since 55946d8

Tooling versions
  • Homeboy CLI: homeboy 0.182.0+ab587e6
  • Extension: wordpress from https://github.com/Extra-Chill/homeboy-extensions
  • Extension revision: dd47f26a
  • Action: unknown@unknown

- WorktreeContextInjector.php:590 — split the canonical-top-level array
  declaration across multiple lines per WordPress.Arrays.ArrayDeclaration
  Spacing.AssociativeArrayFound.
- WorktreeContextInjector.php:596 — flip the underscore-position guard to
  Yoda style (`strlen( $key ) - 1 === $underscore`).
- WorktreeContextInjector.php:1557 — flip the handle equality check to
  Yoda style (the casted lookup is now on the left).

No behavior change. All session-attribution smoke tests still pass.

Refs #416
@chubes4 chubes4 merged commit e901387 into main May 17, 2026
3 of 5 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.

refactor(workspace): generalize worktree session-attribution schema (drop kimaki/opencode-specific field names)

1 participant