Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ skills:
testing:
prompt: |
Focus on behavioral regressions, missing tests, and weak assertions.
go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.
devops:
prompt: |
Focus on deployment safety, observability, and operational failure modes.
Expand Down Expand Up @@ -195,7 +200,7 @@ workspaces:
- name: coder
backend: claude
description: "Implements fixes and features; opens pull requests"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: coder
scope_type: workspace
allow_prs: true
Expand All @@ -205,7 +210,7 @@ workspaces:
- name: refactorer
backend: claude
description: "Refactors existing code for readability and maintainability"
skills: [architect, testing, dx]
skills: [architect, testing, go-api, dx]
prompt_ref: refactorer
scope_type: workspace
allow_prs: true
Expand All @@ -222,7 +227,7 @@ workspaces:
- name: pr-reviewer
backend: codex
description: "Reviews pull requests for quality, test coverage, and correctness"
skills: [architect, testing, security]
skills: [architect, testing, security, go-api]
prompt_ref: pr-reviewer
scope_type: workspace
allow_dispatch: true
Expand Down
9 changes: 7 additions & 2 deletions config_examples/autonomous-fleet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ skills:
testing:
prompt: |
Focus on behavioral regressions, missing tests, and weak assertions.
go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.
security:
prompt: |
Focus on auth, secrets handling, trust boundaries, and unsafe defaults.
Expand Down Expand Up @@ -65,7 +70,7 @@ workspaces:
- name: coder
backend: claude
description: "Implements fixes and features from ai ready issues; opens pull requests"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: coder
scope_type: workspace
allow_prs: true
Expand All @@ -75,7 +80,7 @@ workspaces:
- name: pr-reviewer
backend: codex
description: "Reviews pull requests for correctness, regressions, and missing tests"
skills: [architect, testing, security]
skills: [architect, testing, security, go-api]
prompt_ref: pr-reviewer
scope_type: workspace
allow_dispatch: true
Expand Down
9 changes: 7 additions & 2 deletions config_examples/coder-and-reviewer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ skills:
testing:
prompt: |
Focus on behavioral regressions, missing tests, and weak assertions.
go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.

prompts:
- name: coder
Expand All @@ -45,7 +50,7 @@ workspaces:
- name: coder
backend: claude
description: "Implements fixes and features; opens pull requests"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: coder
scope_type: workspace
allow_prs: true
Expand All @@ -55,7 +60,7 @@ workspaces:
- name: pr-reviewer
backend: codex
description: "Reviews pull requests for quality, test coverage, and correctness"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: pr-reviewer
scope_type: workspace
allow_dispatch: true
Expand Down
7 changes: 6 additions & 1 deletion config_examples/local-llm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ skills:
testing:
prompt: |
Focus on behavioral regressions, missing tests, and weak assertions.
go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.

prompts:
- name: refactorer
Expand All @@ -50,7 +55,7 @@ workspaces:
- name: refactorer
backend: claude_local
description: "Cleans up code style and small refactors using the local model"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: refactorer
scope_type: workspace
allow_prs: true
Expand Down
11 changes: 8 additions & 3 deletions config_examples/multi-repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ skills:
testing:
prompt: |
Focus on behavioral regressions, missing tests, and weak assertions.
go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.
dx:
prompt: |
Focus on onboarding friction, naming, docs drift, and actionable errors.
Expand All @@ -55,23 +60,23 @@ workspaces:
- name: coder
backend: claude
description: "Implements fixes and features; opens pull requests"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: coder
scope_type: workspace
allow_prs: true

- name: refactorer
backend: claude
description: "Refactors existing code for readability and maintainability"
skills: [architect, testing, dx]
skills: [architect, testing, go-api, dx]
prompt_ref: refactorer
scope_type: workspace
allow_prs: true

- name: pr-reviewer
backend: codex
description: "Reviews pull requests for correctness, regressions, and missing tests"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: pr-reviewer
scope_type: workspace
repos:
Expand Down
7 changes: 6 additions & 1 deletion config_examples/solo-coder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ skills:
testing:
prompt: |
Focus on behavioral regressions, missing tests, and weak assertions.
go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.

prompts:
- name: coder
Expand All @@ -37,7 +42,7 @@ workspaces:
- name: coder
backend: claude
description: "Implements fixes and features from labelled issues; opens pull requests"
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: coder
scope_type: workspace
allow_prs: true
Expand Down
8 changes: 7 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ skills:
security:
prompt: |
Focus on authn/authz, secrets exposure, injection vectors, and unsafe defaults.

