一键导入
hgraf-add-annotation-kind
Add a new AnnotationKind — proto, storage, ingress RPC, optional downstream delivery to agents, frontend authoring UI, tests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Add a new AnnotationKind — proto, storage, ingress RPC, optional downstream delivery to agents, frontend authoring UI, tests.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Understand and safely change heartbeat interval, stuck threshold, and timeout constants — client cadence, server detection math, frontend signal.
Add a new agent Capability — proto enum, client advertisement, server ingest, frontend gating in the control UI.
Add a new ControlKind — capability advertisement, client handler, server routing, ack semantics, frontend button, and tests — all the way through.
Add a new drift kind end-to-end. Goldfive owns the detector + enum; harmonograf reflects the kind through the intervention aggregator and the UI timeline.
Script a complex multi-turn FakeLlm scenario — function calls, tool chains, drift events, side-effect hooks, partial responses — for deterministic adk test coverage.
Add a field to a proto message end-to-end — proto edit, regen, client+server converters, frontend types, tests, and sqlite migration if persisted.
| name | hgraf-add-annotation-kind |
| description | Add a new AnnotationKind — proto, storage, ingress RPC, optional downstream delivery to agents, frontend authoring UI, tests. |
You need a new kind of user-authored note attached to a span or agent-time point. Existing kinds (types.proto:306-311):
ANNOTATION_KIND_COMMENT — UI-only, never delivered to the agent.ANNOTATION_KIND_STEERING — delivered to the agent as a CONTROL_KIND_STEER control event.ANNOTATION_KIND_HUMAN_RESPONSE — answers a WAIT_FOR_HUMAN span.Add a new kind when the delivery semantics differ from all three above.
proto/harmonograf/v1/types.proto:304-337 — Annotation, AnnotationKind, AnnotationTarget, AgentTimePoint.server/harmonograf_server/rpc/frontend.py — the PostAnnotation RPC implementation; it's where STEERING fans out into ControlRouter.deliver(...). Grep for CONTROL_KIND_STEER to find the existing pattern.proto/harmonograf/v1/types.proto:306:
enum AnnotationKind {
ANNOTATION_KIND_UNSPECIFIED = 0;
ANNOTATION_KIND_COMMENT = 1;
ANNOTATION_KIND_STEERING = 2;
ANNOTATION_KIND_HUMAN_RESPONSE = 3;
ANNOTATION_KIND_REDACT_PII = 4;
}
make proto
server/harmonograf_server/storage/base.py has a Python AnnotationKind Enum. Add the value. Then extend _ANNOT_KIND_TO_STORAGE / _STORAGE_TO_ANNOT_KIND maps in server/harmonograf_server/convert.py.
The annotations table in sqlite.py:105 stores kind TEXT NOT NULL. Existing values are written as the enum name without the prefix (e.g., "STEERING"). No schema migration needed — TEXT columns accept any value. Just make sure convert.py writes the consistent short name.
server/harmonograf_server/rpc/frontend.py :: PostAnnotation is the fan-out point. Current logic:
if kind == ANNOTATION_KIND_STEERING:
event.kind = CONTROL_KIND_STEER
event.steer.note = request.body
event.steer.author = ann.author # goldfive#171
event.steer.annotation_id = ann.id # goldfive#171
await control_router.deliver(event)
elif kind == ANNOTATION_KIND_HUMAN_RESPONSE:
event.kind = CONTROL_KIND_INJECT_MESSAGE
event.inject_message.text = request.body
await control_router.deliver(event)
Add a branch for your new kind. If the semantics map to a control event:
author and annotation_id onto the event payload so
goldfive can attribute the drift back to the source annotation and
dedupe retries (mirrors goldfive#171 for STEER).control_router.deliver(...) and set
annotation.delivered_at when the ack arrives.If semantics are UI-only, just store and broadcast — no control event.
When the annotation is stored, the PostAnnotation handler publishes a delta onto SessionBus so all WatchSession subscribers see it. Grep bus.publish in rpc/frontend.py for the existing pattern. New kinds typically inherit this path for free.
frontend/src/rpc/convert.ts maps PbAnnotationKind → UI string union. Extend it. The UI annotation type lives in frontend/src/gantt/types.ts or alongside.
frontend/src/components/shell/Drawer.tsx :: AnnotationsTab is the authoring surface. It currently exposes COMMENT / STEERING buttons via useSendControl + usePostAnnotation. Add a button and wire it to usePostAnnotation({ kind: 'REDACT_PII', ... }).
server/tests/test_rpc_frontend.py — PostAnnotation persists the new kind and fires delivery if semantics require it.server/tests/test_storage_extensive.py — round-trip through memory + sqlite stores.server/tests/test_control_e2e.py — if your kind dispatches a control event, the ack path works.frontend/src/__tests__/components/AnnotationsTab.test.tsx — button exists, calls the hook with the right kind.make proto
uv run pytest server/tests -x -q -k annotation
cd frontend && pnpm test -- --run Annotation
cd frontend && pnpm typecheck
delivered_at never set: if your kind has a control event, remember to update the annotations.delivered_at column when the ack arrives. Without it the UI won't show the delivery confirmation."COMMENT", not "ANNOTATION_KIND_COMMENT"). Maintain the shortening consistently in convert.py.bus.publish, WatchSession subscribers won't see it live — only on the next full GetSession refresh.