fix(signal): preserve newlines in debounced messages (#110090)

* fix(signal): use newlines in debounced messages

* docs: fix release gate command formatting
This commit is contained in:
Peter Steinberger
2026-07-17 23:35:05 +01:00
committed by GitHub
parent c2a069086e
commit e805dbb615
3 changed files with 15 additions and 9 deletions

View File

@@ -255,11 +255,16 @@ restart` from the real environment and verify the plist. Product follow-up:
- **Agent landing needs exact-head hosted CI.** The heavy `CI` workflow may
not queue on pushes under org load; the maintainer fallback is a
release-gate dispatch on the PR branch:
`gh workflow run ci.yml --ref <branch> -f target_ref=<head-sha>
-f release_gate=true -f pull_request_number=<pr>` (the run must be on the
```bash
gh workflow run ci.yml --ref <branch> -f target_ref=<head-sha> -f release_gate=true -f pull_request_number=<pr>
```
The run must be on the
branch ref so `head_sha` matches, and the title becomes
`CI release gate <sha>`, which `scripts/verify-pr-hosted-gates.mjs`
accepts). Then `scripts/pr` prepare/merge as usual.
accepts. Then `scripts/pr` prepare/merge as usual.
- **Gates that CI enforces beyond focused tests**: docs map
(`pnpm docs:map:gen` after adding any docs page), oxlint (`no-map-spread`,
`max-lines` — split files, never suppress), `check:test-types`, knip

View File

@@ -336,7 +336,7 @@ describe("signal createSignalEventHandler inbound context", () => {
).resolves.toEqual({ author: "+15550002222", body: "edited hello" });
});
it("preserves the last debounced message body for native reply quote metadata", async () => {
it("joins debounced message bodies with newlines and preserves the last for replies", async () => {
vi.useFakeTimers();
const deliverRepliesMock = vi.fn().mockResolvedValue(undefined);
dispatchInboundMessageMock.mockImplementationOnce(async (params: any) => {
@@ -386,7 +386,8 @@ describe("signal createSignalEventHandler inbound context", () => {
expect(deliverRepliesMock).toHaveBeenCalledTimes(1);
});
const context = requireCapturedContext();
expect(context.BodyForAgent).toBe("first debounced message\\nsecond debounced message");
expect(context.BodyForAgent).toBe("first debounced message\nsecond debounced message");
expect(context.CommandBody).toBe("first debounced message\nsecond debounced message");
expect(context.ReplyToId).toBe("1700000000002");
expect(context.ReplyThreading).toEqual({ implicitCurrentMessage: "allow" });
expect(deliverRepliesMock.mock.calls[0]?.[0]).toMatchObject({
@@ -2070,8 +2071,8 @@ describe("signal createSignalEventHandler inbound context", () => {
const context = requireCapturedContext();
expect(context.BodyForAgent).toContain("[signal attachment unavailable]");
expect(context.RawBody).toBe("first request\\nsecond request");
expect(context.CommandBody).toBe("first request\\nsecond request");
expect(context.RawBody).toBe("first request\nsecond request");
expect(context.CommandBody).toBe("first request\nsecond request");
} finally {
vi.useRealTimers();
}

View File

@@ -731,11 +731,11 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
const combinedText = entries
.map((entry) => entry.bodyText)
.filter(Boolean)
.join("\\n");
.join("\n");
const combinedCommandBody = entries
.map((entry) => entry.commandBody)
.filter(Boolean)
.join("\\n");
.join("\n");
if (!combinedText.trim()) {
await settle();
return;