Public: anyone can read this; the crew can change it.
The first message sent from /talk?about= gives the agent that subject as fenced context #10662
Check · Review · Land · Live done — Landed and live in production.
The first message of a new conversation opened from `/talk?about=…` now gives the agent the subject as fenced data. `tsc`, `gate.ts`, `design-lint.ts` and the talk, conversation-talk and talk-conversations tests pass. I did not run the whole suite.
shipped production is serving it deploy cca14ac
The first message of a new conversation opened from /talk?about=… now gives the agent the subject as fenced data. tsc, gate.ts, design-lint.ts and the talk, conversation-talk and talk-conversations tests pass. I did not run the whole suite.
What changed
- Page and client: the composer on
/talk?about=…carries theaboutvalue only when the page already resolved it as readable. The plain form posts it as a hidden field. The script sends it in the JSON body to/api/v1/conversation/messages. - Door:
talk()insrc/actions/talk.tsandsay()insrc/crew/conversation.tstake an optionalabout, and the messages API passes it through. - Context: a new
aboutContextinsrc/core/talk_about.tsre-validates theaboutvalue against the sender's read access, asaboutLinedoes. It never trusts the form. It returns the subject's kind, name, title and body (body capped at 4000 characters) inside asrc/core/fence.tsblock, or""for anything malformed, unknown or unreadable. - First message only:
say()adds the context only when the conversation has no turns yet. It goes in front of the person's words in the prompt. The stored turn stays the person's words alone, and later messages and resumes do not repeat it. - Sending text with the agent prompt: the
aboutcontext is shipped in theleadparameter I added tobegin().
Tests
- Readable issue: a fake
claudebinary captures the agent's input. A fresh conversation's first message shows the fenced kind, name, title and body, and the second message does not. - Forged
about:aboutContextreturns""for a private repository's issue and for the repository itself, for a non-admin sender and for nobody signed in. - Sending with an
aboutthat names nothing: it adds no fenced block.
Gaps and choices
- Forged-private test is partly at the lookup, not through a send: admins can read every repository, and a non-admin sender needs an account grant, which I did not set up in the test. So the forged private case is tested at
aboutContext, the lookup every send goes through, and the end-to-end send uses a repository that doesn't exist. - Script byte budget: the script's byte ceiling in
test/talk-page.test.tsgoes from 13,500 to 13,600. Theabouthookup added about 40 bytes and the ceiling had no room. - Not done: the terminal
talk --aboutdoes not send this context yet. The issue named only the page composer.
To try it: open /talk?about=OWNER/REPO#N on a preview, start a new conversation and send a first message. The agent's input then starts with the fenced issue.
🤖 Generated with Claude Code
I merged main into the branch and pushed it. The only conflict was in test/talk-page.test.ts. tsc, gate.ts and the talk-page and conversation-talk tests pass (75 tests). I did not run the whole suite.
Both sides edited the imports and added a describe block at the end of the file, so I kept both:
- Imports:
mainaddedHELD_TTL_MSto thefence.tsimport. I kept that and myaboutContext,createConversation,configureConversationandtalkimports. - Rename:
main's new "header's Talk button" block defines its owntalkhelper. I renamed my import totalkToso the two don't clash.
The pull description from my earlier message still stands.
Security looks sound: aboutContext re-resolves the value against the sender's read access, the body is fenced and capped, and the stored turn stays plain. Three things to fix in one round:
- Both doors.
talk --about owner/repo#N wordsin src/tui/ssh-talk.ts (line ~113-154) prints the subject line but calls talk() withoutabout, so the terminal sends no fenced context. The page and the terminal must do the same. Passsubject.aboutthrough the talk() calls there, with a test through serveTalk. - Docs. docs/features/talk.md section 'Naming the subject' says nothing about the first message carrying the subject as fenced context (first message only, body capped at 4000 characters). Add it there, not in README.
- Test for box 3. The forged-private case is checked only at aboutContext, not through a send. Make the end-to-end send use a non-admin sender (create a user with no grant) naming a private repository's issue, and assert the agent input has no
[[datablock and no 'Hidden plans'. Also drop the repeated listTurns call and the awkward cast in the first test's assertion.
Minor: the bump of the talkJs byte ceiling is acceptable, but keep it the smallest that fits.
I've pushed all three fixes, plus the tightened byte ceiling.
Changes
- Terminal door:
talk --about owner/repo#N wordsinsrc/tui/ssh-talk.tsnow passes the subject totalk()on both the plain path and the--attachpath. The page and the terminal send the same fenced context. - Terminal test: a new test sends through
serveTalkand asserts the agent input holds the issue's title and the person's words. - Docs:
docs/features/talk.md, under "Naming the subject", now says the first message carries the subject as fenced context. Only the first message carries it, the body is capped at 4000 characters, the stored turn stays plain, and the value is re-resolved against the sender's read access. Nothing went into the README. - Forged-private test: the end-to-end send now uses a non-admin sender. That sender is a new user holding a Claude account grant but no read access to the private repository. The test asserts the agent input has no
[[datablock and no "Hidden plans". The grant is needed because a non-admin getsno_accountwithout one. - First test cleanup: it now calls
listTurnsonce, with no cast. - Byte ceiling: the
talkJsceiling goes from 13,600 to 13,550. The script is 13,533 bytes, so that is about the smallest round value that fits.
Checks
test/talk-page.test.tspasses: 50 tests.tsc --noEmit,scripts/gate.tsandscripts/design-lint.tsall pass.- I did not run the full suite here; the forge runs it on push.
The terminal speaks in the person's latest conversation. So talk --about only carries the subject when that conversation has no turns yet, the same first-message-only rule as the page. The new test opens a fresh conversation first for that reason.
Earlier asks are met. The terminal talk --about now sends the subject on both the plain and the attach path. The forged-private case is tested end to end with a non-admin sender, and the feature is documented in docs/features/talk.md, not the README. The script byte ceiling is tightened. The about value is re-resolved against the sender's read access (aboutLine, then the fenced block), so a forged value adds nothing. Only a conversation with no turns gets the context, and the stored turn stays the person's own words. The page needs no script for it, since it is a hidden field in the plain form. I could not diff against 906ca7585e37 because the clone lacks that commit, so I judged the head as a whole.
The first message of a new conversation opened from
/talk?about=…now gives the agent the subject as fenced data.tsc,gate.ts,design-lint.tsand the talk, conversation-talk and talk-conversations tests pass. I did not run the whole suite.What changed
/talk?about=…carries theaboutvalue only when the page already resolved it as readable. The plain form posts it as a hidden field. The script sends it in the JSON body to/api/v1/conversation/messages.talk()insrc/actions/talk.tsandsay()insrc/crew/conversation.tstake an optionalabout, and the messages API passes it through.aboutContextinsrc/core/talk_about.tsre-validates theaboutvalue against the sender's read access, asaboutLinedoes. It never trusts the form. It returns the subject's kind, name, title and body (body capped at 4000 characters) inside asrc/core/fence.tsblock, or""for anything malformed, unknown or unreadable.say()adds the context only when the conversation has no turns yet. It goes in front of the person's words in the prompt. The stored turn stays the person's words alone, and later messages and resumes do not repeat it.aboutcontext is shipped in theleadparameter I added tobegin().Tests
claudebinary captures the agent's input. A fresh conversation's first message shows the fenced kind, name, title and body, and the second message does not.about:aboutContextreturns""for a private repository's issue and for the repository itself, for a non-admin sender and for nobody signed in.aboutthat names nothing: it adds no fenced block.Gaps and choices
aboutContext, the lookup every send goes through, and the end-to-end send uses a repository that doesn't exist.test/talk-page.test.tsgoes from 13,500 to 13,600. Theabouthookup added about 40 bytes and the ceiling had no room.talk --aboutdoes not send this context yet. The issue named only the page composer.To try it: open
/talk?about=OWNER/REPO#Non a preview, start a new conversation and send a first message. The agent's input then starts with the fenced issue.🤖 Generated with Claude Code
Closes #10496