RFC#2843 #32: agent-skills are plugins, not asset-channel paths; record declared plugins on create_workspace #3000
Reference in New Issue
Block a user
Delete Branch "rfc2843-32-skills-plugins-not-assets"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
RFC#2843 #32 — make a fresh seo-agent auto-install seo-all post-boot. Two tightly-coupled prod fixes in one PR (they only work end-to-end together).
Summary
FIX A — agent-skills must NOT be an asset-channel path.
IsCPTemplateAssetPathstill allowedagent-skills/*, so a fresh seo-agent's ~501–716 KiB skill tree was pulled into the provision request and fail-closed BEFORE the CP was ever called (WORKSPACE_PROVISION_FAILED, reproduced twice; control template claude-code-default succeeded; prod CP logs showed zero provision requests for the failed ws). Skills are PLUGINS now (core #2995): the gitea:// resolver reads theagent-skills/<skill>subpath from the template repo at install time. The asset-channel allowlist is now config.yaml + prompts/ ONLY**;agent-skills/*is rejected at the addAsset boundary. Skill files stay in the repo (they ARE the plugin source). The seo-agent asset set drops to config.yaml (~9 KB) + prompts/seo-agent.md (~8 KB) ≈ 18 KB, far under the 256 KiB cap.FIX B — create_workspace must record declared plugins. The post-online reconcile only installs plugins with
workspace_declared_pluginsrows.recordDeclaredPluginran ONLY inorg_import.go, not in the singlecreate_workspacepath — so a singly-provisioned seo-agent got no declared rows, the reconcile no-op'd, and seo-all never installed. Newtemplate_plugins.goparses the template config.yamlplugins:block (mergePlugins-aligned dedup +!/-opt-out) and records each declared plugin inWorkspaceHandler.Create, mirroring the org_import loop. Idempotent via the ON CONFLICT upsert.SOP checklist
TestCollectCPConfigFiles_RejectsAgentSkillsAsset; updated wire-shape + large-asset tests to config.yaml/prompts only. handlers package — newtemplate_plugins_test.go:parseTemplatePluginsreads the seo-agent shape + dedup/opt-out; sqlmock-backedseedTemplatePluginsasserts theworkspace_declared_pluginsINSERT with the derived install nameseo-all.go build ./...,go vet, and both package test suites green locally (with MOLECULE_GITEA_TOKEN for token-gated tests).template-delivery-e2eworkflow is the standing regression gate (advisory, Phase 1).reference_runtime_fix_deploy_path,feedback_no_such_thing_as_flakes,feedback_comprehensive_tests_and_e2e_for_llm,feedback_no_silent_checklist_trim,feedback_no_gofmt_w_wildcard(gofmt -w only files I edited).🤖 Generated with Claude Code
qa-review: skills-as-plugins decoupling + declared-plugin recording on create. Allowlist tightened (agent-skills now rejected), DB write proven via sqlmock, asset set verified ~18KB under cap. Approving qa-review on
6f379a884f.security-review: blast-radius allowlist TIGHTENED not loosened (agent-skills/* now rejected at the addAsset boundary alongside MEMORY.md/CLAUDE.md/.claude/sessions). Hostile-template LimitReader reused for the plugins parse. No new secret/transport surface. Approving security-review on
6f379a884f./sop-ack comprehensive-testing
/sop-ack local-postgres-e2e
/sop-ack staging-smoke
/sop-ack root-cause
/sop-ack five-axis-review
/sop-ack no-backwards-compat
/sop-ack memory-consulted
New commits pushed, approval review dismissed automatically according to repository settings
New commits pushed, approval review dismissed automatically according to repository settings
qa-review re-approve on
b46e0349c2(head advanced by the e2e-workflow lint fix — colon-free job name + bp-exempt directive; both BP lints pass locally). Substance unchanged.security-review re-approve on
b46e0349c2(head advanced by the e2e-workflow lint fix only — allowlist tightening + create-path declared-plugin recording unchanged).