The worklog element, with #370's notice lines as its fallback #820
No reviewers
Labels
No labels
move-to-repo
coilyco-bridge-deploy
move-to-repo
coilyco-flight-deck-agent-compose
move-to-repo
coilyco-gaming-eco-app
move-to-repo
coilysiren-inbox
move-to-repo
unknown
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
c#
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-gaming/sirens-echo!820
Loading…
Reference in a new issue
No description provided.
Delete branch "aos/claude/yt58-worklog-embed"
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?
Closes #111. Direction A, both lanes, plus Kai's 16:23 amendment making the notice lines the fallback.
What renders
Resolved rows read as working; a lone spinner reads as possibly hung. That distinction is the whole point, since #137 and #190 were both a live service looking dead.
The two surfaces
EMBED_LINKSdecides. Granted gives the embed, absent gives #370's stacked notice lines unchanged, and #370's surface stays maintained rather than superseded.Read, not assumed. A channel that reads as unknown counts as granted, because a direct message always reads that way. A wrong yes costs one refused call and then degrades; a wrong no would strand the richer surface for a whole turn with nothing to correct it.
A refusal degrades and never fails. Discord answering
50013routes the same turn to the notice lines and latches, so a channel without the grant pays one rejected call rather than one per beat. Both branches are tested, and per the acceptance note the absent-permission branch is the one that will never be exercised in the Sirens guild, so it has the test that matters.Why the notice alphabet gained the underscore
The decision is that the code span applies inside the embed, and Kai's own example of it was
> \calling forgejo list_issue`.noticeAllowedwas[a-z0-9 ,./-], solist_issuesanitized tolist issue` — a name nobody can look up, in a surface whose entire payload is tool names.So the widening is implementing the decision rather than relaxing it. It is also narrow: the alphabet's job is that a phrase cannot close the code span early, inject markdown that renders, or span two lines. An underscore does none of those from inside a code span, and the backtick is still stripped.
TestHarnessNoticeSanitizesThePhrasegains a row pinningeco.get_marketintact, and its markdown row now expects_failed_to survive, which is the honest statement of the new rule.Glyphs and an honest gap
🔨 ✅ 📭 ❌, the reaction vocabulary, via
toolOutcomeGlyphrather than a second copy.tooldisclosure.goalready said the vocabulary was "shared with the reaction set and the progress element", so this is the third rendering that comment anticipated.An unresolved row on a stopped element keeps 🔨. That is deliberate: 🔨 means tool call invoked in the approved set, which is exactly what is known. Resolving it to ❌ would claim a failure the harness cannot support, since the call may have succeeded and the turn died after.
Terminal states
Delivered answer deletes the element. The reply's disclosure footer already names the tools, and two lists of them is what #385 avoided. This satisfies "progress and the footer never coexist".
Every other ending resolves to
Did not finishand stays. An element that merely vanishes mid-narration is the #137 silence in a costume.The block rule is structural, not a wording convention. Every stop gets the same title, and nothing in
progressViewtakes a category or a reason, so there is no field through which a classifier could leak. A block is not tellable from a timeout.Two contract rows I did not implement, and why
"Same message: embed cleared, content set." Not done. The element is deleted on success rather than becoming the answer. Making it the answer means routing delivery through the progress message, which bypasses the overflow-attachment path (#791, merged three hours ago) and the thread routing. That is its own change with its own risk, and deleting still satisfies the rule that motivated it. Say the word and it is a follow-up.
"Appearance threshold ~2.5s." Left at 5s.
turnProgressAfteris an operator knob (SIRENS_ECHO_PROGRESS_AFTER) withturnProgressEveryandturnLongReplyAfterderived from it, so halving it also halves the edit beat and the thread-creation threshold. The contract asked for the appearance threshold, not those two. It is one line whenever you want it, but it is a tuning decision with side effects the design did not weigh.Verified
Eleven new tests in
worklog_test.go: rows resolve in place, each outcome has its own glyph and no two share one, the cap renders a count for what it dropped, every line matchesnoticeShapeincluding a capped list, a tool name survives the alphabet, a channel without the permission gets notice lines and not silence, a refused embed falls back, a refusal latches for the turn, a stopped turn resolves the element, a delivered answer deletes it, and nothing in the stopped rendering narrates the stop.ward exec gategreen. The full existing suite passes unchanged apart from the one notice row above.Not verified, and it cannot be from here
The embed against real Discord. Nothing in this repository has ever constructed one, so the payload shape is asserted against
discordgotypes rather than against Discord. The first long turn in the Sirens guild is the real check, and the fallback is what makes a wrong guess a degradation rather than silence.