Skip to content

fix: private secrets keep their spaces exactly as typed - #31722

Open
juliusmarminge wants to merge 4 commits into
mainfrom
fix/issue-31690
Open

juliusmarminge wants to merge 4 commits into
mainfrom
fix/issue-31690

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

The private secret card (shown when an agent calls request_secret) would not accept a secret made only of whitespace: with a single space typed, Save securely stayed disabled. Secrets with leading or trailing spaces were silently trimmed before they were stored. On main (938b4d4) in the web dev app, typing " " left Save disabled, and saving " pw " delivered a 2-byte file containing pw.

Change

The secret is now kept exactly as typed, and only an empty field counts as empty:

  • packages/contracts: the save answer's secret is Schema.NonEmptyString instead of TrimmedNonEmptyString, so the server no longer trims it or rejects whitespace-only values on decode.
  • packages/client-runtime: secretRequestAnswerInput no longer trims the secret.
  • Web and mobile SecretRequestCard: Save is disabled only when secret.length === 0.

Storage and file delivery already passed the value through unchanged.

Scope and approval

Fixes #31690

Verification

  • vp test run packages/client-runtime/src/secretRequest.test.ts: the new test sends values through secretRequestAnswerInput and then the wire contract decode. On main it fails with expected 'pass word' to be ' pass word '. With the fix, 4/4 tests pass, including a direct check that the contract still rejects "".
  • vp test run apps/server/src/secrets/SecretRequests.test.ts: 17/17.
  • vp run check:changed on the merged head: format, lint, knip, typecheck (36 packages), tests and react compiler all pass.

Delivered file bytes (real app, fresh databases, an agent calling request_secret with delivery: "file"; od -An -tx1 -v and wc -c on the returned file):

Client Revision Typed Bytes Size
Web 938b4d4 (before) " pw " 70 77 2
Web b075a42 (after) " " 20 1
Web b075a42 (after) " pw " 20 20 70 77 20 20 6
Android (Pixel 9) b075a42 " " 20 1
Android (Pixel 9) b075a42 " pw " 20 20 70 77 20 20 6
iOS (iPhone 17 Pro) b075a42 " " 20 1
iOS (iPhone 17 Pro) b075a42 " pw " 20 20 70 77 20 20 6

On main, " " cannot be submitted at all.

Evidence (Chrome, 1280×900 CSS px, DPR 2, dark; Pixel 9 and iPhone 17 Pro from the device pool, dark, large text). The images are attached below:

  • Web, single space: before (938b4d4) Save disabled, after (b075a42) Save enabled. Zero-threshold and 5% fuzz diffs are attached. Inside the card, only the Save button differs. Outside it, the generated thread title, the sidebar history, the elapsed time and the live "Asking" label highlight differ between runs.
  • Web, empty field: Save disabled on both sides.
  • Web, padded value: six masked characters, saved; Saved row shown.
  • Android and iOS (after only): an empty field keeps Save disabled; a single space typed with the native Space key enables Save; the padded value is submitted with native Done; Saved rows shown.

Not checked: interaction recordings were made but could not be published, so clear/retype and Enter/Done ordering rest on the stills and byte table above. Only the web single-space pair has diff images, and those diffs also include the unrelated run differences listed above. There is no unpadded pw control capture. Mobile was captured only after the fix; the mobile change is the same one-line predicate as web. A newer client talking to an older server still has the old trimming on the server side.

AI-assisted; verified with the tests and tools listed above.

🤖 Generated with Claude Code

Before (938b4d4525): one space typed, Save securely disabled

After (b075a426f2): one space typed, Save securely enabled

Diff, zero threshold amplified (single space pair)

Diff, 5% fuzz (single space pair)

Before: empty field, Save disabled

After: empty field, Save still disabled

Before: padded value typed

After: padded value typed

Before: padded value Saved row (file held 2 bytes)

After: single space Saved row

After: padded value Saved row (file held 6 bytes)

Android after: empty field, Save disabled

Android after: one space, Save enabled

Android after: single space Saved

