From df9bc7b376db08db2e51d848637f9549a94a84b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Sep 2026 06:24:48 +0000 Subject: [PATCH] chore: synchronize released plugin bundles --- PROVENANCE.json | 8 +- README.md | 2 +- .../.claude-plugin/plugin.json | 2 +- .../.codex-plugin/plugin.json | 2 +- .../skills/engineering-workflow/SKILL.md | 3 +- .../claude_agents/workflow-explorer.md.tmpl | 9 + .../claude_agents/workflow-reviewer.md.tmpl | 9 + .../claude_agents/workflow-utility.md.tmpl | 8 + .../ENGINEERING_WORKFLOW_STATE.yaml.tmpl | 1 + .../references/claude_model_profiles.md | 26 +++ .../references/platform_compatibility.md | 2 +- .../references/target_workflow_upgrade.md | 22 ++- .../scripts/plan_lifecycle.py | 16 +- .../scripts/upgrade_target_workflow.py | 167 +++++++++++++++--- .../scripts/validate_skill_repo.py | 41 +++++ 15 files changed, 276 insertions(+), 42 deletions(-) create mode 100644 plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-explorer.md.tmpl create mode 100644 plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-reviewer.md.tmpl create mode 100644 plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-utility.md.tmpl create mode 100644 plugins/engineering-workflow/skills/engineering-workflow/references/claude_model_profiles.md diff --git a/PROVENANCE.json b/PROVENANCE.json index 6c97c9e..bbda9ad 100644 --- a/PROVENANCE.json +++ b/PROVENANCE.json @@ -4,13 +4,13 @@ "bundles": [ { "name": "engineering-workflow", - "version": "0.9.9", + "version": "0.9.10", "source_repository": "https://github.com/xeonvs/codex-engineering-workflow", "source_policy": "latest-tag", - "source_ref": "v0.9.9", - "source_commit": "e73c8af994350ae0e463ab8856dff12b761f12ee", + "source_ref": "v0.9.10", + "source_commit": "5db6b8422e39a84c090ff52b60861f7642152f55", "source_path": "plugins/engineering-workflow", - "bundle_sha256": "5aa3b74527d7cd4260ef90223222e3c2e998fae016b3c02919de676449f2e0f3" + "bundle_sha256": "65ea54e0f3bacedca91dd73a0ad7bd9f36ad6f2617fc0c4809edc54902e6931a" }, { "name": "tgrep-search", diff --git a/README.md b/README.md index 499ed75..a37a031 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ reviewed local bundle held in this repository. | Plugin | Version | Purpose | Canonical source | | --- | --- | --- | --- | -| [`engineering-workflow`](plugins/engineering-workflow/) | 0.9.9 | Audit, plan, migrate, validate, and maintain repository engineering workflows. | [`xeonvs/codex-engineering-workflow`](https://github.com/xeonvs/codex-engineering-workflow) | +| [`engineering-workflow`](plugins/engineering-workflow/) | 0.9.10 | Audit, plan, migrate, validate, and maintain repository engineering workflows. | [`xeonvs/codex-engineering-workflow`](https://github.com/xeonvs/codex-engineering-workflow) | | [`tgrep-search`](plugins/tgrep-search/) | 1.0.3 | Search local source trees efficiently with the tgrep trigram index. | [`xeonvs/tgrep-search`](https://github.com/xeonvs/tgrep-search) | diff --git a/plugins/engineering-workflow/.claude-plugin/plugin.json b/plugins/engineering-workflow/.claude-plugin/plugin.json index 52b5bbe..7525500 100644 --- a/plugins/engineering-workflow/.claude-plugin/plugin.json +++ b/plugins/engineering-workflow/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "engineering-workflow", - "version": "0.9.9", + "version": "0.9.10", "description": "Audit, plan, migrate, validate, and maintain repository engineering workflows.", "author": { "name": "xeonvs", diff --git a/plugins/engineering-workflow/.codex-plugin/plugin.json b/plugins/engineering-workflow/.codex-plugin/plugin.json index a44bd9d..4a41b43 100644 --- a/plugins/engineering-workflow/.codex-plugin/plugin.json +++ b/plugins/engineering-workflow/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "engineering-workflow", - "version": "0.9.9", + "version": "0.9.10", "description": "Audit, plan, migrate, validate, and maintain repository engineering workflows.", "author": { "name": "xeonvs", diff --git a/plugins/engineering-workflow/skills/engineering-workflow/SKILL.md b/plugins/engineering-workflow/skills/engineering-workflow/SKILL.md index 17380ff..0d40f6b 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/SKILL.md +++ b/plugins/engineering-workflow/skills/engineering-workflow/SKILL.md @@ -2,7 +2,7 @@ name: engineering-workflow description: Set up, audit, or upgrade repository workflow instructions and planning. Use for workflow changes or explicit skill refresh/update; ordinary repository work does not invoke migration. metadata: - version: 0.9.9 + version: 0.9.10 --- # Engineering Workflow @@ -64,6 +64,7 @@ Use this skill for the workflow layer around a repository. Keep product, domain, - Host capability boundaries and verified Codex/Claude integrations: `references/platform_compatibility.md` - Programmatic tool routing, agent routing, and shared-state ownership: `references/agent_orchestration.md` - Current capability-to-model mapping: `references/model_profiles.md` +- Claude Code project-agent model mapping after explicit opt-in: `references/claude_model_profiles.md` - Installed-skill refresh and update: `references/skill_update.md` - Target workflow migration: `references/target_workflow_upgrade.md` - Validation command and isolation policy: `references/validation_safety.md` diff --git a/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-explorer.md.tmpl b/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-explorer.md.tmpl new file mode 100644 index 0000000..4490854 --- /dev/null +++ b/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-explorer.md.tmpl @@ -0,0 +1,9 @@ +--- +name: workflow-explorer +description: Collect bounded read-heavy repository evidence for the root. +tools: Read, Grep, Glob +model: sonnet +effort: medium +--- + +Inspect only the path scope and accessible inputs in the root's self-contained packet. Do not edit files, write shared state, or spawn child agents. Return status, distilled findings, file references, checks performed, blockers, and accessible artifact paths. Keep raw output bounded; report an inaccessible required input instead of reconstructing missing context. Stop when the requested evidence is sufficient. diff --git a/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-reviewer.md.tmpl b/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-reviewer.md.tmpl new file mode 100644 index 0000000..826173b --- /dev/null +++ b/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-reviewer.md.tmpl @@ -0,0 +1,9 @@ +--- +name: workflow-reviewer +description: Review a bounded change packet for correctness and risk, then report findings to the root. +tools: Read, Grep, Glob +model: sonnet +effort: medium +--- + +Review only the self-contained change packet and accessible evidence supplied by the root. Do not edit files, write shared state, or spawn child agents. Return status; findings with severity, confidence, exact evidence paths, and assumptions; checks performed; blockers; and the stopping or escalation condition. State clearly when there are no findings. Report missing required context rather than inferring it. diff --git a/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-utility.md.tmpl b/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-utility.md.tmpl new file mode 100644 index 0000000..ab694ab --- /dev/null +++ b/plugins/engineering-workflow/skills/engineering-workflow/assets/claude_agents/workflow-utility.md.tmpl @@ -0,0 +1,8 @@ +--- +name: workflow-utility +description: Handle small bounded semantic checks and return a compact result to the root. +tools: Read, Grep, Glob +model: haiku +--- + +Use only the self-contained packet and accessible inputs supplied by the root. Do not expand scope, edit files, write shared state, or spawn child agents. Return status, concise findings, evidence paths, blockers, `needs_escalation`, and whether the stopping condition was met. If a required input is inaccessible, report that blocker instead of guessing. Make at most one transient retry when the packet permits it. diff --git a/plugins/engineering-workflow/skills/engineering-workflow/assets/templates/ENGINEERING_WORKFLOW_STATE.yaml.tmpl b/plugins/engineering-workflow/skills/engineering-workflow/assets/templates/ENGINEERING_WORKFLOW_STATE.yaml.tmpl index 025fb4b..2c57a09 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/assets/templates/ENGINEERING_WORKFLOW_STATE.yaml.tmpl +++ b/plugins/engineering-workflow/skills/engineering-workflow/assets/templates/ENGINEERING_WORKFLOW_STATE.yaml.tmpl @@ -25,6 +25,7 @@ plan_archive_indexes: - docs/README.md active_plan: PLANS.md runtime_agent_config_managed: false +runtime_claude_agent_config_managed: false instruction_contract_version: 3 planning_contract_version: 2 orchestration_contract_version: 3 diff --git a/plugins/engineering-workflow/skills/engineering-workflow/references/claude_model_profiles.md b/plugins/engineering-workflow/skills/engineering-workflow/references/claude_model_profiles.md new file mode 100644 index 0000000..eed22d5 --- /dev/null +++ b/plugins/engineering-workflow/skills/engineering-workflow/references/claude_model_profiles.md @@ -0,0 +1,26 @@ +# Claude Code Model Profiles + +Use this file only in Claude Code as the canonical owner of concrete Claude model and effort recommendations. `agent_orchestration.md` owns task-shape routing; `platform_compatibility.md` selects the invoking host. These profiles apply only when the user explicitly opts in to project-level Claude Code agents during a target workflow upgrade. + +## Source Snapshot + +Verified against the official Claude Code [subagent](https://code.claude.com/docs/en/sub-agents) and [model configuration](https://code.claude.com/docs/en/model-config) documentation on 2026-09-25. Check the active client's model availability, provider restrictions, and supported effort levels when invoking a profile; a repository upgrader cannot infer those properties from project files alone. + +## Claude Capability Mapping + +| Route | Claude Code model | Effort | Project agent | +| --- | --- | --- | --- | +| Bounded semantic utility | `haiku` | Inherit the client's choice; no fixed `effort` field | `workflow-utility` | +| Read-heavy exploration | `sonnet` | `medium` | `workflow-explorer` | +| Evidence-first review | `sonnet` | `medium` | `workflow-reviewer` | +| Bounded implementation | `sonnet` | `medium` | Select natively for the task; no persistent project agent is required | + +The three optional project agents are read-only and cannot spawn child agents. The root supplies each agent a bounded, self-contained packet with accessible paths and a stopping condition. The utility handles small semantic work, not deterministic commands or polling. Review findings return to the root for acceptance and final validation. + +Reserve `opus` for exceptionally difficult, high-consequence semantic work when the user selects it or confirms a proposed escalation. Give the concrete quality or risk reason before proposing that escalation. A user-selected Opus session already provides the choice. Do not change the session's model or create a persistent Opus profile on the agent's initiative. + +## Configuration Boundary + +The target upgrader creates `.claude/agents/workflow-{utility,explorer,reviewer}.md` only after the separate Claude opt-in or a valid workflow state recording that prior choice. The existing Codex opt-in does not imply Claude opt-in. An existing agent definition is replaceable only when its complete bytes match a registered prior generated template; preserve customized model pins and instructions. Keep `CLAUDE.md`, native settings, and unrelated agents under their existing owners. + +Claude Code's model choice can be constrained by the invoking client, provider, and managed policy; effort can be capped. Report an unavailable model or restriction instead of silently substituting a model, weakening a restriction, or mutating global settings. Do not set `CLAUDE_CODE_SUBAGENT_MODEL_FORCE`: it overrides the per-agent model field and defeats role-based selection. The same effort label is not equivalent across different models or providers. diff --git a/plugins/engineering-workflow/skills/engineering-workflow/references/platform_compatibility.md b/plugins/engineering-workflow/skills/engineering-workflow/references/platform_compatibility.md index 85fb828..6333bfd 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/references/platform_compatibility.md +++ b/plugins/engineering-workflow/skills/engineering-workflow/references/platform_compatibility.md @@ -22,7 +22,7 @@ Select this mode only when the actual invoking host is Codex. Codex mode may use Select this mode only when the actual invoking host is Claude Code. When invoked as `/engineering-workflow:engineering-workflow`, explicitly read the target repository's applicable root and nested `AGENTS.md` files as workflow artifacts before acting. Do not claim that Claude Code automatically discovers or applies Codex-specific `AGENTS.md` semantics. -Preserve Claude Code's native session, built-in-agent, and custom-agent model/effort choices, including provider and managed-setting restrictions. Do not set a per-call model/effort override merely because a Codex role recommends one. Model aliases and available effort levels depend on the Claude client, provider, and selected model; matching effort names do not establish equivalent reasoning across providers. Native `CLAUDE.md`, rules, permissions, and existing `.claude` configuration remain authoritative within the host's instruction hierarchy. +Preserve Claude Code's native session, built-in-agent, and custom-agent model/effort choices, including provider and managed-setting restrictions. Do not set a per-call model/effort override merely because a Codex role recommends one. For an explicitly requested project-agent opt-in, use the Claude-only mapping in `claude_model_profiles.md` and retain its ownership and availability checks. Model aliases and available effort levels depend on the Claude client, provider, and selected model; matching effort names do not establish equivalent reasoning across providers. Native `CLAUDE.md`, rules, permissions, and existing `.claude` configuration remain authoritative within the host's instruction hierarchy. For continuation, delegation scope, and handoff, read the shared Default Route, Task Continuity And Handoff, Subagent Contract, and Monitoring And Long-Running Work sections of `agent_orchestration.md`. Use only delegation and waiting capabilities exposed by Claude Code; the skill does not enable agent teams, recursive delegation, or experimental workflows. Keep bounded independent work with the root when native delegation is unavailable. diff --git a/plugins/engineering-workflow/skills/engineering-workflow/references/target_workflow_upgrade.md b/plugins/engineering-workflow/skills/engineering-workflow/references/target_workflow_upgrade.md index 30daf70..798ed87 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/references/target_workflow_upgrade.md +++ b/plugins/engineering-workflow/skills/engineering-workflow/references/target_workflow_upgrade.md @@ -16,15 +16,16 @@ Use this canonical reference for `upgrade_target_workflow`, which migrates the w 10. Mutation Boundaries 11. Apply Sequence 12. Codex Configuration -13. Workflow State Manifest -14. Validation And Rollback +13. Claude Code Configuration +14. Workflow State Manifest +15. Validation And Rollback ## Prompt Invocation Treat `Upgrade A Target Workflow` plus a target repository as an authorized repo-changing prompt, not as a request for CLI instructions. 1. Resolve the target path and requested version from context; default to the installed skill version. -2. Before prompt apply, review target-local owners affected by the requested release's changed semantics when adoption has not already been established. For customized owners, preserve equivalent rules or make the narrow requested correction under the full planning and privacy gates; ask only for a real ownership conflict. A same-version stamp or `already_current` result proves structural state, not semantic adoption. Version 0.9.9 changes only the installed migration privacy-review boundary and requires no target-local instruction rewrite. Version 0.9.8 updates Codex model profiles and refreshes only exact prior generated agent templates when that configuration was already opted in; it requires no target-local instruction rewrite. Version 0.9.7 changes audit discovery and output only, so it also requires no target-local instruction rewrite. For the 0.9.6 changes, inspect the task-handoff route and efficient-execution owner for root working state, self-contained worker context, transient-versus-durable evidence, and artifact-based recovery. Use already-current evidence, and do not sweep unrelated owners. Then invoke `scripts/upgrade_target_workflow.py --prompt` yourself. +2. Before prompt apply, review target-local owners affected by the requested release's changed semantics when adoption has not already been established. For customized owners, preserve equivalent rules or make the narrow requested correction under the full planning and privacy gates; ask only for a real ownership conflict. A same-version stamp or `already_current` result proves structural state, not semantic adoption. Version 0.9.10 fixes custom archive index preservation and adds separately opted-in Claude project agents; it requires no target-local instruction rewrite. Version 0.9.9 changes only the installed migration privacy-review boundary and requires no target-local instruction rewrite. Version 0.9.8 updates Codex model profiles and refreshes only exact prior generated agent templates when that configuration was already opted in; it requires no target-local instruction rewrite. Version 0.9.7 changes audit discovery and output only, so it also requires no target-local instruction rewrite. For the 0.9.6 changes, inspect the task-handoff route and efficient-execution owner for root working state, self-contained worker context, transient-versus-durable evidence, and artifact-based recovery. Use already-current evidence, and do not sweep unrelated owners. Then invoke `scripts/upgrade_target_workflow.py --prompt` yourself. 3. Prompt mode builds and reviews the read-only migration report first. 4. If ownership, conflicts, privacy, and approvals are resolved, it proceeds through guarded apply and validation automatically. 5. If the result returns `agent_action: ask_targeted_question`, ask only `question_to_ask`; keep any later questions deferred and do not write target files. @@ -34,7 +35,7 @@ Treat `Upgrade A Target Workflow` plus a target repository as an authorized repo If the target already records the requested version, all canonical artifacts exist, instruction and index contracts pass, privacy/conflict checks are clear, no registered pristine bytes need an actual update, and any requested optional agent configuration is already fully present, prompt/apply returns `update_status: already_current` with an empty mutation log. It does not create a plan or rewrite state/index files merely to reconfirm that unchanged result. A missing artifact, older contract, drift, conflict, privacy boundary, or requested but incomplete optional configuration keeps the normal guarded path. -The user may explicitly request report-only behavior; then invoke `--plan`. New runtime agent configuration remains opt-in through the user's prompt and `--include-agent-config`; a valid workflow state manifest recording an earlier opt-in carries that choice into subsequent upgrades. +The user may explicitly request report-only behavior; then invoke `--plan`. Codex runtime agent configuration remains opt-in through the user's prompt and `--include-agent-config`; Claude Code project agents have a separate `--include-claude-agent-config` opt-in. A valid workflow state manifest recording either earlier choice carries only that choice into subsequent upgrades. ## CLI Contract @@ -46,6 +47,7 @@ The user may explicitly request report-only behavior; then invoke `--plan`. New - `--prompt` - `--target-version` - `--include-agent-config` +- `--include-claude-agent-config` - `--approve-privacy-review` - `--format json` @@ -70,6 +72,7 @@ Inspect: - `PLANS.md` and older execution-plan locations - backlog, incident catalog, project principles, compatibility instructions, and equivalent names - `.codex/config.toml` and `.codex/agents/*.toml` +- `.claude/agents/workflow-{utility,explorer,reviewer}.md` when Claude configuration is requested or previously opted in - workflow state manifest and migration notes - external tracker references - repository-owned domain, product, architecture, QA, security, and operational documentation @@ -160,7 +163,7 @@ Do not replace a customized shared file wholesale. Create missing files, replace 4. Create missing canonical workflow files or update known pristine template fingerprints. 5. Create/update managed navigation indexes without replacing unmarked repository prose. 6. Validate the complete instruction graph and indexes; stop before version stamping on any finding. -7. Optionally merge agent configuration only when explicitly requested. +7. Optionally merge each platform's agent configuration only when separately requested or recorded by valid prior state. 8. Write the state manifest with relative paths and contract versions. 9. Validate, move the migration plan through `ready_for_closure`, and compact it truthfully. 10. Re-run the public privacy scan immediately before success. Compare it with the in-memory approved pre-apply fingerprint multiset: a disappeared candidate is safe, while a new, changed, moved, or duplicated finding fails and rolls back, regardless of category. @@ -183,6 +186,12 @@ When `--include-agent-config` is present or the target's valid workflow state re Never place Responses API-only fields in Codex TOML. +## Claude Code Configuration + +When `--include-claude-agent-config` is present or the target's valid workflow state records a prior Claude opt-in, apply the three project-agent templates under `.claude/agents/` using `claude_model_profiles.md` as the model and effort owner. The Codex flag or Codex state field alone never enables this step. Without Claude opt-in, leave `.claude/**` byte-for-byte unchanged. + +Create missing project-agent files only after opt-in. Replace an existing agent file only when its complete bytes match a registered prior generated template. Preserve customized model pins, instructions, unrelated agents, `CLAUDE.md`, settings, and managed configuration. Refuse symbolic or unsafe target paths and keep partial writes within the common rollback transaction. Report an unavailable model or an administrative restriction without overriding the user's client or provider settings. Never set a global model or `CLAUDE_CODE_SUBAGENT_MODEL_FORCE`. + ## Workflow State Manifest Target path: `docs/codex/ENGINEERING_WORKFLOW_STATE.yaml`. @@ -201,11 +210,12 @@ Required fields: - `shared_paths` - `protected_paths` - `runtime_agent_config_managed` +- `runtime_claude_agent_config_managed` - `instruction_contract_version` - `planning_contract_version` - `orchestration_contract_version` -Use repository-relative paths. Never record a workstation path, username, home directory, credential, or private hostname. The manifest governs only listed paths or explicit managed sections; it does not claim an entire documentation directory. +In a valid legacy manifest without `runtime_claude_agent_config_managed`, treat Claude configuration as not opted in; a prior Codex opt-in does not imply it. Use repository-relative paths. Never record a workstation path, username, home directory, credential, or private hostname. The manifest governs only listed paths or explicit managed sections; it does not claim an entire documentation directory. ## Validation And Rollback diff --git a/plugins/engineering-workflow/skills/engineering-workflow/scripts/plan_lifecycle.py b/plugins/engineering-workflow/skills/engineering-workflow/scripts/plan_lifecycle.py index ee918f1..c64952e 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/scripts/plan_lifecycle.py +++ b/plugins/engineering-workflow/skills/engineering-workflow/scripts/plan_lifecycle.py @@ -782,13 +782,15 @@ def _compact_root(existing: str, title: str, archive_path: str | None) -> str: existing[match.start() : section_matches[index + 1].start() if index + 1 < len(section_matches) else None] for index, match in enumerate(section_matches) ] - old_entries = [ - line - for section in sections - if section.splitlines()[0].strip() == "## Recently Completed" - for line in section.splitlines()[1:] - if re.match(r"^- \[x\]", line, re.IGNORECASE) - ] + old_entries: list[str] = [] + for section in sections: + if section.splitlines()[0].strip() != "## Recently Completed": + continue + body = section.split("\n", 1)[1] if "\n" in section else "" + starts = list(re.finditer(r"(?im)^- \[x\].*$", body)) + for index, match in enumerate(starts): + end = starts[index + 1].start() if index + 1 < len(starts) else len(body) + old_entries.append(body[match.start() : end].strip("\n")) suffix = f"; [full archived plan]({archive_path})" if archive_path else "" new_entry = f"- [x] {date.today().isoformat()}: Completed {title}{suffix}." entries = [new_entry, *[item for item in old_entries if item != new_entry]][:10] diff --git a/plugins/engineering-workflow/skills/engineering-workflow/scripts/upgrade_target_workflow.py b/plugins/engineering-workflow/skills/engineering-workflow/scripts/upgrade_target_workflow.py index 7958aa5..151c39f 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/scripts/upgrade_target_workflow.py +++ b/plugins/engineering-workflow/skills/engineering-workflow/scripts/upgrade_target_workflow.py @@ -35,15 +35,18 @@ INDEX_END, INDEX_START, LifecycleError, - check_archive_indexes, check_plan_lifecycle, closure_issues, + planned_explicit_index_writes, planned_index_writes, + resolve_archive_layout, ) SKILL_ROOT = Path(__file__).resolve().parents[1] TEMPLATE_ROOT = SKILL_ROOT / "assets" / "templates" AGENT_TEMPLATE_ROOT = SKILL_ROOT / "assets" / "agents" +CLAUDE_AGENT_TEMPLATE_ROOT = SKILL_ROOT / "assets" / "claude_agents" +CLAUDE_AGENT_NAMES = ("workflow-utility", "workflow-explorer", "workflow-reviewer") CANONICAL_SOURCE_REPO = "https://github.com/xeonvs/codex-engineering-workflow" PLAN_MARKER_START = "" PLAN_MARKER_END = "" @@ -100,6 +103,11 @@ "explorer": "cf28d059b8bc28123a038d2f4c40fe24fe45e5623d2ee73c0e2b81f0a1d381b4", "reviewer": "6182122fcec3d18b14acdabb644b750e58c5d2264d8b7a68eaf54644ef6db133", } +PRIOR_CLAUDE_AGENT_TEMPLATE_HASHES = { + "workflow-utility": {"8203a37549a6face81c6db37678752a4d237876e8cf3d269a7b606aa4709cbdb"}, + "workflow-explorer": {"74de5d3163b9239ca7ea957704b7b5bcd2f7733cf3963dc678f575b42b252f3b"}, + "workflow-reviewer": {"0bf1b825f92e4d4f88c139356e9353d6cf086aefb3e7ba63854c5e269c22d8b2"}, +} def _content_hash(text: str) -> str: @@ -114,6 +122,10 @@ def _is_pristine_prior_agent(name: str, text: str) -> bool: return _content_hash(text) == PRIOR_AGENT_TEMPLATE_HASHES[name] +def _is_pristine_prior_claude_agent(name: str, text: str) -> bool: + return _content_hash(text) in PRIOR_CLAUDE_AGENT_TEMPLATE_HASHES[name] + + def _agent_config_selected(root: Path, explicitly_selected: bool) -> bool: if explicitly_selected: return True @@ -135,6 +147,27 @@ def _agent_config_selected(root: Path, explicitly_selected: bool) -> bool: ) +def _claude_agent_config_selected(root: Path, explicitly_selected: bool) -> bool: + if explicitly_selected: + return True + if _first_symlink_component(root, STATE_MANIFEST_PATH): + return False + state = _read(root / STATE_MANIFEST_PATH) + try: + declared, shared_paths = parse_manifest_path_list(state, "shared_paths") + except ValueError: + return False + expected = {f".claude/agents/{name}.md" for name in CLAUDE_AGENT_NAMES} + return ( + re.search(r"(?m)^schema_version:\s*2\s*$", state) is not None + and re.search(r"(?m)^skill_name:\s*engineering-workflow\s*$", state) is not None + and re.search(r"(?m)^mode:\s*upgrade_target_workflow\s*$", state) is not None + and re.search(r"(?m)^runtime_claude_agent_config_managed:\s*true\s*$", state) is not None + and declared + and expected.issubset(shared_paths) + ) + + class MigrationConflict(RuntimeError): def __init__(self, code: str, message: str): super().__init__(message) @@ -572,7 +605,9 @@ def _existing_active_conflict(plans_text: str) -> str | None: return None -def _scan_contract_conflicts(root: Path, include_agent_config: bool = False) -> list[dict[str, str]]: +def _scan_contract_conflicts( + root: Path, include_agent_config: bool = False, include_claude_agent_config: bool = False +) -> list[dict[str, str]]: findings: list[dict[str, str]] = [] patterns = ( ("compressed_plan_rule", re.compile(r"\b(?:lightweight|compact|short)\s+(?:active\s+)?plan\b", re.IGNORECASE)), @@ -591,6 +626,8 @@ def _scan_contract_conflicts(root: Path, include_agent_config: bool = False) -> if include_agent_config: canonical_mutation_paths.add(".codex/config.toml") canonical_mutation_paths.update(f".codex/agents/{name}.toml" for name in ("utility", "explorer", "reviewer")) + if include_claude_agent_config: + canonical_mutation_paths.update(f".claude/agents/{name}.md" for name in CLAUDE_AGENT_NAMES) reported_symlinks = set() for relative in sorted(canonical_mutation_paths): symlink_component = _first_symlink_component(root, relative) @@ -692,7 +729,9 @@ def _topology(root: Path) -> dict[str, Any]: } -def _proposed_changes(root: Path, include_agent_config: bool) -> list[dict[str, str]]: +def _proposed_changes( + root: Path, include_agent_config: bool, include_claude_agent_config: bool = False +) -> list[dict[str, str]]: changes: list[dict[str, str]] = [] plans_action = "update" if _present(root / "PLANS.md") else "create" changes.append({"path": "PLANS.md", "action": plans_action, "reason": "materialize full migration plan first"}) @@ -758,6 +797,22 @@ def _proposed_changes(root: Path, include_agent_config: bool) -> list[dict[str, changes.append( {"path": path, "action": "update", "reason": "known pristine prior agent template fingerprint"} ) + if include_claude_agent_config: + for name in CLAUDE_AGENT_NAMES: + path = f".claude/agents/{name}.md" + template = (CLAUDE_AGENT_TEMPLATE_ROOT / f"{name}.md.tmpl").read_text(encoding="utf-8") + if not _present(root / path): + changes.append( + {"path": path, "action": "create", "reason": "explicit Claude agent configuration request"} + ) + elif _is_pristine_prior_claude_agent(name, _read(root / path)) and _read(root / path) != template: + changes.append( + { + "path": path, + "action": "update", + "reason": "known pristine prior Claude agent template fingerprint", + } + ) return changes @@ -766,14 +821,20 @@ def build_migration_report( target_version: str, include_agent_config: bool = False, approved_privacy_review: str | None = None, + include_claude_agent_config: bool = False, ) -> dict[str, Any]: target_version = _validate_target_version(target_version) root = repo.resolve() if not root.is_dir(): raise MigrationConflict("missing_repository", "Target repository does not exist") include_agent_config = _agent_config_selected(root, include_agent_config) + include_claude_agent_config = _claude_agent_config_selected(root, include_claude_agent_config) audit = audit_repo(root) - conflicts = _scan_contract_conflicts(root, include_agent_config=include_agent_config) + conflicts = _scan_contract_conflicts( + root, + include_agent_config=include_agent_config, + include_claude_agent_config=include_claude_agent_config, + ) state_text = _read(root / STATE_MANIFEST_PATH) if state_text and not _first_symlink_component(root, STATE_MANIFEST_PATH): try: @@ -862,10 +923,14 @@ def build_migration_report( ) elif finding.get("requires_decision") == "true": questions.append(f"Which source should own the contradictory planning rule in {finding['path']}?") - proposed = _proposed_changes(root, include_agent_config) + proposed = _proposed_changes(root, include_agent_config, include_claude_agent_config) touched = {item["path"] for item in proposed} ownership = audit["ownership"] - protected = sorted(set(ownership["protected"] + ownership["unknown"] + ownership["external_source_of_truth"])) + protected = set(ownership["protected"] + ownership["unknown"] + ownership["external_source_of_truth"]) + protected.difference_update( + item["path"] for item in proposed if item["reason"] == "known pristine prior Claude agent template fingerprint" + ) + protected = sorted(protected) current_workflow_version = ( _manifest_version(root / STATE_MANIFEST_PATH) if not _first_symlink_component(root, STATE_MANIFEST_PATH) @@ -893,7 +958,7 @@ def build_migration_report( "historical_paths": ownership["historical"], "conflicts": conflicts, "instruction_contract": instruction_contract, - "archive_indexes": audit["archive_indexes"], + "archive_indexes": check_plan_lifecycle(root)["archive_indexes"], "privacy_findings": privacy_findings, "privacy_review": privacy_review, "proposed_changes": proposed, @@ -909,6 +974,7 @@ def build_migration_report( ], "rollback_plan": "Restore every pre-migration file snapshot in reverse mutation order; preserve a PLANS.md failure note if recovery is needed.", "include_agent_config": include_agent_config, + "include_claude_agent_config": include_claude_agent_config, } @@ -933,12 +999,26 @@ def _optional_agent_config_is_current(root: Path, include_agent_config: bool, to ) +def _optional_claude_agent_config_is_current(root: Path, include_claude_agent_config: bool) -> bool: + if not include_claude_agent_config: + return True + state = _read(root / STATE_MANIFEST_PATH) + return re.search(r"(?m)^runtime_claude_agent_config_managed:\s*true\s*$", state) is not None and all( + _present(root / f".claude/agents/{name}.md") and not _first_symlink_component(root, f".claude/agents/{name}.md") + for name in CLAUDE_AGENT_NAMES + ) + + def _already_current(report: dict[str, Any], include_agent_config: bool, root: Path) -> bool: topology = report["detected_topology"] required_artifacts = ("root_agents", "plans", "backlog", "pitfalls", "principles", "state_manifest") pristine_update_pending = any( change.get("reason") - in {"known pristine legacy template fingerprint", "known pristine prior agent template fingerprint"} + in { + "known pristine legacy template fingerprint", + "known pristine prior agent template fingerprint", + "known pristine prior Claude agent template fingerprint", + } for change in report["proposed_changes"] ) return ( @@ -951,6 +1031,7 @@ def _already_current(report: dict[str, Any], include_agent_config: bool, root: P and report["archive_indexes"]["success"] and not pristine_update_pending and _optional_agent_config_is_current(root, include_agent_config, topology) + and _optional_claude_agent_config_is_current(root, report["include_claude_agent_config"]) ) @@ -976,7 +1057,12 @@ def _already_current_result(report: dict[str, Any], *, mode: str) -> dict[str, A def _migration_plan( - target_version: str, include_agent_config: bool, *, done: bool = False, result: str = "Not run yet." + target_version: str, + include_agent_config: bool, + *, + include_claude_agent_config: bool = False, + done: bool = False, + result: str = "Not run yet.", ) -> str: status = "ready_for_closure" if done else "active" checkbox = "x" if done else " " @@ -1004,7 +1090,7 @@ def _migration_plan( ### Requested Scope - Materialize the full migration plan before any other target write. -- Add missing canonical workflow structure, exact ownership state, and optional agent configuration only when explicitly selected. +- Add missing canonical workflow structure, exact ownership state, and each platform's optional agent configuration only when separately selected. ### Requirement Traceability @@ -1012,7 +1098,7 @@ def _migration_plan( | --- | --- | --- | --- | --- | --- | | REQ-001 | Full migration plan is the first target write. | engineering-workflow contract | WQ-01 | Plan schema validates. | done | | REQ-002 | Workflow-owned structure and manifest reach {target_version} without modifying protected docs. | migration report | WQ-02 | Protected snapshots agree and manifest parses. | {req_status} | -| REQ-003 | Runtime agent configuration follows the explicit selection. | user invocation | WQ-03 | Config is {"structurally merged" if include_agent_config else "untouched"}. | {req_status} | +| REQ-003 | Runtime agent configuration follows each platform's explicit selection. | user invocation | WQ-03 | Codex config is {"structurally merged" if include_agent_config else "untouched"}; Claude agents are {"installed or preserved" if include_claude_agent_config else "untouched"}. | {req_status} | ### Explicit Non-Goals @@ -1029,7 +1115,8 @@ def _migration_plan( ### User Decisions And Answers -- Runtime agent configuration requested: {"yes" if include_agent_config else "no"}. +- Codex runtime agent configuration requested: {"yes" if include_agent_config else "no"}. +- Claude Code project agents requested: {"yes" if include_claude_agent_config else "no"}. ### Completed Baseline State @@ -1049,7 +1136,7 @@ def _migration_plan( - REQ-001: structural plan validation. - REQ-002: manifest, ownership, privacy, and protected-file checks. -- REQ-003: TOML parse and exact configuration diff when selected. +- REQ-003: TOML parse and exact configuration diff for Codex when selected; native Claude agent paths and exact-template preservation when selected. ### Latest Validation Results @@ -1210,6 +1297,7 @@ def _manifest_text( shared_paths: list[str], include_agent_config: bool, existing_manifest: str = "", + include_claude_agent_config: bool = False, ) -> str: archive_path, archive_indexes = _manifest_archive_contract(existing_manifest) applied = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") @@ -1243,6 +1331,7 @@ def _manifest_text( [ 'active_plan: "PLANS.md"', f"runtime_agent_config_managed: {'true' if include_agent_config else 'false'}", + f"runtime_claude_agent_config_managed: {'true' if include_claude_agent_config else 'false'}", "instruction_contract_version: 3", "planning_contract_version: 2", "orchestration_contract_version: 3", @@ -1256,6 +1345,7 @@ def apply_migration( target_version: str, include_agent_config: bool = False, approved_privacy_review: str | None = None, + include_claude_agent_config: bool = False, ) -> dict[str, Any]: root = repo.resolve() expected_root_identity = _directory_identity(root) @@ -1264,8 +1354,10 @@ def apply_migration( target_version, include_agent_config, approved_privacy_review, + include_claude_agent_config, ) include_agent_config = report["include_agent_config"] + include_claude_agent_config = report["include_claude_agent_config"] privacy_review, privacy_findings, approved_fingerprints = _evaluate_privacy_review( root, report["current_workflow_version"], @@ -1343,7 +1435,11 @@ def write(relative: str, text: str) -> None: (created if before is None else changed).append(relative) try: - initial_plan = _migration_plan(target_version, include_agent_config) + initial_plan = _migration_plan( + target_version, + include_agent_config, + include_claude_agent_config=include_claude_agent_config, + ) write("PLANS.md", _put_plan_first(read("PLANS.md"), initial_plan)) template_map = { @@ -1366,6 +1462,10 @@ def write(relative: str, text: str) -> None: try: for relative, data in planned_index_writes(root).items(): write(relative, data.decode("utf-8")) + archive_layout = resolve_archive_layout(root) + if archive_layout.explicit: + for relative, data in planned_explicit_index_writes(root, archive_layout).items(): + write(relative, data.decode("utf-8")) except LifecycleError as exc: raise MigrationConflict(exc.code, str(exc)) from exc @@ -1375,10 +1475,6 @@ def write(relative: str, text: str) -> None: instruction_result["status"], "Generated instruction contract did not validate", ) - index_result = check_archive_indexes(root) - if not index_result["success"]: - raise MigrationConflict("index_validation_failed", "Generated documentation indexes did not validate") - if include_agent_config: existing_config = read(".codex/config.toml") merged, config_diff = _merge_codex_config(existing_config) @@ -1393,6 +1489,16 @@ def write(relative: str, text: str) -> None: (AGENT_TEMPLATE_ROOT / f"{name}.toml.tmpl").read_text(encoding="utf-8"), ) + if include_claude_agent_config: + for name in CLAUDE_AGENT_NAMES: + relative = f".claude/agents/{name}.md" + existing_agent = read(relative) + template = (CLAUDE_AGENT_TEMPLATE_ROOT / f"{name}.md.tmpl").read_text(encoding="utf-8") + if not secure.exists(relative) or ( + _is_pristine_prior_claude_agent(name, existing_agent) and existing_agent != template + ): + write(relative, template) + shared_paths = [path for path in CANONICAL_FILES.values() if secure.exists(path)] if include_agent_config: shared_paths.extend( @@ -1402,12 +1508,19 @@ def write(relative: str, text: str) -> None: ) if secure.exists(".codex/config.toml"): shared_paths.append(".codex/config.toml") + if include_claude_agent_config: + shared_paths.extend( + f".claude/agents/{name}.md" + for name in CLAUDE_AGENT_NAMES + if secure.exists(f".claude/agents/{name}.md") + ) manifest = _manifest_text( target_version, report["protected_paths"], sorted(set(shared_paths)), include_agent_config, read(STATE_MANIFEST_PATH), + include_claude_agent_config=include_claude_agent_config, ) if scan_privacy_text(manifest): raise MigrationConflict("unsafe_manifest", "Generated manifest contains private data") @@ -1426,7 +1539,13 @@ def write(relative: str, text: str) -> None: raise MigrationConflict("protected_file_changed", "A protected file changed during migration") final_result = "Plan schema, ownership manifest, privacy, protected-file, and optional TOML checks passed." - final_plan = _migration_plan(target_version, include_agent_config, done=True, result=final_result) + final_plan = _migration_plan( + target_version, + include_agent_config, + include_claude_agent_config=include_claude_agent_config, + done=True, + result=final_result, + ) write("PLANS.md", _put_plan_first(read("PLANS.md"), final_plan)) final_plan_issues = validate_plan_schema(read("PLANS.md"), declared_external_sources=True) final_plan_issues.extend(closure_issues(read("PLANS.md"), require_ready=True)) @@ -1485,6 +1604,7 @@ def write(relative: str, text: str) -> None: failure = _migration_plan( target_version, include_agent_config, + include_claude_agent_config=include_claude_agent_config, result=f"Apply failed and non-plan files were restored: {type(exc).__name__}.", ) secure.write_text("PLANS.md", _put_plan_first(read("PLANS.md"), failure)) @@ -1547,6 +1667,7 @@ def execute_prompt_upgrade( target_version: str, include_agent_config: bool = False, approved_privacy_review: str | None = None, + include_claude_agent_config: bool = False, ) -> dict[str, Any]: """Run report-first migration for an authorized natural-language target-upgrade request.""" report = build_migration_report( @@ -1554,6 +1675,7 @@ def execute_prompt_upgrade( target_version, include_agent_config, approved_privacy_review, + include_claude_agent_config, ) include_agent_config = report["include_agent_config"] if report["required_user_questions"]: @@ -1633,6 +1755,7 @@ def execute_prompt_upgrade( target_version, include_agent_config, approved_privacy_review, + include_claude_agent_config, ) if applied.get("update_status") == "question_required": agent_action = "ask_targeted_question" @@ -1666,8 +1789,9 @@ def main() -> int: mode.add_argument("--plan", action="store_true") mode.add_argument("--apply", action="store_true") mode.add_argument("--prompt", action="store_true") - parser.add_argument("--target-version", default="0.9.9") + parser.add_argument("--target-version", default="0.9.10") parser.add_argument("--include-agent-config", action="store_true") + parser.add_argument("--include-claude-agent-config", action="store_true") parser.add_argument( "--approve-privacy-review", help="Approve only the exact value-free privacy review token returned by a prior report.", @@ -1682,6 +1806,7 @@ def main() -> int: args.target_version, args.include_agent_config, args.approve_privacy_review, + args.include_claude_agent_config, ) elif args.apply: result = apply_migration( @@ -1689,6 +1814,7 @@ def main() -> int: args.target_version, args.include_agent_config, args.approve_privacy_review, + args.include_claude_agent_config, ) else: result = build_migration_report( @@ -1696,6 +1822,7 @@ def main() -> int: args.target_version, args.include_agent_config, args.approve_privacy_review, + args.include_claude_agent_config, ) except MigrationConflict as exc: selected_mode = "prompt" if args.prompt else ("apply" if args.apply else "plan") diff --git a/plugins/engineering-workflow/skills/engineering-workflow/scripts/validate_skill_repo.py b/plugins/engineering-workflow/skills/engineering-workflow/scripts/validate_skill_repo.py index 6d4e179..853d867 100644 --- a/plugins/engineering-workflow/skills/engineering-workflow/scripts/validate_skill_repo.py +++ b/plugins/engineering-workflow/skills/engineering-workflow/scripts/validate_skill_repo.py @@ -53,6 +53,7 @@ "skill/engineering-workflow/references/planning_and_backlog.md", "skill/engineering-workflow/references/agent_orchestration.md", "skill/engineering-workflow/references/model_profiles.md", + "skill/engineering-workflow/references/claude_model_profiles.md", "skill/engineering-workflow/references/skill_update.md", "skill/engineering-workflow/references/target_workflow_upgrade.md", "skill/engineering-workflow/references/validation_safety.md", @@ -72,6 +73,9 @@ "skill/engineering-workflow/assets/agents/utility.toml.tmpl", "skill/engineering-workflow/assets/agents/explorer.toml.tmpl", "skill/engineering-workflow/assets/agents/reviewer.toml.tmpl", + "skill/engineering-workflow/assets/claude_agents/workflow-utility.md.tmpl", + "skill/engineering-workflow/assets/claude_agents/workflow-explorer.md.tmpl", + "skill/engineering-workflow/assets/claude_agents/workflow-reviewer.md.tmpl", ) FORBIDDEN_PATH_PARTS = {"__pycache__", ".pytest_cache", ".mypy_cache", ".ruff_cache"} FORBIDDEN_SUFFIXES = {".pyc", ".pyo"} @@ -104,6 +108,7 @@ "references/platform_compatibility.md", "references/agent_orchestration.md", "references/model_profiles.md", + "references/claude_model_profiles.md", "references/skill_update.md", "references/target_workflow_upgrade.md", "references/validation_safety.md", @@ -133,6 +138,7 @@ "## Deterministic Route": "skill/engineering-workflow/references/agent_orchestration.md", "## Programmatic Tool Route": "skill/engineering-workflow/references/agent_orchestration.md", "## Capability Mapping": "skill/engineering-workflow/references/model_profiles.md", + "## Claude Capability Mapping": "skill/engineering-workflow/references/claude_model_profiles.md", "## Refresh Loaded Skill Decision": "skill/engineering-workflow/references/skill_update.md", "## Installation Types": "skill/engineering-workflow/references/skill_update.md", "## Prompt Invocation": "skill/engineering-workflow/references/target_workflow_upgrade.md", @@ -532,6 +538,40 @@ def _validate_agent_profiles(repo_root: Path) -> list[str]: return issues +def _validate_claude_agent_profiles(repo_root: Path) -> list[str]: + issues: list[str] = [] + directory = repo_root / "skill/engineering-workflow/assets/claude_agents" + for name, model, effort in ( + ("workflow-utility", "haiku", None), + ("workflow-explorer", "sonnet", "medium"), + ("workflow-reviewer", "sonnet", "medium"), + ): + path = directory / f"{name}.md.tmpl" + if not path.is_file(): + continue + content = path.read_text(encoding="utf-8") + parts = content.split("---\n", 2) + if len(parts) != 3 or parts[0] or not parts[2].strip(): + issues.append(f"{path.name} needs valid frontmatter and a task prompt") + continue + fields: dict[str, str] = {} + for line in parts[1].splitlines(): + key, separator, value = line.partition(":") + if not separator or not key or key in fields: + issues.append(f"{path.name} has invalid or duplicate frontmatter") + break + fields[key] = value.strip() + else: + expected = {"name": name, "model": model, "tools": "Read, Grep, Glob"} + if any(fields.get(key) != value for key, value in expected.items()) or not fields.get("description"): + issues.append(f"{path.name} has an invalid Claude role profile") + if fields.get("effort") != effort: + issues.append(f"{path.name} has an unsupported or unexpected effort") + if set(fields) - {"name", "description", "tools", "model", "effort"}: + issues.append(f"{path.name} includes unexpected Claude agent settings") + return issues + + def _validate_programmatic_tool_assets(repo_root: Path) -> list[str]: issues: list[str] = [] template_path = repo_root / "skill/engineering-workflow/assets/templates/PROGRAMMATIC_TOOL_STAGE.md.tmpl" @@ -761,6 +801,7 @@ def validate_skill_repo(repo_root: Path) -> dict: errors.extend(_validate_source_indexes(root)) errors.extend(_validate_canonical_owners(root)) errors.extend(_validate_agent_profiles(root)) + errors.extend(_validate_claude_agent_profiles(root)) errors.extend(_validate_programmatic_tool_assets(root)) errors.extend(_validate_marketplace_package(root, version)) errors.extend(_validate_active_versions(root, version))