Skip to content

Commit 233cbcc

Browse files
committed
chore: close public release review gaps
1 parent 1a62172 commit 233cbcc

8 files changed

Lines changed: 44 additions & 50 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ jobs:
3333
with:
3434
fetch-depth: 0
3535
persist-credentials: false
36+
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
37+
with:
38+
node-version: 24
39+
- name: Scan private identifiers across complete main history
40+
if: github.event_name == 'push'
41+
env:
42+
PUBLIC_HISTORY_DENYLIST: ${{ secrets.MORROW_PRIVATE_DENYLIST }}
43+
run: npm run scan:history
3644
- name: Install pinned Gitleaks
3745
env:
3846
GITLEAKS_URL: https://github.com/gitleaks/gitleaks/releases/download/v8.24.3/gitleaks_8.24.3_linux_x64.tar.gz

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The module—not the agent or transport adapter—owns lifecycle policy, authori
2424

2525
- Case queues, search, assignment, priority, lifecycle, public replies, and private notes
2626
- Public knowledge, request intake, private case links, attachments, and bounded attachment evidence
27+
- Browser text help, optional voice input, and optional read-only Shopify order lookup
2728
- Channel-neutral conversations with explicit routing to support, sales, both, or no work
2829
- Signed WhatsApp text ingress and conversation-owned replies
2930
- Canonical parties, CRM relationships, sourced activities, and scheduled follow-ups
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { build } from 'esbuild'
2+
import { writeFile } from 'node:fs/promises'
3+
import { dirname, resolve } from 'node:path'
4+
import { fileURLToPath } from 'node:url'
5+
6+
const deskRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
7+
8+
export async function buildBrowserAsset(entry, outfile) {
9+
const result = await build({
10+
entryPoints: [resolve(deskRoot, entry)],
11+
bundle: true,
12+
format: 'iife',
13+
platform: 'browser',
14+
target: ['es2022'],
15+
minify: true,
16+
legalComments: 'none',
17+
sourcemap: false,
18+
outfile: resolve(deskRoot, outfile),
19+
write: false,
20+
})
21+
22+
for (const output of result.outputFiles) {
23+
await writeFile(output.path, output.text.replace(/[\t ]+$/gm, ''), 'utf8')
24+
}
25+
}
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,3 @@
1-
import { build } from 'esbuild'
2-
import { writeFile } from 'node:fs/promises'
3-
import { dirname, resolve } from 'node:path'
4-
import { fileURLToPath } from 'node:url'
1+
import { buildBrowserAsset } from './build-browser-asset.mjs'
52

6-
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
7-
8-
const result = await build({
9-
entryPoints: [resolve(repositoryRoot, 'src/portal/search-client.ts')],
10-
bundle: true,
11-
format: 'iife',
12-
platform: 'browser',
13-
target: ['es2022'],
14-
minify: true,
15-
legalComments: 'none',
16-
sourcemap: false,
17-
outfile: resolve(repositoryRoot, 'public/portal-search.js'),
18-
write: false,
19-
})
20-
21-
for (const output of result.outputFiles) {
22-
await writeFile(output.path, output.text.replace(/[\t ]+$/gm, ''), 'utf8')
23-
}
3+
await buildBrowserAsset('src/portal/search-client.ts', 'public/portal-search.js')
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,3 @@
1-
import { build } from 'esbuild'
2-
import { writeFile } from 'node:fs/promises'
3-
import { dirname, resolve } from 'node:path'
4-
import { fileURLToPath } from 'node:url'
1+
import { buildBrowserAsset } from './build-browser-asset.mjs'
52

6-
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
7-
8-
const result = await build({
9-
entryPoints: [resolve(repositoryRoot, 'src/voice/demo-client.ts')],
10-
bundle: true,
11-
format: 'iife',
12-
platform: 'browser',
13-
target: ['es2022'],
14-
minify: true,
15-
legalComments: 'none',
16-
sourcemap: false,
17-
outfile: resolve(repositoryRoot, 'public/voice-demo.js'),
18-
write: false,
19-
})
20-
21-
for (const output of result.outputFiles) {
22-
await writeFile(output.path, output.text.replace(/[\t ]+$/gm, ''), 'utf8')
23-
}
3+
await buildBrowserAsset('src/voice/demo-client.ts', 'public/voice-demo.js')

apps/desk/test/voice-demo.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const BRANDING = voiceBranding({
1616
displayName: 'Example Company',
1717
logoUrl: 'https://cdn.example.test/logo.png',
1818
faviconUrl: '/favicon.png',
19-
homeUrl: 'https://fix.example.test/',
19+
homeUrl: 'https://company.example.test/',
2020
})
2121

2222
describe('voice demo boundary', () => {
@@ -166,7 +166,7 @@ describe('voice demo boundary', () => {
166166

167167
expect(html).toContain('<title>Example Company support assistant</title>')
168168
expect(html).toContain('aria-label="Example Company home"')
169-
expect(html).toContain('href="https://fix.example.test/"')
169+
expect(html).toContain('href="https://company.example.test/"')
170170
expect(html).toContain('<img class="brand-logo" src="https://cdn.example.test/logo.png" alt="">')
171171
expect(html).toContain('<link rel="icon" href="/favicon.png">')
172172
expect(html).toContain('/workspace-theme.css')

docs/product-v1.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ This document is the implementation contract for the first public-ready Morrow D
44

55
## Product boundary
66

7-
V1 includes case queues and search, complete case workspaces, manual case opening, public replies, private notes, status, priority, category, assignment, customer corrections, knowledge articles, attachments, magic-link customer threads, inbound email, agent-customizable rich outbound notifications, manually recorded phone cases, signed WhatsApp text conversations with explicit Desk/CRM routing and conversation-owned replies, immutable audit evidence, operational diagnostics, canonical Directory adoption, first-class CRM sales leads, CRM relationship state, sourced activities, scheduled follow-ups, cross-module customer context, operation closure contracts, and inactive improvement proposals with independent evaluation records.
7+
V1 includes case queues and search, complete case workspaces, manual case opening, public replies, private notes, status, priority, category, assignment, customer corrections, knowledge articles, attachments, magic-link customer threads, inbound email, agent-customizable rich outbound notifications, browser text and optional voice assistance, optional read-only Shopify order lookup, manually recorded phone cases, signed WhatsApp text conversations with explicit Desk/CRM routing and conversation-owned replies, immutable audit evidence, operational diagnostics, canonical Directory adoption, first-class CRM sales leads, CRM relationship state, sourced activities, scheduled follow-ups, cross-module customer context, operation closure contracts, and inactive improvement proposals with independent evaluation records.
88

9-
V1 excludes customer passwords, community features, live chat, non-WhatsApp social channels, WhatsApp media/templates/status receipts, opportunity/deal pipelines, configurable CRM objects, sales forecasting, store integrations, macros, general workflow automation, SLA timers, analytics, CSAT, custom fields, an integration marketplace, autonomous candidate activation, and multi-tenant SaaS.
9+
V1 excludes customer passwords, community features, live human chat, telephone-network calling, non-WhatsApp social channels, WhatsApp media/templates/status receipts, opportunity/deal pipelines, configurable CRM objects, sales forecasting, write-capable store integrations, macros, general workflow automation, SLA timers, analytics, CSAT, custom fields, an integration marketplace, autonomous candidate activation, and multi-tenant SaaS.
1010

1111
## Required interfaces
1212

@@ -79,7 +79,7 @@ The public support homepage is an agent conversation that grounds ordinary quest
7979
- Tool success, provider acceptance, delivery, and business outcome remain distinct states.
8080
- Runtime evidence may motivate a proposal but cannot activate a prompt, playbook, policy, tool, ontology, model, or code version.
8181

82-
The domain vocabulary is fixed to `open | waiting_on_customer | on_hold | resolved | closed`, `low | normal | high | urgent`, `portal | email | manual | whatsapp`, and `queued | accepted | blocked | failed | indeterminate`. A Conversation is channel evidence; a Case is support work; a Sales Lead is qualification work.
82+
The domain vocabulary is fixed to `open | waiting_on_customer | on_hold | resolved | closed`, `low | normal | high | urgent`, `portal | email | manual | whatsapp | voice`, and `queued | accepted | blocked | failed | indeterminate`. A Conversation is channel evidence; a Case is support work; a Sales Lead is qualification work.
8383

8484
## Architecture
8585

scripts/scan-public-readiness.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ for (const filename of await walk(root)) {
6565
if (lowered.includes(token.toLowerCase())) failures.push(`${rel}: contains a deployment-specific identity token`)
6666
}
6767
for (const email of personalEmails(text)) failures.push(`${rel}: contains non-example email ${email}`)
68-
if (/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/i.test(text) && rel === 'wrangler.jsonc') {
68+
if (/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/i.test(text) && /^wrangler(?:\.[^.]+)*\.jsonc$/.test(path.basename(filename))) {
6969
failures.push(`${rel}: contains a concrete resource identifier`)
7070
}
7171
if (/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/.test(text)) failures.push(`${rel}: contains a private key`)

0 commit comments

Comments
 (0)