iOS after: empty field, Save disabled

iOS after: one space, Save enabled

iOS after: single space Saved

juliusmarminge and others added 4 commits November 5, 2026 21:31
These tests reproduce #31690: a saved secret should be
delivered exactly as typed. Today the client trims it (and refuses a
whitespace-only value) and the wire contract trims it again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The secret card trimmed the value in the client and again in the wire
contract, so leading/trailing whitespace was lost and a whitespace-only
secret could not be saved. Only an empty value now counts as empty:
the contract uses Schema.NonEmptyString, the client stops trimming, and
the web and mobile "Save securely" buttons enable for any non-empty value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ty secret

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Nov 5, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 10c4b78

Macroscope's review found this PR approvable — Secret submissions now retain whitespace exactly as entered across web, mobile, and the shared input contract. Existing private storage and delivery behavior remain unchanged, and empty values are still rejected.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ The exact PR base did not have a successful artifact. Baseline uses the latest successful main measurement shown below.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Baseline: 4bfb896 · PR result: 10c4b78 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:needs-julius Needs Julius: route gate, unverifiable surface, or blocked labels Nov 5, 2026
@coderabbitai

coderabbitai Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The secret-answer schema and client now preserve non-empty secret values without trimming. The web and mobile secret cards allow whitespace-only values to be saved. Tests cover whitespace preservation and empty-input handling.

Changes: Secret submission

Layer / File(s) Summary
Secret answer contract and payload
packages/contracts/src/secretRequest.ts, packages/client-runtime/src/secretRequest.ts, packages/client-runtime/src/secretRequest.test.ts
The save-answer schema now accepts non-empty strings. The answer helper preserves the supplied secret, and tests cover whitespace preservation and empty-input handling.
Web and mobile save controls
apps/web/src/components/chat/SecretRequestCard.tsx, apps/mobile/src/features/threads/SecretRequestCard.tsx
Both cards enable the Save button for any non-empty secret, including whitespace-only values.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related PRs

  • pingdotgg/t3code#31257: Changes trimming in secretRequestAnswerInput, which this PR reverses for single-secret answers.
  • pingdotgg/t3code#31670: Changes how the mobile Save button uses input drafts; this PR changes when whitespace-only values enable saving.

Suggested labels: size:S

Suggested reviewers: t3dotgg

Merge Risk: Low · up to 10c4b

Whitespace-only secrets require a server update; retrying against an older server will not work.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title clearly describes the change to preserve spaces in private secrets.
Description check Passed The description is mostly complete. It explains the problem, changes, verification results, and limitations. It links an issue, but does not confirm its triage status. It names the agent harness but n…
Linked Issues check Passed [#31690] The web card enables Save for nonempty values and submits the input value. The client helper and wire contract preserve whitespace and reject empty secrets. Automated tests cover padded, whit…
Out of Scope Changes check Passed [#31690] The contract, client helper, web card, and tests support exact secret submission. The mobile card applies the same rule to the shared secret flow.
Full details: Description check

Explanation

The description is mostly complete. It explains the problem, changes, verification results, and limitations. It links an issue, but does not confirm its triage status. It names the agent harness but not the model. Mobile before screenshots and interaction recordings are missing.

Full details: Linked Issues check

Explanation

[#31690] The web card enables Save for nonempty values and submits the input value. The client helper and wire contract preserve whitespace and reject empty secrets. Automated tests cover padded, whitespace-only, and empty values. The PR reports unchanged file bytes for submitted whitespace and padded secrets.

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 4a94658 and 10c4b78.

Files selected for processing: 5

  • apps/mobile/src/features/threads/SecretRequestCard.tsx
  • apps/web/src/components/chat/SecretRequestCard.tsx
  • packages/client-runtime/src/secretRequest.test.ts
  • packages/client-runtime/src/secretRequest.ts
  • packages/contracts/src/secretRequest.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:needs-julius Needs Julius: route gate, unverifiable surface, or blocked size:S 10-29 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Secret card can't save a whitespace-only secret (e.g. a password that is a single space)

1 participant