refactor(workspace): generalize worktree session-attribution schema (#416)#418
Conversation
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 Results —
|
- 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
Summary
Generalizes the worktree
origin_sessionenvelope 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 perRULES.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_idresolution scans registered runtimes in registration order (first non-emptysession_idwins; falls back to the first non-empty subkey of the first runtime with data; honors an explicitprimary_idon the stored envelope if present). DMC enumerates zero runtime IDs and zero subkeys.Mirrors the architectural model established by
CliChannelTransportin v0.44.0.Closes #416.
Files
inc/Abilities/WorkspaceAbilities.phpsessionwithprimary_id+ free-formids(additionalPropertiesmap).inc/Workspace/WorktreeContextInjector.phpresolve_origin_session()to iterate registered runtimes generically with URL-suffix validation for*_urlsubkeys; rewritesummarize_session()to normalize the envelope and resolveprimary_idvia registration order; add isolatedmigrate_legacy_origin_session()helper.inc/Environment.phptests/smoke-worktree-agent-session-lifecycle.phpprimary_id-override coverage path.tests/smoke-worktree-inventory-store.phpapply_filtersshim, register a synthetic test runtime, exercise store/load of the new envelope through the DB-backed inventory repository.tests/smoke-worktree-lifecycle-metadata.phpDMC_SMOKE_LIFECYCLE_RUN_IDinstead of vendor-specific env vars.Filter contract (for the wp-coding-agents sibling PR)
kimaki,opencode).session_id,thread_id,thread_url,run_idfor cross-runtime renderer parity, but integrations may add their own (e.g.channel_id,guild_id).*_urlsubkeys (any subkey ending in_url, plus literalurl) are validated ashttp(s)://...and dropped if malformed. No other subkey-level validation.primary_id.The sibling PR Extra-Chill/wp-coding-agents#131 will register the
kimakiandopencodesignatures 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_metadatasite option and the DB-backedwp_datamachine_code_worktreesinventory). 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(settrueonce 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/andtests/Zero matches. The only remaining
opencodereference in the repo is in the plugin header description indata-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.php— 58/58 PASS (includes new legacy-migration coverage)tests/smoke-worktree-inventory-store.php— 12/12 PASStests/smoke-worktree-lifecycle-metadata.php— 57/57 PASStests/smoke-worktree-inventory-repository.php— PASStests/smoke-cli-channel-transport.php— PASS (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 onmainand 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 (novendor/bin/phpcs); per AGENTS.md, pre-existing lint debt is out of scope.Scope hygiene
docs/CHANGELOG.md.data-machine-code.php— homeboy owns versioning via conventional commits (refactor:does not bump).cc <@532385681268408341>
Closes #416