- Shipped
- September 2, 2026 at 8:47 PM UTC
- Author
- Kamo
- Commit
- d5f09fd
Two CRM settings tabs behind MANAGE_SALES_SCRIPTS — the live scripts and the archive — plus a Sales Scripts tab in the Interaction Center behind VIEW_SALES_SCRIPTS, and a pin that keeps the Interaction Center open. THE READER IS A TELEPROMPTER, NOT A DOCUMENT. The only question that matters while somebody is talking to a customer is "where am I", so a progress spine runs down the block stack and fills as parts are ticked, the next unread part is lifted to full contrast while read ones recede, and ticking one scrolls the next into place. A script genuinely is a sequence read top to bottom once, which is what earns the sequential treatment. Everything else on the surface is kept quiet so that one thing can carry it. The composer mirrors the same spine, so an author is looking at the shape the agent will see rather than at a form that produces it. Read-progress and the pin both live in sessionStorage: they survive a reload in the tab the member is working in, do not follow them into a second tab handling a different call, and are gone at sign-out — app/logout already does a wholesale sessionStorage.clear(), so neither needed unwinding. Every accessor is wrapped, because sessionStorage THROWS rather than returning null in a hardened profile, and this code runs inside app-wide chrome. NavTop's opener gate widens from chat access alone to chat OR sales scripts. It had to: a CRM agent on an org without the Chat app could not reach the Interaction Center at all, so the Sales Scripts tab would have been invisible to exactly the people it is for. Each tab inside still carries its own gate. The composer takes its "nothing changed yet" baseline one macrotask after loading rather than from the values it seeded. Lexical re-serializes a document when it loads it — equivalent JSON, rarely identical — so comparing against the server's string reported every freshly opened script as edited and prompted "leave without saving?" on the way out of one nobody had touched. Blocks with neither a heading nor any text are dropped on save. An author who added one and moved on did not mean to publish an empty step, and the reader would render a checkbox against nothing. The two new landing cards join hasActiveMarkets and canManageSalesScripts to the card memo's dependencies. Both settle asynchronously, so without them the memo was built while both were false and never rebuilt. The four market-gated CRM cards above were already relying on some other dependency changing in time; naming hasActiveMarkets fixes them in passing.