# dsh-ecc plugin specification Status: Draft 0.1 Target: DSH plugin package @staavanothanh/dsh-ecc Scope: ECC agents, skills; model-facing tools `spawn` and `spawn_fork` ## 1. Mục tiêu dsh-ecc cho phép một DSH session sử dụng các phần có giá trị nhất của ECC mà không sửa DSH core: - đọc ECC agent definitions như child persona/instruction; - đăng ký ECC skills vào DSH skill catalog và invocation flow; - tạo một child continuable với LLM route do main agent chọn: provider, model, effort. - tạo một child fork-continuable (`spawn_fork`) với cùng route/persona contract, nhưng seed bằng các turn đã hoàn tất của parent session. Tool phải giữ được khả năng main agent tiếp tục giao việc cho child trong cùng session, hoặc resume child sau khi process/UI quay lại. ## 2. Không thuộc phạm vi - Không sửa source trong deepseek-harness. - Không biến ECC agents/*.md thành DSH agent preset/mode. - Không gọi workflow.agent, subagent, background_agent hay agent_teams_add_member từ bên trong tool mới. - Không cài/đóng gói MCP server, rules, commands legacy hoặc team/mailbox/task UI trong MVP. MCP entries trong persona được ghi nhận và bỏ qua; host tự cung cấp MCP tools theo cấu hình riêng. - Không tự đọc/ghi trực tiếp settings.yaml. Các runtime call tới DSH (ctx.llm, ctx.subagents và ctx.skills) được phép; điều cấm là gọi một tool model-facing khác như một implementation shortcut. ## 3. Cấu hình plugin ~~~ eccSource: mode: bundled # MVP: bundled; remote update is post-MVP repository: https://github.com/affaan-m/ECC.git ref: d8409a4b0813771235555e32e3d8046a73988bfa # commit immutable, không dùng main integrity: sha256:... # bắt buộc với remote artifact updatePolicy: explicit # không tự fetch mỗi lần startup transportProvider: spawn # DSH provider dùng cho continuable child skills: ~~~ Installing dsh-ecc always mounts the complete MVP surface: ECC agents and skills. There are no per-surface `enabled` switches in the MVP; a user disables the integration by removing the plugin from the DSH profile. The persona normalizer strips upstream `tools` and `model` metadata before runtime creation. The child inherits the parent effective DSH tool scope and the spawn request owns provider/model/effort. Plugin phân phối một bundled ECC snapshot đã pin. Remote mode là đường update chủ động sau MVP, fetch vào DSH-managed cache rồi atomic-swap sang revision mới. MVP không fetch ECC lúc runtime; không được fetch main/latest mỗi lần startup và không nhận path cục bộ của tác giả. Nếu thiếu bundle, ref hoặc integrity không hợp lệ, plugin fail fast với lỗi có source/revision. Remote update lỗi giữ revision đang chạy; không đổi nửa chừng. ### 3.1 DSH install scope và asset root DSH không có thư mục plugin tương đương `.claude/plugins/` hoặc `.codex/plugins/`. Plugin được cài theo profile: ~~~text /profiles//package.json /profiles//node_modules/@staavanothanh/dsh-ecc/ # logical package location ~~~ `DSH_HOME` có thứ tự ưu tiên: configured path, biến môi trường `DSH_HOME`, rồi `~/.dsh`. Lệnh `dsh plugin --profile add ` chạy pnpm trong profile directory. Sau khi package có `dsh.bundle.patch`, DSH thêm package đó vào `dsh.profile.bundles` và mount patch khi profile boot. `node_modules` ở trên chỉ là layout thường gặp; pnpm store/symlink và fallback `/profiles/node_modules` không phải API mà plugin được phép hard-code. Package dsh-ecc phải tự mang ECC snapshot trong package artifact: ~~~text / assets/ecc/ agents/*.md skills/*/SKILL.md ~~~ `EccSourceManager` lấy `packageRoot` từ module/package đã được DSH resolve và chọn `/assets/ecc` làm bundled root. `process.cwd()`, project `.agents/skills`, DSH `.agent-presets` và path cục bộ của tác giả không phải nguồn mặc định cho ECC assets. ECC agents vẫn là persona markdown, không được copy thành DSH `agent.cordis.yml` preset. Remote update (nếu bật) dùng cache do plugin sở hữu dưới `/cache/dsh-ecc/ecc//`; cache chỉ thay thế bundled revision sau khi verify integrity. ### 3.2 Package và distribution contract MVP local dùng checkout để phát triển, nhưng runtime contract phải dùng package đã build. Repository checkout không phải runtime asset root của user khác. Package tối thiểu phải chứa: ~~~text @staavanothanh/dsh-ecc/ package.json # name, version, exports, dsh.bundle.patch dist/ # compiled runtime/plugin entry assets/ecc/ # pinned ECC snapshot cordis.patch.yml # DSH profile bundle patch LICENSE NOTICE # ECC/reference MIT attribution ~~~ `package.json` phải khai báo `dsh.bundle.patch` trỏ tới `./cordis.patch.yml`. Không bundle DSH core vào package; khai báo DSH runtime packages dưới peer/compatible dependency range và fail rõ nếu host thiếu API. `files`/exports của package phải include `dist`, `assets/ecc`, patch và license; `npm pack --dry-run` là preflight bắt buộc để tránh phát hành thiếu agent hoặc skill. Kênh cài đặt chuẩn: ~~~text # GitHub release tarball (canonical community install channel) dsh plugin --profile add https://github.com/staavanothanh/dsh-ecc/releases/download/v/staavanothanh-dsh-ecc-.tgz # GitHub repository/tag (development fallback) dsh plugin --profile add github:staavanothanh/dsh-ecc#v # Local development only dsh plugin --profile add file: ~~~ Tarball/GitHub command phải cài package vào profile, để DSH tự reconcile `dsh.profile.bundles`. User không cần copy `agents` hoặc `skills` vào `.dsh` thủ công. Update/rollback dùng tarball cụ thể: ~~~text # Migration from an older profile that used the unscoped package. dsh plugin --profile remove dsh-ecc dsh plugin --profile add https://github.com/staavanothanh/dsh-ecc/releases/download/v/staavanothanh-dsh-ecc-.tgz # Rollback: remove the scoped package, then add the exact known-good tarball. dsh plugin --profile remove @staavanothanh/dsh-ecc ~~~ Release phải pin ba giá trị: dsh-ecc version, ECC revision và dependency range của DSH. CI phải build package, kiểm tra package contents, cài vào một `DSH_HOME` tạm, boot profile sạch và xác nhận bundle patch/asset resolver hoạt động. GitHub tag và registry version dùng cùng một source revision; không phát hành artifact lấy ECC từ `main/latest`. ### 3.3 Kế hoạch cập nhật ECC sau MVP Mỗi bản phát hành dsh-ecc vẫn là một bundle ECC bất biến. Pipeline hậu MVP sẽ: 1. theo dõi tag/release mới của `affaan-m/ECC` (scheduled check hoặc maintainer trigger), không theo dõi `main` như một dependency runtime; 2. cập nhật `eccSource.ref`, commit SHA và integrity trong manifest; 3. build lại closure `agents` và `skills`, đồng thời cập nhật `NOTICE`; 4. chạy compatibility suite cho frontmatter agent/skill và tool mapping trước khi phát hành dsh-ecc patch/minor version; 5. publish GitHub tag và registry artifact từ cùng một commit. Sau khi pipeline bundle ổn định, có thể triển khai `mode: remote` như một opt-in. Remote mode chỉ nhận ECC commit/ref đã pin, verify integrity trước khi atomic-swap và giữ revision cũ để rollback; nó không thay thế quy trình release định kỳ. transportProvider không phải LLM provider. Nó được resolve qua ctx.subagents.getProvider và phải hỗ trợ continuable. LLM provider chỉ nằm trong request spawn và được validate qua ctx.llm. ## 4. Contract của spawn ### 4.1 Input schema ~~~ type SpawnSubagentInput = { operation?: "start" | "followup"; // mặc định start; fallback khi host không có control surface prompt: string; // instruction đầu tiên hoặc follow-up cho child provider?: string; // bắt buộc với operation=start model?: string; // bắt buộc với operation=start effort?: string; // bắt buộc nếu model công bố effort ids persona?: string; // ECC persona id, ví dụ ecc:architect label?: string; // label hiển thị; plugin sẽ prefix dsh-ecc: childId?: string; // bắt buộc với operation=followup }; ~~~ `operation=start` là mặc định. Với start, prompt, provider và model không được rỗng. Effort là required có điều kiện: nếu model công bố effort ids thì main agent phải chọn một id hoặc friendly name hợp lệ; nếu catalog effort rỗng, plugin bỏ field `reasoningEffort` để DSH/provider dùng default. Với `operation=followup`, `childId` và prompt là bắt buộc; route/persona snapshot lấy từ binding và descriptor đã snapshot, không nhận route mới từ prompt. persona nếu có phải là identifier dạng ecc:; plugin resolve tới đúng một file ECC agents/.md. Tool không nhận raw persona markdown từ model. label chỉ là display metadata. Plugin tạo id riêng, chống collision và không cho user ghi đè parent/session id. ### 4.2 Validation và route resolution Thứ tự trước khi tạo child: 1. Validate shape và length của input. 2. Gọi ctx.llm.resolveModelInfo(provider, model). 3. Đọc modelInfo.reasoningEfforts: - nếu danh sách có dữ liệu, map canonical id hoặc friendly name; - nếu danh sách rỗng, không yêu cầu input effort và không đưa reasoningEffort vào call config. 4. Gọi ctx.llm.resolveCallConfig(...) để DSH kiểm tra route/model/effort lần cuối. 5. Normalize and resolve optional ECC persona. The adapter keeps name, description and Markdown body, and strips upstream `tools` and `model`. 6. Kiểm tra transport capability cho persona. 7. Chỉ sau mọi bước trên mới gọi ctx.subagents.startContinuable. Không clamp effort, không fallback provider/model, không silently retry sang route khác. Nếu settings hot-reload xóa route sau khi child đã tạo, follow-up phải trả lỗi route unavailable; snapshot child không bị đổi âm thầm. ### 4.3 Continuation contract Child luôn được tạo bằng startContinuable; không expose cờ one-shot hoặc run_in_background: false. Kết quả phải chứa child handle để DSH session/control surface có thể follow-up: ~~~ type SpawnSubagentResult = { status: "success" | "warning" | "error"; summary: string; next_actions: string[]; artifacts: [{ kind: "subagent"; childId: string; label: string; provider: string; model: string; reasoningEffort?: string; persona?: string; continuation: "enabled"; }]; error?: { code: string; retryable: boolean; stopCondition?: string; }; }; ~~~ next_actions phải nói rõ child có thể được tiếp tục trong session. Đường chính Đường chính là DSH `ctx.subagents.followup`/control surface. Nếu host hoặc UI đang không expose control surface, chính tool này dùng `operation: "followup"` như fallback; fallback chỉ gọi runtime API, không gọi một model-facing tool khác. MVP không được báo `continuation: "enabled"` nếu cả hai đường đều unavailable. Implementation checkpoint: plugin đã có service thuần cho route/effort/persona và adapter DSH đăng ký `spawn` qua `ctx.tools`, skills qua `ctx.skills`, continuable setup qua `ctx.subagents`, và follow-up qua API `ctx.subagents.followup` với parent agent. Effort được truyền qua field host-local trong child options rồi cài bằng `installModelSelection`; request header được seed để cold-resume không mất effort. Binding audit hiện là in-memory ở checkpoint này; durable binding chưa được xem là MVP complete. ### 4.4 Optional ECC persona persona không phải preset/mode selector. `EccPersonaCatalog` dùng một bước `PersonaNormalizer` trước khi tạo runtime definition: - giữ frontmatter `name` và `description`; - loại bỏ `tools` và `model` của upstream ECC; - giữ Markdown body làm persona/instructions; - giữ content hash trên file upstream đầy đủ để phát hiện thay đổi ECC. Body được truyền qua child persona (hoặc system-prompt section tương đương). Request `spawn` là nguồn duy nhất cho provider, model và effort; không có `modelHint` từ agent Markdown. Khi không gửi `toolFilter`, DSH compose child từ parent. Child nhận effective tool scope của parent và các contribution native của DSH. Với continuable in-process child, native `report` tool vẫn được đăng ký và có thể đánh thức parent theo `reportDelivery`. `assets/ecc/agents/*.md` không bị rewrite. Normalizer chỉ tạo một bản frontmatter runtime mới, không mutate source document. DSH continuable descriptor persist persona và selection snapshot cho cold-resume. Vì persona không còn tool allowlist riêng, follow-up tiếp tục dùng tool scope được DSH compose từ parent và các restriction của host. ### 4.5 Contract của `spawn_fork` `spawn_fork` dùng cùng input/output schema và validation với `spawn`. Khác biệt là transport cố định ở provider `fork` của DSH; model-facing input không được đổi transport bằng field tùy ý. Runtime gọi trực tiếp `ctx.subagents.startContinuable({ provider: "fork", request: { parent, ... }})`. Provider fork của DSH tạo seed từ các event thuộc các parent turn đã kết thúc (`turn/end`); turn đang in-flight không nằm trong seed. Fork child vẫn nhận provider/model explicit và effort có điều kiện, persona ECC đã normalize và tool scope do parent compose. Binding được lưu trong store riêng của `spawn_fork`, vì vậy follow-up của `spawn` không thể dùng nhầm fork child và ngược lại. Follow-up/cold-resume dùng cùng route/persona snapshot như lúc start. DSH upstream cảnh báo rằng ghép fork seed với continuable setup/report prefix có thể tạo context composition đặc biệt. Đây là behavior được test ở adapter; dsh-ecc không gọi nested model-facing `subagent_fork` và không sửa DSH core. ## 5. Skills contract ### 5.1 Provider EccSkillProvider đăng ký qua ctx.skills.registerProvider. Provider discover eccRoot/skills//SKILL.md, parse frontmatter/body và trả definition theo DSH SkillProvider contract: ~~~ type EccSkillDefinition = { name: string; // internal kebab-case name; user entrypoint is /ecc: description: string; whenToUse?: string; invocation: { userInvocable: boolean; modelInvocable: boolean; }; content: string; resourceBase: string; source: "ecc"; }; ~~~ disable-model-invocation và metadata ECC phải được chuyển thành policy modelInvocable. Skill provider phải invalidate khi SKILL.md thay đổi và không cache content cũ sau khi event skills/change phát ra. Nếu DSH export public parser parseSkillFile, provider được phép dùng parser đó. Nếu parser là internal hoặc export thay đổi, adapter dùng parser frontmatter tối thiểu có test tương thích; không import private module bằng deep path. ### 5.2 Tên và slash invocation DSH có thể yêu cầu canonical registry name không chứa dấu hai chấm, vì vậy provider nội bộ có thể dùng ``, nhưng dsh-ecc **chỉ công bố và nhận invocation có namespace `/ecc:`**: - /ecc:: adapter resolve trực tiếp ECC skill cùng tên. - /: không phải entrypoint của dsh-ecc; native/project skill tự quyết. Alias có prefix là bắt buộc để tránh collision giữa skill ECC và skill cùng tên được cài bởi DSH hoặc plugin khác. Alias không mutate raw user message. Nó inject skill content vào context theo format DSH skill tool sử dụng. Nếu alias target không tồn tại, để nguyên user input và trả diagnostic chỉ khi host hỗ trợ diagnostics; không chặn hội thoại. Model invocation vẫn dựa trên DSH catalog/tool policy. Adapter không nhồi toàn body của tất cả skill vào system prompt; chỉ publish digest/metadata, sau đó model gọi skill loader khi cần. ## 6. DSH settings và persistence Plugin đọc settings bằng ctx.settings/namespace API để lấy config và theo dõi thay đổi. Nó không parse settings.yaml và không ghi mỗi child thành một settings row. Route capability đến từ DSH LLM settings (reasoningEfforts). Child binding là plugin-owned durable state: ~~~ type ChildBinding = { childId: string; label: string; transportProvider: string; provider: string; model: string; reasoningEffort?: string; persona?: string; createdAt: string; }; ~~~ Binding được dùng bởi registerContinuableSetup trong fresh start và cold resume. Store phải atomic và scope theo workspace/session; mất binding là lỗi resume rõ ràng, không fallback sang parent route. Lifecycle/persistence implementation được tham chiếu từ `dsh-background-agents` và `dsh-agent-teams`; hai repo là blueprint để adapt, không phải runtime dependency. ## 7. Error codes MVP tối thiểu: | Code | Ý nghĩa | Tạo child? | Retry | | --- | --- | ---: | --- | | INVALID_INPUT | schema/length/label không hợp lệ | Không | Sửa input | | MODEL_NOT_FOUND | provider/model không resolve | Không | Chọn route khác | | UNSUPPORTED_EFFORT | effort không có trong model metadata | Không | Chọn effort hợp lệ | | CALL_CONFIG_INVALID | DSH resolveCallConfig reject | Không | Sửa selection | | PERSONA_NOT_FOUND | ECC persona không tồn tại/parse lỗi | Không | Chọn persona khác | | PERSONA_CAPABILITY_UNAVAILABLE | Transport không hỗ trợ persona injection | Không | Đổi transport | | TRANSPORT_UNAVAILABLE | provider không có hoặc không continuable | Không | Cấu hình transport | | CHILD_START_FAILED | startContinuable thất bại | Có thể chưa | Retry có giới hạn | | RESUME_BINDING_MISSING | cold resume không có snapshot | Child cũ | Dừng, không fallback | Mọi lỗi model-facing phải có summary, next_actions, retryable và stopCondition khi retry không an toàn. ## 8. Acceptance criteria ### Functional - `spawn` và `spawn_fork` reject route/effort sai trước khi tạo child. - `spawn_fork` seed chỉ completed parent turns và loại trừ in-flight turn. - Binding của `spawn` và `spawn_fork` không được dùng chéo. - Load được ECC persona/skill từ một managed ECC revision xác định. - /ecc:name resolve đúng ECC skill; /name không phải entrypoint của dsh-ecc. - Child sử dụng đúng provider/model/reasoning effort đã chọn. - Follow-up vẫn dùng selection đó sau cold resume. - Persona được resolve từ ECC id và không nhận raw system prompt. - Frontmatter `tools` và `model` của ECC không xuất hiện trong persona runtime; child kế thừa effective tool scope của parent. - Persona continuable có thể dùng native `report` tool của DSH; parent nhận report theo `reportDelivery` khi contribution đó được host mount. - `npm pack --dry-run` (hoặc package-manager tương đương) chứa `dist`, `assets/ecc`, `cordis.patch.yml`, license và NOTICE; không chứa absolute local path. - Một DSH_HOME tạm cài được package từ registry hoặc GitHub tag bằng một lệnh, boot profile sạch và resolve được bundle patch. - Update, rollback và remove chỉ dùng DSH plugin command; không cần copy asset thủ công. - Không có core DSH diff. ### Reliability/security - Unit test parser, collision, effort mapping, binding serialization. - Integration test start → follow-up → process restart/cold-resume. - Package smoke test trên clean DSH_HOME, install/update/remove và bundle manifest reconciliation. - Input path nằm trong managed ECC revision; không có command injection từ label/persona id. - License notice MIT cho code ECC/reference được copy/adapt. Mục tiêu coverage implementation: tối thiểu 80% theo policy workspace; ưu tiên test lifecycle và route-selection hơn snapshot output. ## 9. Phase sau MVP - ECC sync pipeline: theo dõi release/tag ECC, cập nhật commit/integrity, rebuild bundled closure, chạy compatibility suite và phát hành dsh-ecc version mới. - Opt-in remote revision update với verify, atomic swap, rollback và giữ nguyên revision đang phục vụ child cũ. - Additive persona mode (giữ deployment persona của parent) nếu host cần; native mode hiện tại dùng child-scoped persona section của DSH. - Schema/behavior compatibility tests cho các tool alias và MCP public-name normalization. - UI/control tool riêng cho list/follow-up/stop child nếu host chưa expose. - Skill namespaced registry nếu DSH cho phép provider-qualified lookup. - Đồng bộ profile ECC qua config UI thay vì chỉ config file. - MCP/rules/commands adapter sau khi agents/skills ổn định.