You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The worktree session-attribution metadata (origin_session capture) hardcodes vendor-specific field names — kimaki_session_id, kimaki_thread_id, kimaki_thread_url, opencode_session_id, opencode_run_id — directly into DMC's schema, value objects, env-var sniffing, smoke tests, and report rendering. This couples DMC, a layer that should be runtime-agnostic, to specific coding-agent runtime brands.
Per the platform's Layer purity rule (now codified in RULES.md under "## Coding"):
A layer must not reference, name, special-case, or know about anything below it. The substrate (e.g. agents-api) doesn't know about transports. A generic transport runtime (e.g. DMC's CLI transport) doesn't know about specific CLI tools (kimaki, cc-connect, telegram). The integration plugin (e.g. wp-coding-agents) is the only place where vendor-specific names live.
DMC's worktree-lifecycle code violates this. The integration layer that should own these names is wp-coding-agents.
tests/smoke-worktree-agent-session-lifecycle.php (multiple assertions on branded keys).
tests/smoke-worktree-inventory-store.php L128.
Why this is wrong
DMC claims to be runtime-agnostic. The README lists kimaki and opencode as examples of supported runtimes; the plugin description says "Claude Code, OpenCode, kimaki, etc." That positioning is incompatible with hardcoded kimaki_* and opencode_* field names in shared schemas. Either DMC is generic and the brands live in config, or DMC is kimaki+opencode-specific and the prose is wrong.
Adding a new runtime (cc-connect, claude-code-sdk, future tools) requires editing DMC. That's the test the layer-purity rule is built to catch. New vendors should be config or installer-side concerns.
primary_id resolution baking in brand precedence (kimaki_session_id ?? opencode_session_id ?? opencode_run_id ?? kimaki_thread_id) is operationally fragile — it assumes which fields are most authoritative without letting the calling integration declare that itself.
The env-var sniffer reads brand-named env vars directly into brand-named keys, making the runtime → key mapping invisible and undocumented anywhere a new bridge could discover it.
Proposed shape
Replace the typed brand-named map with a generic envelope:
primary_id stays — it's already the unbranded primary identifier, used everywhere downstream. No change to its semantics.
ids is keyed by runtime identifier ("kimaki", "opencode", future ones). The runtime ID is a string the integration layer chooses, not something DMC enumerates.
Each entry is a flexible string-map. DMC doesn't validate against a closed set of subkeys — integrations declare what they want to capture (session_id, thread_id, etc.).
primary_id resolution becomes a single ordered scan of registered runtime IDs, ordered by registration priority. DMC does not bake in a brand precedence.
Then wp-coding-agents (the layer that knows about kimaki and opencode) registers its signatures via that filter. DMC reads the registered map and iterates env vars generically. All brand names move out of DMC into wp-coding-agents.
Acceptance criteria
Schema in inc/Abilities/WorkspaceAbilities.phporigin_session block contains zero brand names. Likely shape: primary_id (string|null) + ids (object<string, object<string, string|null>>).
WorktreeContextInjector::summarize_session() produces output keyed only by primary_id + ids[runtime_id]. No branched lookups by hardcoded brand.
WorktreeContextInjector env-var sniffer reads from a registered filter map (e.g. datamachine_code_worktree_runtime_signatures). Default registration in DMC is empty; integrations populate it.
wp-coding-agents (separate PR) registers the kimaki + opencode signatures via that filter during setup.sh / runtime install.
Smoke tests are updated to register a test runtime via the filter, then assert generic-shape output (no string-literal kimaki_session_id etc. in test assertions).
grep -riE 'kimaki|discord|telegram|slack|whatsapp|cc-connect' inc/ tests/ returns zero matches in DMC's source. (Plugin description prose is the only acceptable exception per the layer-purity rule.)
Migration: existing rows in workspace inventory storage are upgraded on read — if old branded fields exist (kimaki_session_id, etc.), they're mapped into the new ids envelope under the inferred runtime ID. No data loss.
Out of scope
The dispatch architecture (CliChannelTransport, CliChannelRegistry) — already generic, no changes needed.
Other ad-hoc kimaki/opencode references outside the worktree-attribution surface, if any exist. (If you find them, file follow-up issues — do not bundle.)
The wp-coding-agents PR that registers its runtime signatures. That's a separate sibling PR depending on this one.
Related
Layer purity rule (canonical): added to RULES.md on 2026-05-17. Applies retroactively to existing violations including this one.
Future sibling PR in wp-coding-agents: register kimaki + opencode runtime signatures via the new filter, eliminating the env-var hardcode.
This issue is the operational follow-through on the layer-purity rule. Existing violations are tech debt to be paid down with tracked issues, not grandfathered.
Summary
The worktree session-attribution metadata (origin_session capture) hardcodes vendor-specific field names —
kimaki_session_id,kimaki_thread_id,kimaki_thread_url,opencode_session_id,opencode_run_id— directly into DMC's schema, value objects, env-var sniffing, smoke tests, and report rendering. This couples DMC, a layer that should be runtime-agnostic, to specific coding-agent runtime brands.Per the platform's Layer purity rule (now codified in
RULES.mdunder "## Coding"):DMC's worktree-lifecycle code violates this. The integration layer that should own these names is
wp-coding-agents.Current violation surface
```
$ git grep -nE 'kimaki_session_id|kimaki_thread_id|kimaki_thread_url|opencode_session_id|opencode_run_id'
```
Schema (
inc/Abilities/WorkspaceAbilities.php):origin_sessionschema declares the five vendor-specific fields as named properties.Value object / builder (
inc/Workspace/WorktreeContextInjector.php):@returnshape hardcodes the field names.summarize_session()reads each field by name from$session.primary_idresolution chains specific brand fields:$kimaki_session_id ?? $opencode_session_id ?? $opencode_run_id ?? $kimaki_thread_id.KIMAKI_SESSION_ID,KIMAKI_THREAD_ID,KIMAKI_THREAD_URL,OPENCODE_SESSION_ID,OPENCODE_RUN_IDdirectly into branded keys.Smoke tests (vendor-name dependent):
tests/smoke-worktree-agent-session-lifecycle.php(multiple assertions on branded keys).tests/smoke-worktree-inventory-store.phpL128.Why this is wrong
kimaki_*andopencode_*field names in shared schemas. Either DMC is generic and the brands live in config, or DMC is kimaki+opencode-specific and the prose is wrong.primary_idresolution baking in brand precedence (kimaki_session_id ?? opencode_session_id ?? opencode_run_id ?? kimaki_thread_id) is operationally fragile — it assumes which fields are most authoritative without letting the calling integration declare that itself.Proposed shape
Replace the typed brand-named map with a generic envelope:
```json
{
"primary_id": "",
"ids": {
"": {
"session_id": "",
"thread_id": "",
"thread_url": "",
"run_id": ""
}
}
}
```
Where:
primary_idstays — it's already the unbranded primary identifier, used everywhere downstream. No change to its semantics.idsis keyed by runtime identifier ("kimaki","opencode", future ones). The runtime ID is a string the integration layer chooses, not something DMC enumerates.primary_idresolution becomes a single ordered scan of registered runtime IDs, ordered by registration priority. DMC does not bake in a brand precedence.Env-var sniffer becomes registry-driven
Instead of hardcoding:
```php
$kimaki_session_id = getenv( 'KIMAKI_SESSION_ID' );
// ...
$opencode_run_id = getenv( 'OPENCODE_RUN_ID' );
```
Use a registered map from a new filter (or a
WorktreeRuntimeRegistry::register( $runtime_id, $env_var_map )API):```php
apply_filters( 'datamachine_code_worktree_runtime_signatures', array() );
// returns e.g.
[
'kimaki' => [ 'session_id' => 'KIMAKI_SESSION_ID', 'thread_id' => 'KIMAKI_THREAD_ID', 'thread_url' => 'KIMAKI_THREAD_URL' ],
'opencode' => [ 'session_id' => 'OPENCODE_SESSION_ID', 'run_id' => 'OPENCODE_RUN_ID' ],
]
```
Then
wp-coding-agents(the layer that knows about kimaki and opencode) registers its signatures via that filter. DMC reads the registered map and iterates env vars generically. All brand names move out of DMC into wp-coding-agents.Acceptance criteria
inc/Abilities/WorkspaceAbilities.phporigin_sessionblock contains zero brand names. Likely shape:primary_id(string|null) +ids(object<string, object<string, string|null>>).WorktreeContextInjector::summarize_session()produces output keyed only byprimary_id+ids[runtime_id]. No branched lookups by hardcoded brand.WorktreeContextInjectorenv-var sniffer reads from a registered filter map (e.g.datamachine_code_worktree_runtime_signatures). Default registration in DMC is empty; integrations populate it.wp-coding-agents(separate PR) registers the kimaki + opencode signatures via that filter duringsetup.sh/ runtime install.kimaki_session_idetc. in test assertions).grep -riE 'kimaki|discord|telegram|slack|whatsapp|cc-connect' inc/ tests/returns zero matches in DMC's source. (Plugin description prose is the only acceptable exception per the layer-purity rule.)kimaki_session_id, etc.), they're mapped into the newidsenvelope under the inferred runtime ID. No data loss.Out of scope
CliChannelTransport,CliChannelRegistry) — already generic, no changes needed.wp-coding-agentsPR that registers its runtime signatures. That's a separate sibling PR depending on this one.Related
RULES.mdon 2026-05-17. Applies retroactively to existing violations including this one.CliChannelTransport/CliChannelRegistry(feat(channels): generic CLI transport runtime for agents/dispatch-message #412, v0.44.0). This issue is the same rule applied to the worktree-attribution surface.wp-coding-agents: register kimaki + opencode runtime signatures via the new filter, eliminating the env-var hardcode.This issue is the operational follow-through on the layer-purity rule. Existing violations are tech debt to be paid down with tracked issues, not grandfathered.