go-api:
prompt: |
For Go HTTP APIs, route each HTTP method to a distinct handler function.
Keep handlers thin: decode and validate input, call the service layer, and encode the response.
Avoid combined method-switch handlers; method policy should stay explicit and testable at the route boundary.
```

Skills are keyed by stable public ref. For compatibility, agents may reference a visible
Expand All @@ -187,7 +193,7 @@ workspaces:
- name: coder
backend: claude
description: Implements fixes and features
skills: [architect, testing]
skills: [architect, testing, go-api]
prompt_ref: coder
scope_type: workspace
allow_prs: true
Expand Down
33 changes: 29 additions & 4 deletions docs/self-improvement-feedback.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,35 @@ pass `workspace` to narrow diagnostic views.
Single-target proposals are for simple one-asset edits. Reactive multi-asset
bundles are feedback-driven and keep coordinated changes together. Proactive
catalog audits are a separate workflow that reviews the catalog without a
specific feedback event. Assistant preference memory is also separate: it
should learn from accepted, rejected, edited, linked-existing, discarded, and
published decisions after these review surfaces exist, but it must not bypass
the recommendation, bundle, or publish gates.
specific feedback event.

Assistant preference memory is global product memory for the self-improvement
assistant. Feedback and recommendations stay workspace-scoped as evidence
records, but approved preference memory is shared across future analyses so the
same maintainer preference does not need to be relearned per workspace. It is
not agent runtime memory: it is inspectable preference guidance about how the
maintainer wants recommendations ranked and framed, not private run state loaded
into coder/reviewer agents.

Preference memory entries are managed in **Improvements → Memory**, through
`GET|POST /improvements/memory`,
`PATCH /improvements/memory/{id}`,
`POST /improvements/memory/{id}/approve`,
`POST /improvements/memory/{id}/reject`, and
`POST /improvements/memory/{id}/archive`, or through the MCP
`list_improvement_memory`, `create_improvement_memory`,
`update_improvement_memory`, `approve_improvement_memory`,
`reject_improvement_memory`, and `archive_improvement_memory` tools. Active
entries are included in future analyst inputs and may be listed on resulting
recommendations as `memory_influences`; proposed entries are visible but do
not influence analysis until approved.

The assistant may propose memory from accepted or rejected recommendations, and
proposal-bundle decisions remain available as evidence for
future memory proposals. User approval is required before inferred preference
memory becomes active. Current feedback and maintainer clarification in the
active analysis override stored memory, and memory never bypasses the
recommendation, bundle, or publish gates.

When a recommendation needs more input, the dashboard's **Clarify** action lets
an operator edit one clarification field while seeing the original feedback,
Expand Down
16 changes: 16 additions & 0 deletions internal/daemon/daemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ func TestBuildRouterRegistersExpectedRoutes(t *testing.T) {
{http.MethodPost, "/improvements/recommendations/rec_1/clarification"},
{http.MethodPost, "/improvements/recommendations/rec_1/proposal-bundle"},
{http.MethodGet, "/improvements/recommendations/rec_1/proposal-bundle"},
{http.MethodGet, "/improvements/memory"},
{http.MethodPost, "/improvements/memory"},
{http.MethodPatch, "/improvements/memory/mem_1"},
{http.MethodPost, "/improvements/memory/mem_1/approve"},
{http.MethodPost, "/improvements/memory/mem_1/reject"},
{http.MethodPost, "/improvements/memory/mem_1/archive"},
{http.MethodPatch, "/improvements/proposal-bundles/bundle_1/items/item_1"},
{http.MethodPost, "/improvements/proposal-bundles/bundle_1/items/item_1/reject"},
{http.MethodPost, "/improvements/proposal-bundles/bundle_1/items/item_1/link-existing"},
Expand All @@ -162,6 +168,16 @@ func TestBuildRouterRegistersExpectedRoutes(t *testing.T) {
}
}

func TestBuildRouterDoesNotRegisterDeleteImprovementMemoryArchive(t *testing.T) {
t.Parallel()

srv, _ := newTestServer(t, testCfg(nil))
req := httptest.NewRequest(http.MethodDelete, "/improvements/memory/mem_1/archive", nil)
if srv.Router().Match(req, &mux.RouteMatch{}) {
t.Fatal("DELETE /improvements/memory/{id}/archive is registered, want POST-only archive route")
}
}

func TestImprovementProposalBundleRESTLifecycle(t *testing.T) {
t.Parallel()
srv, _ := newTestServer(t, testCfg(nil))
Expand Down
99 changes: 99 additions & 0 deletions internal/daemon/observe/observe.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ func (h *Handler) RegisterRoutes(r *mux.Router, withTimeout func(http.Handler) h
r.Handle("/improvements/recommendations/{id}/proposal", withTimeout(http.HandlerFunc(h.HandleImprovementProposal))).Methods(http.MethodGet)
r.Handle("/improvements/recommendations/{id}/proposal-bundle", withTimeout(http.HandlerFunc(h.HandleCreateImprovementProposalBundle))).Methods(http.MethodPost)
r.Handle("/improvements/recommendations/{id}/proposal-bundle", withTimeout(http.HandlerFunc(h.HandleImprovementProposalBundle))).Methods(http.MethodGet)
r.Handle("/improvements/memory", withTimeout(http.HandlerFunc(h.HandleImprovementMemory))).Methods(http.MethodGet)
r.Handle("/improvements/memory", withTimeout(http.HandlerFunc(h.HandleCreateImprovementMemory))).Methods(http.MethodPost)
r.Handle("/improvements/memory/{id}", withTimeout(http.HandlerFunc(h.HandleUpdateImprovementMemory))).Methods(http.MethodPatch)
r.Handle("/improvements/memory/{id}/approve", withTimeout(http.HandlerFunc(h.HandleApproveImprovementMemory))).Methods(http.MethodPost)
r.Handle("/improvements/memory/{id}/reject", withTimeout(http.HandlerFunc(h.HandleRejectImprovementMemory))).Methods(http.MethodPost)
r.Handle("/improvements/memory/{id}/archive", withTimeout(http.HandlerFunc(h.HandleArchiveImprovementMemory))).Methods(http.MethodPost)
r.Handle("/improvements/proposal-bundles/{id}/items/{item_id}", withTimeout(http.HandlerFunc(h.HandleUpdateImprovementProposalBundleItem))).Methods(http.MethodPatch)
r.Handle("/improvements/proposal-bundles/{id}/items/{item_id}/reject", withTimeout(http.HandlerFunc(h.HandleRejectImprovementProposalBundleItem))).Methods(http.MethodPost)
r.Handle("/improvements/proposal-bundles/{id}/items/{item_id}/link-existing", withTimeout(http.HandlerFunc(h.HandleLinkImprovementProposalBundleItem))).Methods(http.MethodPost)
Expand Down Expand Up @@ -346,6 +352,99 @@ func (h *Handler) HandleImprovementProposalBundle(w http.ResponseWriter, r *http
_ = json.NewEncoder(w).Encode(bundle)
}

func (h *Handler) HandleImprovementMemory(w http.ResponseWriter, r *http.Request) {
rows, err := h.improve.ListMemory("", r.URL.Query().Get("status"), 200)
if err != nil {
h.writeStoreError(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(rows)
}

func (h *Handler) HandleCreateImprovementMemory(w http.ResponseWriter, r *http.Request) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/agents improve - we should never create handlers this way, we should split one per each http method. This should be added to a new skill "go-api" .

var req struct {
Key string `json:"key"`
Value string `json:"value"`
Status string `json:"status"`
EvidenceType string `json:"evidence_type"`
EvidenceID string `json:"evidence_id"`
EvidenceURL string `json:"evidence_url"`
Confidence string `json:"confidence"`
}
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("decode request: %v", err), http.StatusBadRequest)
return
}
row, err := h.improve.CreateMemory(selfimprovement.AssistantMemoryInput{
Key: req.Key,
Value: req.Value,
Status: req.Status,
EvidenceType: req.EvidenceType,
EvidenceID: req.EvidenceID,
EvidenceURL: req.EvidenceURL,
Confidence: req.Confidence,
ProposedBy: "dashboard",
})
if err != nil {
h.writeStoreError(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(row)
}

func (h *Handler) HandleUpdateImprovementMemory(w http.ResponseWriter, r *http.Request) {
var req selfimprovement.AssistantMemoryUpdate
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("decode request: %v", err), http.StatusBadRequest)
return
}
row, err := h.improve.UpdateMemory(mux.Vars(r)["id"], req)
if err != nil {
h.writeStoreError(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(row)
}

func (h *Handler) HandleApproveImprovementMemory(w http.ResponseWriter, r *http.Request) {
row, err := h.improve.ApproveMemory(mux.Vars(r)["id"])
if err != nil {
h.writeStoreError(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(row)
}

func (h *Handler) HandleRejectImprovementMemory(w http.ResponseWriter, r *http.Request) {
var req struct {
Reason string `json:"reason"`
}
if r.Body != nil {
_ = json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&req)
}
row, err := h.improve.RejectMemory(mux.Vars(r)["id"], req.Reason)
if err != nil {
h.writeStoreError(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(row)
}

func (h *Handler) HandleArchiveImprovementMemory(w http.ResponseWriter, r *http.Request) {
row, err := h.improve.ArchiveMemory(mux.Vars(r)["id"])
if err != nil {
h.writeStoreError(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(row)
}

func (h *Handler) HandleUpdateImprovementProposalBundleItem(w http.ResponseWriter, r *http.Request) {
var req selfimprovement.SelfImprovementBundleItemUpdate
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&req); err != nil {
Expand Down
Loading
Loading