diff --git a/content/pages/en/use-cases.ts b/content/pages/en/use-cases.ts index c40fd42..572bb6a 100644 --- a/content/pages/en/use-cases.ts +++ b/content/pages/en/use-cases.ts @@ -3,6 +3,9 @@ import type { MarketingPage } from '../../../src/content-pages/types'; // One card per existing scenario post, grouped by business team (ruling on // #359, 2026-11-04). Each card summarizes its post and claims nothing the post // does not say; the post, template and guide links are checked at build time. +// `readiness` (#370) is taken from evidence, not optimism: `today` only where +// the linked template or post shows the scenario running on the shipped +// platform; in doubt, the more conservative tier. zh-Hans carries the same tiers. const page = { slug: 'use-cases', navLabel: 'Use cases', @@ -44,6 +47,7 @@ const page = { body: 'Reps fill in CRM fields after the selling is done, while the real context sits in meetings, emails, notes and follow-up promises.', scenario: { post: 'ai-sales-assistant', + readiness: 'integration', guide: 'crm-case-management-ai', withAi: 'Reps update by voice or text. AI summarizes meetings, picks out next steps, surfaces deal risk, suggests stage changes and preps the next account meeting.', approves: 'The rep confirms each suggested CRM change, and customer-facing commitments need confirmation. Managers set the risk rules and approval boundaries.', @@ -62,6 +66,7 @@ const page = { body: 'A long email or a frustrated chat becomes a record that waits for someone to read, classify, search, reply and escalate.', scenario: { post: 'ai-ticket-hub', + readiness: 'integration', template: 'helpdesk', withAi: 'AI classifies each issue, detects urgency and sentiment, matches knowledge articles, drafts replies with citations and ranks cases by SLA.', approves: 'Agents confirm drafts, routing and case updates. Refund, compensation and contract-promise replies can be set to wait for a manager’s approval; escalations are created only after permission checks.', @@ -80,6 +85,7 @@ const page = { body: 'OCR fills in the fields, but finance still judges by hand whether a claim is in policy, in budget, project-related and not a duplicate.', scenario: { post: 'ai-expense-audit', + readiness: 'integration', withAi: 'AI extracts receipt data, checks policy, budget, project and duplicate invoices, ranks the claims to review first and cites the policy line behind each finding.', approves: 'AI recommends; payment approval follows a clear authority chain, high-risk claims route to finance review, and reviewer corrections are recorded.', }, @@ -89,6 +95,7 @@ const page = { body: 'Supplier risk is spread across systems: expiring certificates, slipping delivery, unusual pricing, contract gaps and quality returns.', scenario: { post: 'ai-procurement-risk', + readiness: 'integration', template: 'procurement', withAi: 'Before an order is placed, AI checks qualifications and delivery, compares the quote with history, surfaces quality and contract issues and suggests alternatives.', approves: 'AI does not make the buying decision: procurement approves with the risk explanation in front of it, and review outcomes feed back into supplier governance.', @@ -107,6 +114,7 @@ const page = { body: 'Employees need a VPN, a laptop, leave or a policy answer, and the portal makes them find the right HR, IT, admin or finance form first.', scenario: { post: 'ai-employee-service-center', + readiness: 'rule', template: 'hr', withAi: 'AI splits one message into the right service requests, pre-fills the employee’s context, answers policy questions from the knowledge base and routes fulfilment tasks.', approves: 'Requests are created only through a governed service catalog; access, devices and reimbursements stay behind permissions and approvals, tracked in the same conversation.', @@ -125,6 +133,7 @@ const page = { body: 'The contract register is managed but the review is not: risk stays in attachments and comments, and approvers see too little context.', scenario: { post: 'ai-contract-risk', + readiness: 'rule', template: 'contracts', withAi: 'AI extracts parties, amounts, terms and non-standard clauses, compares them with the standard template and turns deviations into risk records.', approves: 'Legal confirms or edits every finding and owns the final opinion. High-risk findings go to approval, and signed obligations become follow-up tasks.', @@ -135,6 +144,7 @@ const page = { body: 'Compliance teams align policy text with evidence scattered across IAM, contracts, procurement, logs, workflows and spreadsheets — by hand.', scenario: { post: 'ai-compliance-control', + readiness: 'integration', template: 'compliance', withAi: 'AI parses policies into controls, matches evidence, explains why a gap is high risk and suggests remediation tasks.', approves: 'Responsible owners confirm high-risk findings and evidence requirements; remediation and its confirmation stay in the audit history.', @@ -153,6 +163,7 @@ const page = { body: 'Risk hides in meeting notes, task updates, weekly reports, change requests and resource conversations until a milestone slips.', scenario: { post: 'ai-project-risk-assistant', + readiness: 'rule', template: 'project', withAi: 'AI links notes to tasks and milestones, detects blockers, dependency and resource conflicts, creates risk records with suggested actions and answers health questions.', approves: 'AI detects and explains; commitments stay human decisions. Managers turn accepted risks into action plans; a rule can hold scope changes until their impact is assessed.', @@ -171,6 +182,7 @@ const page = { body: 'AI may speed up one draft, but ideas, sources, review comments, the publishing calendar and retrospectives stay scattered.', scenario: { post: 'ai-content-workbench', + readiness: 'integration', template: 'content', withAi: 'AI clusters source material into topics, writes briefs with audience and evidence, highlights claims that lack support and summarizes performance after publishing.', approves: 'Drafts route to brand, product, legal or customer-success review before publishing, and review comments and approvals are structured records.', @@ -181,6 +193,7 @@ const page = { body: 'An assistant answers the question, then the user still opens the business system to create tasks, start approvals, update records and leave an audit trail.', scenario: { post: 'ai-agent-workbench', + readiness: 'rule', withAi: 'The agent turns a goal into a visible plan and calls governed tools: it queries records, creates tasks, starts approvals and generates reports.', approves: 'It checks the user’s access before querying data, asks before creating tasks, gates high-risk actions with approval and records each tool call and result.', }, diff --git a/content/pages/zh-Hans/use-cases.ts b/content/pages/zh-Hans/use-cases.ts index 9d3704c..ab8753a 100644 --- a/content/pages/zh-Hans/use-cases.ts +++ b/content/pages/zh-Hans/use-cases.ts @@ -2,6 +2,8 @@ import type { MarketingPage } from '../../../src/content-pages/types'; // 每张卡对应一篇已有的场景文章,按业务团队分组(#359 裁决,2026-11-04)。 // 卡片只概括文章已写的内容;文章、模板与专题链接在构建时校验。 +// `readiness`(#370)按证据定档:只有链接的模板或文章演示了在已发布平台上运行, +// 才标 `today`;拿不准时取更保守的一档。与英文版逐卡同档。 const page = { slug: 'use-cases', navLabel: '使用场景', @@ -43,6 +45,7 @@ const page = { body: '销售在事情办完之后才去填 CRM 字段,真正的上下文留在会议、邮件、笔记和跟进承诺里。', scenario: { post: 'ai-sales-assistant', + readiness: 'integration', guide: 'crm-case-management-ai', withAi: '销售用语音或文字更新进展。AI 整理会议纪要、识别下一步动作、提示商机风险、建议阶段变更,并为下一次客户会议做准备。', approves: '每条 CRM 变更建议由销售本人确认,面向客户的承诺需要确认。风险规则和审批边界由主管设定。', @@ -61,6 +64,7 @@ const page = { body: '一封长邮件或一段不满的对话变成一条记录,然后等人去读、分类、查资料、回复和升级。', scenario: { post: 'ai-ticket-hub', + readiness: 'integration', template: 'helpdesk', withAi: 'AI 识别问题类型、紧急程度和客户情绪,匹配知识库,起草带引用的回复,并按 SLA 给工单排序。', approves: '回复草稿、分派和工单更新由客服确认。退款、补偿和合同承诺类回复可设为须主管审批后才能发出;升级任务只在权限校验通过后创建。', @@ -79,6 +83,7 @@ const page = { body: 'OCR 能把字段填上,但报销是否合规、是否在预算内、是否与项目相关、有没有重复,财务仍要人工判断。', scenario: { post: 'ai-expense-audit', + readiness: 'integration', withAi: 'AI 提取票据信息,核对政策、预算、项目和重复发票,排出今天优先要审的报销单,并给出每条发现依据的政策条款。', approves: 'AI 只给建议;付款审批走清晰的授权链,高风险报销转财务复核,复核人的修正会被记录。', }, @@ -88,6 +93,7 @@ const page = { body: '供应商风险分散在多个系统里:资质快到期、交付变慢、报价异常、合同缺口、质量退货。', scenario: { post: 'ai-procurement-risk', + readiness: 'integration', template: 'procurement', withAi: '下单之前,AI 核对资质和交付表现,把报价与历史对比,列出质量和合同问题,并建议替代方案。', approves: 'AI 不替你做采购决定:采购在看到风险说明之后再审批,复核结果回流到供应商管理。', @@ -106,6 +112,7 @@ const page = { body: '员工要开 VPN、领电脑、请假或问一条政策,门户却要他们先在人事、IT、行政、财务里找到对的表单。', scenario: { post: 'ai-employee-service-center', + readiness: 'rule', template: 'hr', withAi: 'AI 把一句话拆成对应的服务请求,预填员工信息,用知识库回答政策问题,并把履约任务分派给对的团队。', approves: '请求只能经由受治理的服务目录创建;访问权限、设备和报销都在权限与审批之后,进度在同一段对话里跟踪。', @@ -124,6 +131,7 @@ const page = { body: '合同台账有人管,审查却没有:风险留在附件和批注里,审批人看到的上下文太少。', scenario: { post: 'ai-contract-risk', + readiness: 'rule', template: 'contracts', withAi: 'AI 提取签约方、金额、期限和非标准条款,与标准模板对比,把偏离变成风险记录。', approves: '每条发现由法务确认或修改,最终意见归法务。高风险发现进入审批,已签署的义务变成跟进任务。', @@ -134,6 +142,7 @@ const page = { body: '合规团队要手工把制度条文和散落在 IAM、合同、采购、日志、流程系统和表格里的证据对上。', scenario: { post: 'ai-compliance-control', + readiness: 'integration', template: 'compliance', withAi: 'AI 把制度拆成控制项,匹配证据,解释为什么某个缺口是高风险,并建议整改任务。', approves: '高风险发现和证据要求由责任人确认;整改及其确认都留在审计历史里。', @@ -152,6 +161,7 @@ const page = { body: '风险藏在会议纪要、任务更新、周报、变更请求和资源沟通里,直到里程碑滑期才被看见。', scenario: { post: 'ai-project-risk-assistant', + readiness: 'rule', template: 'project', withAi: 'AI 把纪要关联到任务和里程碑,识别阻塞、依赖和资源冲突,创建带建议动作的风险记录,并回答项目健康度问题。', approves: 'AI 负责发现和解释;承诺仍由人决定。经理把接受的风险转成行动计划;可以设规则让范围变更先做影响评估。', @@ -170,6 +180,7 @@ const page = { body: 'AI 也许能让一篇草稿写得更快,但选题、素材、评审意见、发布排期和复盘仍然散落各处。', scenario: { post: 'ai-content-workbench', + readiness: 'integration', template: 'content', withAi: 'AI 把素材聚成选题,写出带受众和论据的简报,标出缺少支撑的说法,并在发布后总结表现。', approves: '草稿发布前流转到品牌、产品、法务或客户成功评审;评审意见和审批都是结构化记录。', @@ -180,6 +191,7 @@ const page = { body: '助手回答了问题,用户却还得打开业务系统去建任务、发起审批、更新记录、留下审计痕迹。', scenario: { post: 'ai-agent-workbench', + readiness: 'rule', withAi: 'Agent 把目标变成一份看得见的执行计划,再调用受治理的工具:查询记录、创建任务、发起审批、生成报告。', approves: '查询数据前先校验用户权限,创建任务前先请求确认,高风险动作经审批,每次工具调用及其结果都被记录。', }, diff --git a/src/components/MarketingEvidencePage.astro b/src/components/MarketingEvidencePage.astro index 64e3023..03448b2 100644 --- a/src/components/MarketingEvidencePage.astro +++ b/src/components/MarketingEvidencePage.astro @@ -4,7 +4,7 @@ import heroImage from '../assets/objectos-home-hero.png'; import { getMarketingPage, getMarketingPages, marketingPagePath } from '../content-pages/registry'; import { clusterBySlug, clusterPath } from '../lib/clusters'; import { clusterCopy } from '../lib/cluster-i18n'; -import type { MarketingPage } from '../content-pages/types'; +import { SCENARIO_READINESS, type MarketingPage } from '../content-pages/types'; import { marketingPageUi } from '../content-pages/ui'; import { cardSpans } from '../lib/card-rows'; import { phraseHtml } from '../../scripts/lib/cjk-phrases.mjs'; @@ -67,8 +67,16 @@ for (const section of page.sections) { if (scenario.guide && !clusterBySlug(scenario.guide)) { throw new Error(`Scenario card ${where}: no guide (cluster) "${scenario.guide}"`); } + // Every card states how far it is from running (#370): exactly one of the + // closed set, so a scenario cannot ship without a stated tier. + if (!(SCENARIO_READINESS as readonly string[]).includes(scenario.readiness)) { + throw new Error( + `Scenario card ${where}: readiness "${scenario.readiness ?? ''}" is not one of ${SCENARIO_READINESS.join(' | ')}` + ); + } } } +const hasScenarios = page.sections.some((section) => section.items.some((item) => item.scenario)); const guideLink = (slug: string) => { const cluster = clusterBySlug(slug)!; return { href: clusterPath(locale, slug), label: clusterCopy(cluster, locale).shortTitle }; @@ -138,6 +146,15 @@ const ph = (text: string): string => phraseHtml(text, locale); ))} + {hasScenarios && ( +

+ {ui.scenario.readinessLegend} + {SCENARIO_READINESS.map((tier) => ( + {ui.scenario.readiness[tier]} + ))} +

+ )} +

{page.artifact.eyebrow}

@@ -166,6 +183,9 @@ const ph = (text: string): string => phraseHtml(text, locale); {section.items.map((item, index) => item.scenario ? (

{item.title}

+

+ {ui.scenario.readiness[item.scenario.readiness]} +

{ui.scenario.today}
@@ -613,6 +633,47 @@ const ph = (text: string): string => phraseHtml(text, locale); flex-direction: column; min-height: 0; } + /* Readiness (#370): a plain chip with a status dot, on each scenario card + and in the legend under the metrics. The label carries the meaning; the + dot only helps the eye match a card to the legend: green and amber are + status colours, the rule tier a hollow ring (indigo stays interaction). */ + .metrics:has(+ .readiness-legend) { + margin-bottom: 1.25rem; + } + .readiness-legend { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem 0.75rem; + margin: 0 0 5rem; + color: var(--ink-soft); + font-size: var(--fs-14); + line-height: var(--lh-ui); + } + .readiness-chip { + white-space: normal; + } + .readiness-chip::before { + content: ''; + flex: none; + width: 0.5rem; + height: 0.5rem; + border-radius: var(--r-pill); + background: var(--muted); + } + .readiness-today::before { + background: var(--green); + } + .readiness-integration::before { + background: var(--amber); + } + .readiness-rule::before { + background: transparent; + box-shadow: inset 0 0 0 2px var(--muted); + } + .scenario .scenario-readiness { + margin: 0 0 0.9rem; + } .scenario dl { display: grid; gap: 0.7rem; @@ -876,6 +937,9 @@ const ph = (text: string): string => phraseHtml(text, locale); .metrics { margin-bottom: 3rem; } + .readiness-legend { + margin-bottom: 3rem; + } .metrics div { min-height: auto; } diff --git a/src/content-pages/types.ts b/src/content-pages/types.ts index 7852c51..5f24a5f 100644 --- a/src/content-pages/types.ts +++ b/src/content-pages/types.ts @@ -8,6 +8,20 @@ export interface MarketingPageAction { href: string; } +/** + * How far a /use-cases/ scenario is from running (#370), a closed set: + * - `today`: it works on the shipped platform, shown by its linked template + * or post; + * - `integration`: it needs a connection to a system the reader runs (e-mail, + * chat, OCR, ERP, quality, IAM, analytics…); + * - `rule`: the business rule it applies has to be written down first. + * The tier comes from evidence, never optimism: in doubt, the more + * conservative one. `MarketingEvidencePage.astro` fails `astro build` on a + * scenario whose value is missing or not in this list. + */ +export const SCENARIO_READINESS = ['today', 'integration', 'rule'] as const; +export type ScenarioReadiness = (typeof SCENARIO_READINESS)[number]; + /** * A scenario card on /use-cases/: the item's `body` is the "today" line, and * these two lines finish the shape "today → with AI → what a person approves, @@ -23,6 +37,8 @@ export interface MarketingPageScenario { template?: string; /** Cluster slug of a pillar guide that goes deeper, where one exists. */ guide?: string; + /** How far the scenario is from running; one of `SCENARIO_READINESS`. */ + readiness: ScenarioReadiness; withAi: string; approves: string; } diff --git a/src/content-pages/ui.ts b/src/content-pages/ui.ts index 71ab203..afe21c0 100644 --- a/src/content-pages/ui.ts +++ b/src/content-pages/ui.ts @@ -1,4 +1,5 @@ import type { Locale } from '../lib/i18n'; +import type { ScenarioReadiness } from './types'; import { s2t } from '../lib/zhconvert'; interface MarketingPageUi { @@ -23,6 +24,9 @@ interface MarketingPageUi { readPost: string; template: string; guide: string; + /** The readiness tag on each card and the legend's lead-in (#370). */ + readinessLegend: string; + readiness: Record; }; } @@ -59,6 +63,12 @@ const EN_UI: MarketingPageUi = { readPost: 'Read the scenario', template: 'Template', guide: 'Guide', + readinessLegend: 'What each scenario needs before it runs:', + readiness: { + today: 'Works on the platform today', + integration: 'Needs a connection to a system you run', + rule: 'Needs the business rule written down first', + }, }, }; @@ -95,6 +105,12 @@ const ZH_HANS_UI: MarketingPageUi = { readPost: '阅读完整场景', template: '模板', guide: '专题', + readinessLegend: '每个场景跑起来之前需要什么:', + readiness: { + today: '平台即可', + integration: '需要接现有系统', + rule: '需要先写清制度', + }, }, }; @@ -120,6 +136,12 @@ const toHant = (s: MarketingPageUi): MarketingPageUi => ({ readPost: s2t(s.scenario.readPost), template: s2t(s.scenario.template), guide: s2t(s.scenario.guide), + readinessLegend: s2t(s.scenario.readinessLegend), + readiness: { + today: s2t(s.scenario.readiness.today), + integration: s2t(s.scenario.readiness.integration), + rule: s2t(s.scenario.readiness.rule), + }, }, });