Skip to content

feat(drafts): Rail-2 — Aye converses in the germination rail - #228

Merged
prellr merged 4 commits into
mainfrom
feat/drafts-aye-rail
Jul 1, 2026
Merged

prellr merged 4 commits into
mainfrom
feat/drafts-aye-rail

Conversation

@prellr

@prellr prellr commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Rail-2 of the Drafts germination rail. Gives the lead agent Aye the ability to converse in the draft-level conversation rail shipped in Rail-1, routes her turn's general talk there (reserving annotations for anchored structure), and updates her identity to the finalized "soul" plus the rail capability. Backend/daemon only — no TypeScript touched.

Built on Rail-1's draft_message table + GET/POST /api/drafts/{id}/messages (already agent-attribution-ready via resolveDraftAuthor).

What's in it

1. CLI — Aye reads + posts to the rail (server/cmd/multica/cmd_draft.go)

  • multica draft say <id> --body "…" → POST /api/drafts/{id}/messages (mirrors runDraftReply; rejects empty --body; agent attribution automatic via X-Agent-ID/X-Task-ID).
  • multica draft messages <id> → GET /api/drafts/{id}/messages (mirrors runDraftAnnotations; table + json).
  • Both registered in the draft group + help.

2. Turn prompt routing (server/internal/daemon/prompt.go, buildDraftTurnPrompt)

  • Reads the rail at turn start; routes her general response to the rail via say; reserves reply/annotate for anchored points.
  • Principle: "Talk lands in the rail; structure lands in the doc (annotations/suggestions)." Existing no-body-edit / no-issue-creation guards unchanged.

3. Identity (server/internal/handler/aye.go)

  • ayeInstructions (Layer 1) → the approved soul (Disposition incl. system-steward, Inquisitiveness, memory loop incl. working memory, Orchestration).
  • ayeSkillContent (Layer 2) → conversation-rail commands + the "talk in the rail, structure in the doc" rule; rest of the skill unchanged.

4. Backfill updates existing Aye (server/internal/handler/aye_backfill.go)

  • BackfillAyeAgents now also refreshes an already-seeded Aye's instructions + attached skill content when they drift from the constants (so the new soul + rail-aware skill reach already-seeded prod agents), in addition to seeding where missing.
  • New sqlc queries UpdateAgentInstructions (by agent id + workspace id) and UpdateAgentSkillContent (by agent id + skill name via the agent_skill join), both :execrows and gated on a real diff so a matching Aye is a no-op.
  • Keyed strictly on AyeAgentID → a user-created agent is never touched. Best-effort + logged; never blocks startup.

Verification

  • make sqlc — clean, no drift.
  • go build ./..., go vet ./... — clean.
  • go test ./internal/handler -run 'Aye|Draft' ./internal/daemon — green (incl. the new backfill-update test and the updated draft-turn prompt test).
  • Built multica draft --help lists say + messages; both --help render; empty say --body is rejected.

Deploy note

CLI (say/messages) + the turn prompt live in the daemon binary → daemon rebuild. The identity constants + backfill run in the backend → backend rebuild.

Does NOT touch feat/drafts-3b-spike. Do not merge — for review.

🤖 Generated with Claude Code

prellr and others added 4 commits June 30, 2026 23:00
Give Aye read/write access to the draft-level conversation rail shipped in
Rail-1:

- `multica draft say <id> --body "…"` posts a rail message
  (POST /api/drafts/{id}/messages), mirroring runDraftReply; agent
  attribution is automatic via the trusted X-Agent-ID/X-Task-ID headers.
  Rejects an empty --body.
- `multica draft messages <id>` lists the rail (GET .../messages),
  mirroring runDraftAnnotations, with a table view.

Both are registered in the draft command group + help. The rail is where
general talk lands; annotations stay for anchored structure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ail-2)

Extend buildDraftTurnPrompt so Aye:
- reads the rail at turn start (`multica draft messages <id>`) as part of
  understanding what the human wants;
- routes her GENERAL response — narration, alignment, direction questions,
  acknowledgements — to the rail via `multica draft say`;
- reserves annotations (reply/annotate) for points anchored to exact text.

States the principle plainly: "Talk lands in the rail; structure lands in
the doc." The existing no-body-edit / no-issue-creation guards are unchanged.
Updates the prompt test to assert the rail commands + routing language.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ty (Rail-2)

- ayeInstructions (Layer 1): replace with the approved soul (Disposition
  incl. system-steward, Inquisitiveness, the memory loop incl. working
  memory, Orchestration). Opener still names her; meta title/blockquote
  stripped. This is her core cross-surface identity.
- ayeSkillContent (Layer 2, Drafts surface): add the conversation rail to
  "what you can do here" — the `multica draft say`/`messages` commands and
  the "talk in the rail, structure in the doc" rule (rail = germination
  conversation; annotations = anchored points; suggestions = concrete text
  proposals). The contract, turn/heartbeat, graduation, sub-agent cadence,
  and not-yet-active co-editing note are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Rail-2)

BackfillAyeAgents previously only SEEDED Aye where she was missing. Extend
it so on boot it also UPDATES an already-seeded Aye's instructions (the new
soul) and her attached Drafts-surface skill's content (rail-aware) when they
differ from the current constants — so the identity refresh reaches already
seeded prod agents, not just new workspaces.

- Add sqlc queries UpdateAgentInstructions (by agent id + workspace id) and
  UpdateAgentSkillContent (by agent id + skill name via the agent_skill
  join); both :execrows and gated on a real diff (col <> $n) so a matching
  Aye is a no-op. `make sqlc` regenerated, no drift.
- refreshAyeIdentity keys strictly on AyeAgentID (and the skill by name via
  the join), so a user-created agent is never touched. Best-effort + logged,
  like the existing backfill; never blocks startup.
- Test: a stale Aye is refreshed to the constants; a matching Aye is a
  no-op; a user agent's instructions + skill are left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@prellr
prellr merged commit 7a0166c into main Jul 1, 2026
2 checks passed
@prellr
prellr deleted the feat/drafts-aye-rail branch July 1, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant