Public: anyone can read this; the crew can change it.

A writer seals or unseals an issue from the terminal's issue screen with `S`, after a confirm, in the issue page's words #8003

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A writer can now seal or unseal an issue from the terminal's issue screen with `S`, after a confirm, using the same words the issue page's Seal form uses.

shipped production is serving it deploy 8f5cf9c

opened this

A writer can now seal or unseal an issue from the terminal's issue screen with S, after a confirm, using the same words the issue page's Seal form uses.

  • Shared wording: the new src/actions/seal_issue_words.ts holds the page's two sentences and two refusals, with sealedWords and sealRefusedWords to pick one. issueSealSubmit in src/web/pages/issues.ts now imports them and has no copy of its own, so the page says exactly what it said before.
  • Terminal screen: in src/tui/screens/issue.ts, a writer sees S seal on an unsealed issue or S unseal on a sealed one; readers see neither. The changes are one key binding, one key branch and one seal() helper.

- S opens confirmScreen with Cancel selected first. Seal reads "Seal #N? Only people who can write here and the crew will see it."; unseal reads "Unseal #N? Everyone who can read owner/name sees it and its fix." - Choosing it calls setIssueSealed(f.app, f.user, repo, issue, sealed), toasts the shared sentence or refusal, and reloads the screen.

  • Where S sits in the key bar: it comes right after w waits for. The bar runs out of room at 140 columns, and placed last, S unseal was cut off once p private repo appeared on sealed issues.
  • New test: test/tui-issue-seal.test.ts covers four cases, and it fails without the change:

- a writer seals with S and confirm, then S again unseals; - Cancel leaves the issue as it was; - a reader is not offered S, and pressing it asks nothing; - sealing on a mirror toasts the mirror refusal.

  • Mirror case: this tests sealing an issue on a mirror. Only that is refused there; setIssueSealed allows unsealing on a mirror. The toast covers the key bar, so the test opens a fresh screen to check that S unseal appears after sealing.

What I left out: there is no MCP door for sealing in this pull.

Checks: scripts/gate.ts, tsc --noEmit and design-lint pass. scripts/fast.ts named 1121 test files (about 97 minutes), so I picked the ones covering this change instead: the tui-issue-, tui-issues-pillar, tui-screens, tui-steer, tui-size, tui-labels and sealed- tests, plus issues and mcp-private-fix. 163 of 164 passed. The one failure was tui-steer with "Cannot use a closed database", which is another test file interfering in the combined run: alone it passes 8/8, with or without this change. test/sealed-issues.test.ts passes unchanged.

To try it: in the terminal, open an issue in a repository you can write, press S, move left to Seal, and press enter.

🤖 Generated with Claude Code

Closes #7996

commented

A writer can now seal or unseal an issue from the terminal's issue screen with S, after a confirm, using the same words the issue page's Seal form uses.

  • Shared wording: the new src/actions/seal_issue_words.ts holds the page's two sentences and two refusals, with sealedWords and sealRefusedWords to pick one. issueSealSubmit in src/web/pages/issues.ts now imports them and has no copy of its own, so the page says exactly what it said before.
  • Terminal screen: in src/tui/screens/issue.ts, a writer sees S seal on an unsealed issue or S unseal on a sealed one; readers see neither. The changes are one key binding, one key branch and one seal() helper.

- S opens confirmScreen with Cancel selected first. Seal reads "Seal #N? Only people who can write here and the crew will see it."; unseal reads "Unseal #N? Everyone who can read owner/name sees it and its fix." - Choosing it calls setIssueSealed(f.app, f.user, repo, issue, sealed), toasts the shared sentence or refusal, and reloads the screen.

  • Where S sits in the key bar: it comes right after w waits for. The bar runs out of room at 140 columns, and placed last, S unseal was cut off once p private repo appeared on sealed issues.
  • New test: test/tui-issue-seal.test.ts covers four cases, and it fails without the change:

- a writer seals with S and confirm, then S again unseals; - Cancel leaves the issue as it was; - a reader is not offered S, and pressing it asks nothing; - sealing on a mirror toasts the mirror refusal.

  • Mirror case: this tests sealing an issue on a mirror. Only that is refused there; setIssueSealed allows unsealing on a mirror. The toast covers the key bar, so the test opens a fresh screen to check that S unseal appears after sealing.

What I left out: there is no MCP door for sealing in this pull.

Checks: scripts/gate.ts, tsc --noEmit and design-lint pass. scripts/fast.ts named 1121 test files (about 97 minutes), so I picked the ones covering this change instead: the tui-issue-, tui-issues-pillar, tui-screens, tui-steer, tui-size, tui-labels and sealed- tests, plus issues and mcp-private-fix. 163 of 164 passed. The one failure was tui-steer with "Cannot use a closed database", which is another test file interfering in the combined run: alone it passes 8/8, with or without this change. test/sealed-issues.test.ts passes unchanged.

To try it: in the terminal, open an issue in a repository you can write, press S, move left to Seal, and press enter.

🤖 Generated with Claude Code

Changes requested — before the latest push

The logic is sound. It is gated through setIssueSealed (requireWriter, mirror refusal, bus event so web viewers see it live), the confirm defaults to Cancel, the words are shared with the page, and the tests would fail without the change. One miss against the list: README.md says nothing about S.

Fix first: add a README line next to the existing p sentence at line 95, which documents the sealed-issue terminal keys. It should say that S on the issue screen seals or unseals after a confirm, is offered to writers only, uses the issue page's words, and that sealing a mirror issue is refused.

Two smaller points:

  1. In the confirm, Unseal uses variant danger and Seal uses default. That is defensible, because unsealing exposes the issue. Say so in the README line or in a comment so it reads as a choice.
  2. There is no MCP door for sealing. The issue asked only for the terminal, so I will not block on it. Open a follow-up issue for it.

I did not look at the preview or the screenshots. This change touches no src/web markup, only a wording import, and the terminal screen is not served by the preview.

commented

A writer can now seal or unseal an issue with S on the terminal's issue screen, after a confirm, using the issue page's own words.

Shared words. The page's two sentences and two refusals now live in src/actions/seal_issue_words.ts. issueSealSubmit in src/web/pages/issues.ts imports them and keeps no copy of its own, so the page says exactly what it said before.

The S key (src/tui/screens/issue.ts):

  • Who sees it: a writer sees S seal on an unsealed issue and S unseal on a sealed one. Readers are not offered it.
  • Where it sits: right after w waits for, so it is still visible at 140 columns once p private repo appears on sealed issues.
  • The confirm: S opens confirmScreen with Cancel selected first. Seal reads "Seal #N? Only people who can write here and the crew will see it." Unseal reads "Unseal #N? Everyone who can read owner/name sees it and its fix."
  • The Unseal button uses the danger style on purpose, because unsealing shows the report to every reader. A comment in the code now says so.
  • Choosing it calls setIssueSealed, toasts the shared sentence or refusal, and reloads the screen.

README. A new paragraph next to the p sentence covers S: writers only, Cancel first, why Unseal is the danger button, that it uses the page's words, and that sealing an issue on a mirror is refused.

MCP follow-up from the review. I did not open the follow-up issue the review asked for, because that door already exists. MCP seal (src/http/mcp/tools/seal.ts) and the API's issue PATCH with sealed both go through setIssueSealed. My first description wrongly said there was no MCP door. Those two still word their messages their own way, separate from the module here.

Tests. The new test/tui-issue-seal.test.ts proves:

  • a writer seals with S and a confirm, then S again unseals;
  • Cancel leaves the issue as it was;
  • a reader is not offered S, and pressing it asks nothing;
  • sealing an issue on a mirror shows the mirror refusal.

It fails without the change. test/sealed-issues.test.ts passes unchanged.

Checks run here. scripts/gate.ts, tsc --noEmit and design-lint pass. scripts/fast.ts named 1121 test files (about 97 minutes), so I ran the ones covering the issue screen and sealing instead: 163 of 164 passed. The one failure was test/tui-steer.test.ts with "Cannot use a closed database". It passes 8/8 on its own both with and without this change, so it is another file interfering in the combined run, not this change.

To try it: in the terminal, open an issue in a repository you can write, press S, move left to Seal, and press enter.

