Summary
/cpa:new-plugin Phase 3 Step 1 relocates .cpa-workflow-artifacts from the workspace into the plugin directory. Two defects:
- The
mv fails whenever the destination already exists and is non-empty — which is the normal state for any plugin that has been worked on across more than one session.
- The
echo is not chained to the mv, so it prints "Moved .cpa-workflow-artifacts into plugin directory" unconditionally, including when nothing moved.
Net effect: the relocation silently no-ops and reports success. Artifacts then accumulate at the workspace root indefinitely.
Related: #40 (closed) covered the earlier "created one level too high" symptom in this same area. This is a different failure mode in the relocation step itself.
Current code
commands/new-plugin.md:291
if [ -d "$CPA_WORKSPACE_DIR/.cpa-workflow-artifacts" ]; then
mv "$CPA_WORKSPACE_DIR/.cpa-workflow-artifacts" "$CPA_PLUGIN_DIR/"
echo "Moved .cpa-workflow-artifacts into plugin directory"
fi
Reproduction
mkdir -p /tmp/mvtest/ws/.cpa-workflow-artifacts /tmp/mvtest/plugin/.cpa-workflow-artifacts
touch /tmp/mvtest/ws/.cpa-workflow-artifacts/spec.md
touch /tmp/mvtest/plugin/.cpa-workflow-artifacts/costs.json
mv /tmp/mvtest/ws/.cpa-workflow-artifacts /tmp/mvtest/plugin/ ; echo "exit=$?"
mv: rename /tmp/mvtest/ws/.cpa-workflow-artifacts to
/tmp/mvtest/plugin/.cpa-workflow-artifacts: Directory not empty
exit=1
mv will not merge into an existing non-empty directory. Because echo is a separate statement rather than mv ... && echo ..., the success message still prints.
Why it recurs
The CPA_RUNNING=1 SessionEnd hooks write cost and user-input logs to $CPA_WORKSPACE_DIR/.cpa-workflow-artifacts/ on every /exit. So the workspace-root directory is recreated after each session, and any plugin that already holds its own copy will collide on the next Phase 3 run. It is not a one-time cleanup.
Observed state in a workspace with one plugin and three sessions — both copies present, both committed:
canvas-plugins/.cpa-workflow-artifacts/{costs,user_inputs}/...
canvas-plugins/portal-patient-documents/.cpa-workflow-artifacts/{costs,user_inputs}/...
Impact
Low severity — nothing is lost and the phase continues. But:
- The success message is misleading when diagnosing artifact layout.
- Session exhaust accumulates at the workspace root rather than being consolidated per plugin.
user_inputs/*.json contains verbatim user prompts. If the workspace root has no .gitignore (there is no root ignore file after canvas init, which only writes one inside the plugin container), a git add -A at the root will commit them. On a public repo that publishes prompt history.
Suggested fix
Merge rather than rename, and gate the message on the actual outcome:
if [ -d "$CPA_WORKSPACE_DIR/.cpa-workflow-artifacts" ]; then
mkdir -p "$CPA_PLUGIN_DIR/.cpa-workflow-artifacts"
if cp -R "$CPA_WORKSPACE_DIR/.cpa-workflow-artifacts/." \
"$CPA_PLUGIN_DIR/.cpa-workflow-artifacts/" \
&& rm -rf "$CPA_WORKSPACE_DIR/.cpa-workflow-artifacts"; then
echo "Moved .cpa-workflow-artifacts into plugin directory"
else
echo "WARNING: could not relocate .cpa-workflow-artifacts" >&2
fi
fi
Related consideration
Worth having canvas init (Phase 2 Step 5) write a workspace-root .gitignore alongside the per-plugin one, covering the session exhaust:
**/.cpa-workflow-artifacts/costs/
**/.cpa-workflow-artifacts/user_inputs/
**/.cpa-workflow-artifacts/costs_aggregation.json
**/.cpa-workflow-artifacts/user_inputs_aggregation.json
instance-config-*.md
Note the **/ prefix is required — a pattern with an internal slash is anchored to the repo root and will not match the copy inside the plugin container. plugin-spec.md and wrap-up-report-*.md should stay tracked. instance-config-*.md is included because /cpa:analyze-instance writes its report to the cwd (skills/instance-analyze/SKILL.md:190) and that report describes a live instance's roles, teams, note types, and installed plugins.
Environment
- cpa plugin
1.29.0
- macOS (Darwin 25.5.0), BSD
mv
Summary
/cpa:new-pluginPhase 3 Step 1 relocates.cpa-workflow-artifactsfrom the workspace into the plugin directory. Two defects:mvfails whenever the destination already exists and is non-empty — which is the normal state for any plugin that has been worked on across more than one session.echois not chained to themv, so it prints "Moved .cpa-workflow-artifacts into plugin directory" unconditionally, including when nothing moved.Net effect: the relocation silently no-ops and reports success. Artifacts then accumulate at the workspace root indefinitely.
Related: #40 (closed) covered the earlier "created one level too high" symptom in this same area. This is a different failure mode in the relocation step itself.
Current code
commands/new-plugin.md:291Reproduction
mvwill not merge into an existing non-empty directory. Becauseechois a separate statement rather thanmv ... && echo ..., the success message still prints.Why it recurs
The
CPA_RUNNING=1SessionEnd hooks write cost and user-input logs to$CPA_WORKSPACE_DIR/.cpa-workflow-artifacts/on every/exit. So the workspace-root directory is recreated after each session, and any plugin that already holds its own copy will collide on the next Phase 3 run. It is not a one-time cleanup.Observed state in a workspace with one plugin and three sessions — both copies present, both committed:
Impact
Low severity — nothing is lost and the phase continues. But:
user_inputs/*.jsoncontains verbatim user prompts. If the workspace root has no.gitignore(there is no root ignore file aftercanvas init, which only writes one inside the plugin container), agit add -Aat the root will commit them. On a public repo that publishes prompt history.Suggested fix
Merge rather than rename, and gate the message on the actual outcome:
Related consideration
Worth having
canvas init(Phase 2 Step 5) write a workspace-root.gitignorealongside the per-plugin one, covering the session exhaust:Note the
**/prefix is required — a pattern with an internal slash is anchored to the repo root and will not match the copy inside the plugin container.plugin-spec.mdandwrap-up-report-*.mdshould stay tracked.instance-config-*.mdis included because/cpa:analyze-instancewrites its report to the cwd (skills/instance-analyze/SKILL.md:190) and that report describes a live instance's roles, teams, note types, and installed plugins.Environment
1.29.0mv