@@ -14,7 +14,9 @@ your task needs it. If you are instead **submitting data to the running service*
1414| ---| ---|
1515| Wire a skill/producer to submit feedback | [ docs/agent-usage.md] ( docs/agent-usage.md ) — self-contained API contract, curl recipes |
1616| Change service code | This file (rules below), then [ docs/architecture.md] ( docs/architecture.md ) for the layer model |
17- | Change the API surface | Rules below — three artifacts must move together |
17+ | Change the API surface | Rules below — four artifacts must move together |
18+ | Change the companion skill (client scripts) | ` skills/agent-feedback/ ` — this repo is the canonical source; run ` tests/skill/run-tests.sh ` |
19+ | Install/adopt the companion skill | ` skills/agent-feedback/SKILL.md ` — Installation section |
1820| Understand naming/error/test conventions | [ docs/conventions.md] ( docs/conventions.md ) |
1921| See how the service was scaffolded | [ docs/adding-a-service.md] ( docs/adding-a-service.md ) , [ docs/patterns.md] ( docs/patterns.md ) |
2022| Run / verify / deploy | Commands + Verification below; deploy runbook in [ README.md] ( README.md ) ("Deploy to production") |
@@ -26,11 +28,15 @@ backend/ Go module root (single go.mod)
2628 pkg/ shared infra (postgres pool, envutil, httputil, observability)
2729 services/feedback/
2830 cmd/feedback/main.go wiring: config, DI, chi router, graceful shutdown
29- core/ business logic: validation, idempotent create, list/get
31+ core/ business logic: validation, content hashing/dedupe, create, list/get, processed
3032 handler/ HTTP: auth middleware, DTOs, sentinel-error → status mapping
3133 storage/postgres/ q.sql + migrations/ (inputs) → sqlc/ (generated, never edit)
3234infra/agent-feedback/ docker-compose stack (postgres + service)
3335scripts/e2e.sh live-contract test suite (all endpoints)
36+ skills/agent-feedback/ CANONICAL companion skill (SKILL.md + client scripts ONLY —
37+ distributed as-is; sync/distribution handled outside this repo)
38+ tests/skill/ hermetic tests for the skill scripts (repo-only; must NEVER
39+ travel with the skill directory)
3440docs/agent-usage.md THE API contract (producers integrate against this)
3541```
3642
@@ -44,6 +50,7 @@ just test # unit tests (integration tests skip without TEST_POSTGRES_
4450just run-local # needs .env (cp .env.example .env)
4551just sqlc-generate # regenerate storage/postgres/sqlc/ after editing q.sql or migrations
4652bash scripts/e2e.sh < API_KEY> [BASE_URL] # from repo root, against a running stack
53+ bash tests/skill/run-tests.sh # hermetic client-script tests (mock server)
4754```
4855
4956Integration tests need a real Postgres:
@@ -64,14 +71,20 @@ docker rm -f pg-test
6471 ` just sqlc-generate ` . Never hand-edit generated files.
6572- ** Migrations are append-only** now that the service is deployed. New numbered
6673 file per change; never rewrite an existing one.
67- - ** An API change is a three-artifact change** : handler/core code,
68- [ docs/agent-usage.md] ( docs/agent-usage.md ) , and ` scripts/e2e.sh ` move in the
69- same commit. The doc is a load-bearing contract — producer agents build their
70- recipes from it without reading the code.
71- - ** Write-once semantics are deliberate.** No update endpoints. Reviews dedupe on
72- ` (submission_type, run_id) ` — replay returns the existing row with 200. The
73- skill name ` friction ` is reserved. Don't add PATCH; a correction is a new
74- submission.
74+ - ** An API change is a four-artifact change** : handler/core code,
75+ [ docs/agent-usage.md] ( docs/agent-usage.md ) , ` scripts/e2e.sh ` , and the client
76+ scripts in ` skills/agent-feedback/ ` move in the same commit. The doc is a
77+ load-bearing contract — producer agents build their recipes from it without
78+ reading the code.
79+ - ** Write-once payloads, one mutable flag.** Submission content is never
80+ updated; ` processed_at ` (set/cleared via ` POST /api/v1/submissions/processed ` )
81+ is the only mutable state. Reviews dedupe on ` (submission_type, run_id) ` :
82+ identical replay → 200 existing row; different content under the same key →
83+ 409 ` replay_mismatch ` (hash comparison via ` payload_hash ` ) — a correction is a
84+ new submission under a new run_id. Frictions dedupe on content hash within a
85+ 24 h window (` core.frictionDedupeWindow ` ) → duplicate returns 200 + existing
86+ row; the auto-collected ` context ` object is EXCLUDED from that hash (it varies
87+ between attempts of the same friction). The skill name ` friction ` is reserved.
7588- ** Identity PK is a deliberate exception** to the template's UUID convention
7689 (see note in [ docs/conventions.md] ( docs/conventions.md ) ).
7790- Create endpoints are strict: unknown JSON fields → 400, bodies over 10 MiB → 413.
@@ -98,3 +111,6 @@ docker rm -f pg-test
981113 . If you touched the API surface: ` docker compose up -d ` in ` infra/agent-feedback/ `
99112 (needs ` .env ` , see ` .env.example ` ), then ` bash scripts/e2e.sh <key> ` — all
100113 checks green — then ` docker compose down -v ` to remove test data.
114+ 4 . If you touched ` skills/agent-feedback/ ` : ` bash tests/skill/run-tests.sh `
115+ (hermetic — needs python3, no running stack). Keep the skill directory free
116+ of tests/tooling — it is distributed verbatim.
0 commit comments