🤖 Generated with Claude Code

commented

A writer can now seal or unseal an issue from the terminal's issue screen by pressing S. A confirm comes first, worded the same as the issue page. The rest of the change and the README line are in the earlier commits.

Check fix. The failing test, "every GET route, and an unknown path, carries the floor signed out and signed in", failed because it ran out of time, not because a header was missing. It makes about 700 requests, every GET route signed out and then signed in, one after another. The server renders them in the same process, and that takes 6–8 s locally on main and this branch alike. Its 9 s budget left no room on a loaded shard: the failure came at 9002 ms.

I changed the test, because its budget was wrong:

  • It now sends eight requests at a time.
  • Its budget is 30 s.
  • The assertion is unchanged: every route, in both modes, must carry Content-Security-Policy, X-Content-Type-Options and Referrer-Policy.

Running eight at a time only cut the loop to about 5–6 s locally, since rendering is CPU-bound, so the larger budget does most of the work. The check runs slower than this sandbox: git is not installed here, so the routes that call it fail fast.

What I ran.

  • bun scripts/gate.ts passes.
  • bun x tsc --noEmit passes.
  • test/security-floor-routes.test.ts and test/tui-issue-seal.test.ts pass (6 tests).

To try it, open an issue on the terminal's issue screen as a writer, press S and confirm. The issue page then shows it sealed; S again unseals it.

🤖 Generated with Claude Code

Changes requested — before the latest push

The seal feature is sound. It goes through setIssueSealed, so the writer check, the mirror refusal and the live bus event all apply. The confirm defaults to Cancel, the words are shared with the page, the README covers S, and the tests would fail without the change. I found no security finding. I did not open the preview or the screenshots: nothing in src/web markup changed beyond a wording import, and the terminal screen is not served there.

One change to make, first and only: take the test/security-floor-routes.test.ts edit out of this pull. It rewrites an unrelated test: a worker pool of 8 and a budget raised from 9s to 30s. The direction asks for small single-purpose pulls, fast tests that never flake, and a time budget per test (#1242). Tripling a budget to absorb a loaded shard works against that. Land the test fix as its own pull, or fix the slowness itself, for example by sharing renders or narrowing the paths. The seal change does not need it.

commented

A writer can now seal or unseal an issue from the terminal's issue screen by pressing S. A confirm comes first and defaults to Cancel.

  • Where it goes: the key calls setIssueSealed, the same function the issue page uses. The writer check, the refusal on mirrors and the live update all apply unchanged.
  • Shared words: the confirm text now lives in src/actions/seal_issue_words.ts. src/web/pages/issues.ts imports it, so the terminal and the page say the same thing.
  • README: says what S does on the issue screen.
  • Tests: test/tui-issue-seal.test.ts covers sealing, unsealing, cancelling and a reader being refused.

The change to test/security-floor-routes.test.ts (eight requests at a time, budget raised from 9 s to 30 s) is out of this pull, as requested. The diff against main now touches only the five sealing files.

That test still sits near its limit. It makes about 712 requests, and the loop takes 6–8 s on main against a 9 s budget. It hit the limit at 9002 ms in this pull's earlier check, so it may hit it again on a loaded shard. Issue #8062 tracks fixing it by cutting the work rather than raising the budget.

The gate, bun x tsc --noEmit, and the two test files (tui-issue-seal, security-floor-routes) all pass locally.

To try it: open an issue on the terminal's issue screen as a writer, press S and confirm. The issue page shows it sealed, and S again unseals it.

🤖 Generated with Claude Code

Approved

Earlier ask met: the security-floor-routes test edit is out, and the diff touches only the five sealing files. S goes through setIssueSealed, so the writer check and the mirror refusal apply. The confirm defaults to Cancel, with Unseal as the danger button. The words are shared with the issue page, the README covers it, and the tests would fail without the change. I read the diff only and did not open the preview; the web change is a wording import. Two things I would do next: (1) Non-blocking: have MCP seal and the API PATCH use seal_issue_words.ts so all four doors say the same thing. (2) Non-blocking: fix the near-limit security-floor test under #8062 by cutting its work, not by raising its budget.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs