release: freeze multi-scene RC-04 candidate
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Contract OpenAPI
|
||||
|
||||
This package is the source of truth for the V2 contract platform HTTP contract.
|
||||
This package publishes the V2 contract platform HTTP contract. Runtime structure is reconciled against an isolated service export; manually maintained explanations and explicit schema refinements are retained.
|
||||
|
||||
Primary file:
|
||||
|
||||
@@ -13,3 +13,33 @@ Rules:
|
||||
- Contract Web should consume this contract through generated or typed clients.
|
||||
- Contract API implementation must match this contract.
|
||||
- Contract API calls AI Platform only through SDK/OpenAPI clients, not direct provider adapters.
|
||||
|
||||
The 2026-09-09 local synchronization contains 282 paths, 328 operations and 450 component schemas: 440 runtime schemas plus 10 `Documented_*` components supporting seven explicit refinements of unconstrained runtime schemas. These refinements are reported separately from exact structural equality and fingerprinted under `x-yuqei-sync`.
|
||||
|
||||
The previous `POST /api/v1/auth/login` publication has no matching registered runtime route. Its definition remains in `x-yuqei-legacy-contract`, outside active `paths`; the appendix does not assert that a login endpoint or flow is available. Documentation from replaced fields remains under `x-yuqei-unmatched-documentation`. Historical `nullable` syntax is normalized to OpenAPI 3.1 null unions.
|
||||
|
||||
No generated SDK or consumer was replaced in this batch. Runtime operationId changes are recorded for later client migration. See [synchronization rules and check commands](../../../../common/tooling/openapi/sync.README.md) and [before/after evidence](../../../../common/governance/evidence/batch2-openapi-sync-contract.json). Re-export from current source before claiming current-source consistency; a historical runtime JSON is not proof for newer source.
|
||||
|
||||
TP06.10 increment: adds the current-tenant `GET /api/v1/admin/product-analytics/scene-quality`, privacy-minimized template recommendation events, and `GET /api/v1/identity/platform-access`. Apply additive migration `20260909_0078` before running the new analytics ORM/event writer. Prompt management in Contract Web checks the live platform permission on every BFF request; this does not extend authentication of direct AI service calls. The new quality view distinguishes candidate origin, withdrawal, reversal, missing information, and failures; its rates are not legal acceptance or pure AI success rates. Historical synchronization counts above predate this increment.
|
||||
|
||||
TP06.12 increment: optional `DraftingCreateRequest.journey_id` binds only an existing same-tenant/user/template `draft_guide_started` receipt. Workspace `journey_timing` records server-observed preview, required-input completion and candidate-content delivery checks; existing workspaces stay unmeasured. `GET /api/v1/admin/product-analytics/drafting-stage-timing` is a current-tenant platform-admin aggregate, with all start journeys in the denominator, explicit missing observations and a separate historical failure-journey count. This is not browser-visible time, legal approval, signing readiness or a release acceptance metric. No database migration is required for these additive workspace JSON fields. Run `yuqei-infra/scripts/tp06-12/sync-journey-openapi.py --check` with the repository Python paths to verify these scoped fragments; it does not certify all earlier OpenAPI refinements.
|
||||
|
||||
TP06.24.1/TP06.24.2 increments add candidate semantic-review creation and action routes.
|
||||
The action route retains the runtime `{action}` path parameter; the current
|
||||
implementation accepts only `refresh`, `cancel`, `retry`, and `repair`. Its request binds
|
||||
the expected workspace revision and candidate body hash. It does not expose a
|
||||
separate automatic-repair endpoint. `DraftingCandidateRequest.auto_repair` defaults to
|
||||
`false` for omitted API input; the ordinary drafting UI presents an initially checked,
|
||||
editable option and discloses the extra tasks before submission. When enabled, the
|
||||
coordinator may create at most one repair generation and
|
||||
records its parent/review linkage and continuation error without overwriting the parent.
|
||||
The workspace response includes the frozen request,
|
||||
attempt history, deterministic assessment, and server-validated execution
|
||||
evidence. A passed model opinion alone is not legal approval or adoption
|
||||
authorization. Run
|
||||
`yuqei-infra/scripts/tp06-24-1/sync-semantic-review-contract-openapi.py`
|
||||
without `--update` to check the isolated memory/no-database runtime fragment.
|
||||
When a reviewed candidate is adopted, `ContractVersion.drafting_review_reference`
|
||||
may expose only the review, job and audit IDs plus request/output/assessment and
|
||||
receipt hashes. Historical versions return `null`; the API does not expose the
|
||||
rest of internal version metadata through this field.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,43 @@
|
||||
# Yuqei Design System
|
||||
|
||||
Shared V2 design tokens and workflow primitives.
|
||||
Shared V2 design tokens, workflow primitives, and V4 shell components.
|
||||
|
||||
This package starts intentionally small. It holds product language, layout constants, tokens, and workflow metadata before it grows into a component library.
|
||||
|
||||
## V4 Shell Components
|
||||
|
||||
组件为纯展示组件(不引入运行时依赖),通过 `transpilePackages` 由消费方编译。样式类名与品牌令牌由消费方全局样式表提供(见 `apps/contract-web/src/app/globals.css` 的 V4 层,令牌定义见 `src/tokens.ts`)。
|
||||
|
||||
### SiteHeader
|
||||
|
||||
置顶网站导航骨架。品牌与导航链接组件由消费方注入(Next.js 应用传 `next/link`)。
|
||||
|
||||
```tsx
|
||||
import Link from "next/link";
|
||||
import { SiteHeader } from "@yuqei/design-system";
|
||||
|
||||
<SiteHeader
|
||||
brand={{ href: "/workspace", mark: "钰", title: "钰麒合同", subtitle: "AI 合同平台" }}
|
||||
nav={[{ href: "/workspace", label: "工作台", active: true }]}
|
||||
link={Link}
|
||||
actions={<>{/* 搜索、CTA、用户菜单 */}</>}
|
||||
onToggleNav={openDrawer}
|
||||
toggleIcon={<Menu size={20} />}
|
||||
/>
|
||||
```
|
||||
|
||||
### MemberHero
|
||||
|
||||
会员主页问候区:衬线标题 + 数据概览 + 行动按钮。
|
||||
|
||||
```tsx
|
||||
import { MemberHero } from "@yuqei/design-system";
|
||||
|
||||
<MemberHero
|
||||
title="下午好,张法务"
|
||||
kicker="8 月 28 日 星期五 · 法务"
|
||||
lead="合同在这里开始……"
|
||||
actions={<Link className="button button-primary" href="/legal-documents">起草合同</Link>}
|
||||
stats={[{ key: "pending", value: 3, label: "待我处理" }]}
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from "./tokens";
|
||||
export * from "./workflow";
|
||||
export * from "./site-header";
|
||||
export * from "./member-hero";
|
||||
export * from "./risk-card";
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* 会员主页 Hero(V4 外壳)。
|
||||
* 深松墨底 + 衬线问候 + 右侧数据概览;按钮/图标由消费方以节点传入,
|
||||
* 样式类名(member-hero …)由消费方全局样式表提供。
|
||||
*/
|
||||
|
||||
export type MemberHeroStat = {
|
||||
key: string;
|
||||
value: string | number;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export function MemberHero({
|
||||
kicker,
|
||||
title,
|
||||
lead,
|
||||
actions,
|
||||
stats,
|
||||
titleId = "member-hero-title",
|
||||
statsLabel = "今日概览"
|
||||
}: {
|
||||
kicker?: ReactNode;
|
||||
title: string;
|
||||
lead?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
stats: MemberHeroStat[];
|
||||
titleId?: string;
|
||||
statsLabel?: string;
|
||||
}) {
|
||||
return (
|
||||
<section aria-labelledby={titleId} className="member-hero">
|
||||
<div className="member-hero-copy">
|
||||
{kicker ? <p className="member-hero-kicker">{kicker}</p> : null}
|
||||
<h1 className="member-hero-title" id={titleId}>{title}</h1>
|
||||
{lead ? <p className="member-hero-lead">{lead}</p> : null}
|
||||
{actions ? <div className="member-hero-actions">{actions}</div> : null}
|
||||
</div>
|
||||
{stats.length > 0 ? (
|
||||
<ul aria-label={statsLabel} className="member-hero-stats">
|
||||
{stats.map((stat) => (
|
||||
<li key={stat.key}>
|
||||
<strong>{stat.value}</strong>
|
||||
<span>{stat.label}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* 风险卡(审查结果与公开样例共用骨架)。
|
||||
* 「风险 — 依据 — 建议 — 置信度」的统一呈现:
|
||||
* header = 风险标题 + 标签(严重度/置信度等),facts = 依据与建议条目,
|
||||
* children 用于审查引用列表等页面专属扩展。样式类名(risk-card …)
|
||||
* 由消费方全局样式表提供,令牌见 tokens.ts。
|
||||
*/
|
||||
|
||||
export type RiskCardTagTone = "neutral" | "success" | "warning" | "danger";
|
||||
|
||||
export type RiskCardTag = {
|
||||
key?: string;
|
||||
label: string;
|
||||
tone?: RiskCardTagTone;
|
||||
};
|
||||
|
||||
export type RiskCardFact = {
|
||||
key: string;
|
||||
term: string;
|
||||
node: ReactNode;
|
||||
};
|
||||
|
||||
const tagToneClass: Record<RiskCardTagTone, string> = {
|
||||
neutral: "tag",
|
||||
success: "tag tag-success",
|
||||
warning: "tag tag-warning",
|
||||
danger: "tag tag-danger",
|
||||
};
|
||||
|
||||
export function RiskCard({
|
||||
title,
|
||||
tags = [],
|
||||
detail,
|
||||
facts = [],
|
||||
footer,
|
||||
children
|
||||
}: {
|
||||
title: ReactNode;
|
||||
tags?: RiskCardTag[];
|
||||
detail?: ReactNode;
|
||||
facts?: RiskCardFact[];
|
||||
footer?: ReactNode;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<article className="risk-card">
|
||||
<header className="risk-card-header">
|
||||
<strong className="risk-card-title">{title}</strong>
|
||||
{tags.length > 0 ? (
|
||||
<span className="risk-card-tags">
|
||||
{tags.map((tag, index) => (
|
||||
<span
|
||||
className={tagToneClass[tag.tone ?? "neutral"]}
|
||||
key={tag.key ?? `${tag.label}-${index}`}
|
||||
>
|
||||
{tag.label}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
) : null}
|
||||
</header>
|
||||
{detail ? <p className="risk-card-detail">{detail}</p> : null}
|
||||
{facts.length > 0 ? (
|
||||
<dl className="risk-card-facts">
|
||||
{facts.map((fact) => (
|
||||
<div className="risk-card-fact" key={fact.key}>
|
||||
<dt>{fact.term}</dt>
|
||||
<dd>{fact.node}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
) : null}
|
||||
{footer ? <footer className="risk-card-footer">{footer}</footer> : null}
|
||||
{children}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import type { ComponentType, ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* 置顶网站导航(V4 外壳)。
|
||||
* 纯展示组件:导航与品牌链接通过 link 注入(Next.js 应用传 next/link),
|
||||
* 样式类名(site-header / site-nav / brand-block …)由消费方全局样式表提供,
|
||||
* 品牌令牌见 tokens.ts。
|
||||
*/
|
||||
|
||||
export type HeaderLinkComponent = ComponentType<{
|
||||
href: string;
|
||||
className?: string;
|
||||
"aria-current"?: "page";
|
||||
children: ReactNode;
|
||||
}>;
|
||||
|
||||
export type SiteHeaderBrand = {
|
||||
href: string;
|
||||
mark: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
};
|
||||
|
||||
export type SiteHeaderNavItem = {
|
||||
href: string;
|
||||
label: string;
|
||||
active?: boolean;
|
||||
};
|
||||
|
||||
export function SiteHeader({
|
||||
brand,
|
||||
nav,
|
||||
link,
|
||||
actions,
|
||||
onToggleNav,
|
||||
toggleIcon,
|
||||
toggleLabel = "打开导航",
|
||||
toggleControls,
|
||||
toggleExpanded,
|
||||
navLabel = "主导航"
|
||||
}: {
|
||||
brand: SiteHeaderBrand;
|
||||
nav: SiteHeaderNavItem[];
|
||||
link: HeaderLinkComponent;
|
||||
actions?: ReactNode;
|
||||
onToggleNav?: () => void;
|
||||
toggleIcon?: ReactNode;
|
||||
toggleLabel?: string;
|
||||
toggleControls?: string;
|
||||
toggleExpanded?: boolean;
|
||||
navLabel?: string;
|
||||
}) {
|
||||
const Link = link;
|
||||
|
||||
return (
|
||||
<header className="site-header">
|
||||
<div className="site-header-inner">
|
||||
{onToggleNav ? (
|
||||
<button
|
||||
aria-controls={toggleControls}
|
||||
aria-expanded={toggleExpanded}
|
||||
aria-label={toggleLabel}
|
||||
className="site-nav-toggle"
|
||||
onClick={onToggleNav}
|
||||
title={toggleLabel}
|
||||
type="button"
|
||||
>
|
||||
{toggleIcon}
|
||||
</button>
|
||||
) : null}
|
||||
|
||||
<Link className="brand-block site-brand" href={brand.href}>
|
||||
<span aria-hidden="true" className="brand-mark">{brand.mark}</span>
|
||||
<span className="brand-copy">
|
||||
<span className="brand-title">{brand.title}</span>
|
||||
<span className="brand-subtitle">{brand.subtitle}</span>
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{nav.length > 0 ? (
|
||||
<nav aria-label={navLabel} className="site-nav">
|
||||
{nav.map((item) => (
|
||||
<Link
|
||||
aria-current={item.active ? "page" : undefined}
|
||||
className={`site-nav-link ${item.active ? "site-nav-link-active" : ""}`}
|
||||
href={item.href}
|
||||
key={item.href}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
) : null}
|
||||
|
||||
{actions ? <div className="site-header-actions">{actions}</div> : null}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +1,52 @@
|
||||
/**
|
||||
* 钰麒合同 V4 品牌令牌(与 apps/contract-web globals.css 的 V3/V4 层保持一致)。
|
||||
* 深松墨 + 印章金 + 松石绿 + 暖纸;显示字为中文衬线栈。
|
||||
*/
|
||||
export const brandTokens = {
|
||||
color: {
|
||||
ink: "#172033",
|
||||
muted: "#667085",
|
||||
line: "#d9e0ea",
|
||||
canvas: "#f6f8fb",
|
||||
surface: "#ffffff",
|
||||
primary: "#2457d6",
|
||||
primarySoft: "#eaf0ff",
|
||||
success: "#16794c",
|
||||
successSoft: "#e7f6ef",
|
||||
warning: "#b35b00",
|
||||
warningSoft: "#fff3df",
|
||||
danger: "#bd2d2d",
|
||||
dangerSoft: "#fdecec",
|
||||
accent: "#0b7a75",
|
||||
accentSoft: "#e4f5f3"
|
||||
ink: "#17201d",
|
||||
inkSoft: "#35433e",
|
||||
muted: "#6a726c",
|
||||
line: "rgba(53, 67, 62, 0.16)",
|
||||
canvas: "#f6f5ef",
|
||||
surface: "#fffdf8",
|
||||
surfaceElevated: "rgba(255, 253, 248, 0.96)",
|
||||
primary: "#0f6f63",
|
||||
primaryDark: "#0a524b",
|
||||
primarySoft: "#e8f3ef",
|
||||
success: "#1f7459",
|
||||
successSoft: "#e9f5ee",
|
||||
warning: "#9a5d18",
|
||||
warningSoft: "#fff3dd",
|
||||
danger: "#b3313d",
|
||||
dangerSoft: "#fff0f1",
|
||||
accent: "#b86f2d",
|
||||
accentSoft: "#fff0dc",
|
||||
pine: "#10251f",
|
||||
sealGold: "#e9ad55",
|
||||
sealGoldDeep: "#d4923a",
|
||||
paper: "#f6f5ef"
|
||||
},
|
||||
font: {
|
||||
body: 'system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif',
|
||||
display:
|
||||
'"Noto Serif SC", "Source Han Serif SC", "Songti SC", "STZhongsong", "SimSun", serif'
|
||||
},
|
||||
radius: {
|
||||
sm: "6px",
|
||||
md: "8px"
|
||||
md: "8px",
|
||||
lg: "10px",
|
||||
pill: "999px"
|
||||
},
|
||||
shadow: {
|
||||
panel: "0 10px 30px rgba(16, 24, 40, 0.08)"
|
||||
panel: "0 14px 36px rgba(23, 32, 29, 0.06)",
|
||||
card: "0 18px 48px rgba(30, 37, 34, 0.09)",
|
||||
glow: "0 22px 56px rgba(233, 173, 85, 0.32)"
|
||||
},
|
||||
layout: {
|
||||
sidebarWidth: "248px",
|
||||
topbarHeight: "64px",
|
||||
contentMaxWidth: "1440px"
|
||||
headerHeight: "68px",
|
||||
contentMaxWidth: "1200px",
|
||||
drawerWidth: "min(86vw, 350px)"
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user