
CopilotKit reskinnable-demo 的 Aeronova 航空公司皮肤基于 Demo Beats 的通用生成式 UI 演示设计蓝图【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit导读本文围绕 examples/showcases/reskinnable-demo 中airline皮肤品牌名Aeronova的设计文档 beat-map.md 展开完整拆解一个乘客出行礼宾passenger-facing travel concierge是如何把 reskin 框架定义的九大demo beats逐一落地的。读完本文你将理解为什么权利来自票面规则entitlement而非组织层级是这套皮肤的地基如何用 REST ledger、卡确认、四档筛选杠杆、种子记忆、存储过程与可教学闸门把九拍演成一场可复现的现场演示以及演示正确与演示说谎之间那些只在台上暴露的坑。Aeronova 是什么乘客礼宾而不是运营控制台airline皮肤从一开始就锚定一个身份Aeronova 是面向乘客的旅行礼宾——没有运营席、没有值班调度、没有别人的问题清单。用户就是一个在看自己行程的乘客。早期对这套底层的一次尝试曾得出结论乘客礼宾无法承载 beat 3a、3c 和 6理由是乘客没有操作员、没有审批权、没有看板于是把皮肤改写成非正常运营控制台。那次重构被否决了。纠正它的洞察是整个设计的前提原文用引用块写在文档最上方权威authority不必是组织性的它可以是资格性的entitlement。一道写着你的票价不允许这样做的闸门和一道写着你缺乏审批权限的闸门拒绝得一样坚决而且前者完全不需要任何层级。票价规则本身就是权威模型。由此九个 beat 想要的运营方能力全部在乘客世界里找到了更熟悉的对位运营台会有……乘客拥有……一个审批权限票价规则——这张票允许做什么升级给经理在豁免类别下、附上文件提交的票价例外一摞案子自己的订座——一个带同行人的账户一个过滤的工作队列房间里每个人都亲身用过的改签搜索一个签名 PIN付费改签时的卡片确认一个要通知的同事在机场接机的人或留着房间的酒店账户结构一个主账户三个旅行者底层数据是一个旅行者档案——账户持有人Camila Rojas同一档案下挂两位同行人伴侣Tomás Aguirre和母亲Inés Vidal。这是再普通不过的航司 App 形态保存的出行人 为他们做的每一笔预订。它让 beat 3c 有了看板、让 beat 6 有了不止一个受闸门约束的案例而无需发明任何组织角色。只留一个数据源内存 store 已被删除文档记录了一次已经完成的迁移useAirlineData与 data/use-data.ts文中标注为已删除曾是另一个并行的内存 React store现在所有组件只读useAirlineLedger()由 components/concierge-view.ts 投影成值机页原本渲染的形状skin.useData不再配置。原因很实际两份种子数据并存台上就可能出现同一个航班两个时间的自相矛盾。见文末五、双数据源的教训。拍点地图The Beat Map九拍与八颗药丸reskin 框架把一场演示必须证明的能力编码为九拍定义在 .claude/skills/reskin/demo-beats.md。airline的拍点地图把每一拍对应到具体的实现载体BeatAeronova 的做法药丸建议话术实现位置1 face行程墙即生成式 UI每一笔预订、票价条件、当下被扰乱的内容How do my trips look?GET /ledger→useComponent2 rich thread每个可视化都是useComponent以result为键刷新后行程墙仍在对话流里无——用刷新演示每个终态渲染都以result为键lint 保护3a drive the app把档案卡片的后四位打进聊天里的卡片支付差价Move me to the earlier Buenos Aires flightdata/card-authorization.ts POST /authorizations3b sees my screen在 Trips 页发问 → 跳转到改签搜索 → 再问一次What am I looking at?路由 每页 readables/ledger是一张快照3c levers改签搜索的起飞时段、经停、舱位、排序四档 top-N全部高亮Evening nonstops home, cheapest first, top 5data/rebooking-levers.ts data/rebooking-options.ts3d multimodal附上酒店确认单→ 在行程记录上归档一份持久的Trip BriefRead my hotel confirmation and file the trip briefGET /hotel-confirmationPOST /briefs data/hotel-confirmation-pdf.ts4 memory预置座位与票价偏好靠过道、机翼前、绝不 Basic Economy、用她的时区报时间Summarize my tripsintelligence/seed-memories.tsscope: user5 stored skill我回家的航班被取消了——处理一下 → 依次改签、换座、通知同上POST /bookings/[id]/change、/seat、/notify data/handling.ts6 teach a skill改一张不可改的票。被拒。在豁免类别下解锁Get Tomás onto the Thursday Miami flightdata/fare-waiver-codes.ts data/fare-rules.ts POST /fare-exceptions/[id]/approve(reset)演讲者重置恢复账户并遗忘 重新播种 beat 4/5侧边栏按钮不是药丸POST /dev/reset九拍对应八颗药丸beat 2 靠刷新浏览器演示。没有再建议第九颗画布药丸beat 3d 的药丸既摄取酒店确认单、又归档产物走的是banking形状而非people/commerce的独立画布药丸。这与 demo-beats.md 中只有画布 brief 需要自己的诉求时才加第九颗的规则一致。Beat 3a —— 界面保守的秘密卡片后四位秘密是什么、怎么流付款卡片的后四位。乘客在做一次付费改签要付票价差价加上票规里的任何改签费。航司要求乘客确认卡片。乘客把后四位打进了聊天里的卡片agent 永远看不到它、被明确告知永远不要索要它POST /authorizations也从不回显它——被拒时只说不接受从不说你打了什么。data/card-authorization.ts 中的readCardLast4()同时返回卡片打印的引导文案和提交按钮比对的谓词所以卡片不可能邀请一个服务端会拒绝的形状。它采用读不懂就拒绝而非剥离字符——文档特意点名了Number(typed.replace(/[^0-9]/g,))这种解析器会把-4417悄悄变成有效确认同时还没输入是独立标志未触碰的输入框不会被训斥。readCardLast4只容忍首尾空白字母、符号、小数点、指数一律拒绝并给出可朗读的句子。只做格式校验这是故意的和物流皮肤 PIN 一样这只是格式校验种子里没有任何卡号ledger 也从不携带卡号档案只暴露paymentCardLabel是品牌加圆点绝不是数字。任意四位都接受。对舞台演示这是刻意为之——这一拍要证明的是值从哪里走而不是认证谁。card-authorization.ts顶部那段注释就是将来要真正做第二因素时该删掉的东西。⚠️ 它不是资格覆盖——这是整局的陷阱POST /authorizations会重新运行普通改签路由跑的同一个checkFareChange()而且跑在自己重算的数字上。有效卡确认 不可改票面 仍是422 FARE_NOT_CHANGEABLE。如果卡片能放行那就等于在 beat 6 的闸门旁边开了第二扇门agent 会绕过闸门、教学弧线永不触发、什么都不会失败——卡片很漂亮、写入成功、全场鼓掌而演示证明了与它主张相反的东西。src/app/api/airline/v1/authorizations/route.ts 的执行顺序还藏着两个细节卡片格式检查最先跑一个读不懂的请求在查 ledger 之前就被拒避免把哪些订座存在、哪些选项可用免费泄露给未认证调用方之后才是闸门、算钱。Aeronova 的分离比物流皮肤更结实物流的闸门压在金额上调用方选哪个选项决定闸门是否触发Aeronova 的闸门是票面的属性任何选项选择都溜不过去——非可改订座上的每个选项都被拒authorizations/route.test.ts正是从实时 ledger 发现并遍历全部选项来断言这一点。两个fare-rules.ts保持的性质出自 failure-modes § 12卡片只出现在乘客已经有权采取的选项上——authorizableOptions()只返回那些只在确实有钱要付时出现。取消后的非自愿改签是$0为移动$0要卡片是把形式主义包装成授权物流那个 costing$0的absorb是同一个 bug。amountDueUsd 0在该过滤里是承重的卡片必须说这单没欠款而不是渲染一个注定失败的输入框。路由同样强制$0授权返回422 NOTHING_DUE。两条写路由互补这才是卡片不是装饰的原因。POST /bookings/[id]/change只在无欠款时提交有钱时它停在402 PAYMENT_REQUIRED并点名金额。因此POST /authorizations是唯一能提交付费改签的路径——因为卡确认只到达它。如果普通改签路由自己能收钱beat 3a 的卡片就成了演示可以跳过的装饰步骤且什么都不失败。种子记录beat 3a 跑在bkg-av7702上——Camila 去布宜诺斯艾利斯的 Flex 票。选它是因为 Flex 不收改签费应付额纯粹是票价差价乘客是为更好的座位付钱不是付罚金——这是台上观众觉得通情达理而非恼人的版本。Beat 3c —— 四档杠杆改签搜索是房间里每个人都亲手用过的东西没人需要被告知出发时段筛选器是什么。data/rebooking-levers.ts 持有一个归一化记录同时喂给确认卡片的 chips、push 的 URL、页面管道和工具 schema杠杆页面词汇含义未拉取windowmorning、afternoon、evening00:00–11:59 / 12:00–17:59 / 18:00–23:59 起飞allstopsnonstop、one_stop、two_plus0 / 1 / 2 次经停allcabineconomy、premium、business选项定价的舱位allsortprice_asc、depart_soonest、duration_asc最便宜 / 起飞最早 / 航程最短alltop正整数截断到 N 行0三个实现事实值得展开每个杠杆必填且未拉取哨兵就在枚举内部。原因是物流皮肤实测过的面对可选枚举的模型照样会填满它于是机动操作落在一个空看板上、四个自信点亮却空的控件。normalizeLevers按构造丢弃哨兵它不在页面词汇里未设置的杠杆画不出 chip、写不进 query 参数。applyLevers从同一条管道发布两个长度——matching应用了杠杆和visiblematching截到top。页面的Top 5 of 18标题、行和 readable 都读这两个值所以标题不可能说过滤没起作用而行又说起作用了commerce 的 bug。data/rebooking-options.ts 里localHourOf直接从 ISO 字符串里读机场本地小时而不是走new Date().getHours()——后者返回进程所在时区的小时会让利马的傍晚班机在 CI 机器上被过滤成下午班机。种子故意做肥。被取消的回程AV1466LIM→SCL携带30 个改签选项横跨三个时段、三个经停桶、三个舱位。beat 自己的杠杆组合——windowevening、stopsnonstop、sortprice_asc、top5——剩10 行匹配截成 5。物流的suggestions.ts记录了为什么一行的看板在台上和筛选器坏了无法区分。rebooking-options.test.ts直接断言 ≥5 的下限未来重新播种把看板变薄会先挂测试而不是挂演示。Beat 4 —— 偏好记忆Camila 的座位与票价偏好四条子句每一条都肉眼可见地改变回答且底层都带着对应字段给我靠过道、机翼前的座位如果有。绝不 Basic Economy即使它更便宜。 用我的家时区America/Santiago报每次出发而不是机场本地钟。 总结我的行程时先说被扰乱的。子句需要的字段位置靠过道 / 机翼前columnKind()isForwardCabin()、availableSeatsdata/handling.ts、Flight绝不 Basic每个选项上的fareBrandRebookingOption家时区旅行者上的homeTimezone每个 ISO 的偏移Traveler、Flight扰乱优先statusdelayMinutesscheduleChangeMinutesFlight总结工具需要一个note槽位让 agent说出它应用了哪条偏好否则这一拍是隐形的——观众只看到一次普通回答。种子在 intelligence/seed-memories.ts已上线scope: user绝不project原因见 demo-beats.md Seeding memories一节project 作用域对该共享实例的每个用户 id 都生效、且能穿过重置存活会让第二次演示开场就已经会了。种子记忆里明确要求应用全部偏好而无需询问并在渲染行程总结时把所用偏好写进 note让乘客看到自己被记住了。Beat 5 —— 存储过程处理一下触发句只有一句含糊的话My flight home just got cancelled — handle it.记录是bkg-av1466——Camila 的回程AV1466LIM→SCL状态cancelled。三步有序写入每一步都在行程记录上可见改签到最佳选项——POST /bookings/[id]/change。航班被取消改签是非自愿的按规则任何票面都免费。这正是 beat 5 不碰 beat 6 闸门的原因见下。按偏好换座——POST /bookings/[id]/seat偏好来自SEAT_PREFERENCESaisle、window、forward-cabin、exit-row。服务端挑选符合偏好的最佳可用座位并说出匹配的是哪条规则航班没有的空座拒绝而非虚构。data/handling.ts 里的pickSeatForPreference返回null而非最近的近似座——悄悄把人塞进中间座还报告成功正是本应用最怕犯的自信的假话。通知下游一方——POST /bookings/[id]/notify一方来自NOTIFY_PARTIESarrival-pickup、hotel、travel-companion、employer-travel-desk。联系方式从订座记录上复制绝不取自调用方——客户端给的姓名是模型拼出来的姓名。订座没有该方联系方式的就拒绝应用永远不会声称通知了一个不知道怎么联系的人。三步都追加一条TripLogEntry且通知条目带强制的标记markNote——让改签在投影仪上不可略过。用平淡措辞复述的模型不能夺走这一拍唯一可见的产物。词汇是给agent 的这个词汇表显式授予 agent——枚举在工具 schema、提示词和拒绝正文里。这与 beat 6 正相反反差就是重点这里没什么要发现的整个主张就是它已经知道流程。两个词汇住在两个共享零 token 的模块——data/handling.ts授予vs data/fare-waiver-codes.ts扣留。handling.ts里没有 waiver、exception、schedule change、medical、bereavement、militaryfare-waiver-codes.ts里没有 notify、party、seat、aisle、hotel、pickup。fare-waiver-codes.test.ts双向断言这个隔离将来有人伸手拿代码文件时无法把扣留词汇意外导入tools.tsx。干扰项已注册且必须保留showFlight、showLoyalty、showRedemptions、trackBaggage、showSeatMap、showBoardingPass。它选对了那三个只有在这些都在场时才有意义。提示词还要明说这是不同于beat 6 票价例外弧线的流程不得主动提出记录任何东西。为什么 beat 5 永远碰不到 beat 6 的闸门checkFareChange()在看到票面之前先短路处理非自愿扰乱航班cancelled或时刻变更达到/超过 4 小时非自愿阈值INVOLUNTARY_SCHEDULE_CHANGE_MINUTES 240就允许在任何票面上免费改签包括 Basic Economy。这是真实的行业规则也是两拍不撞车的原因beat 5 的订座已取消所以不受闸门约束beat 6 的受闸订座是完好航班上的自愿改签。fare-rules.test.ts钉死两个方向。Beat 6 —— 可教学的闸门 ⭐这是本皮肤最重要的设计决策文档原文也完整写出了推理。闸门POST /api/airline/v1/bookings/[id]/change把订座改签到另一个选项。拒绝422 FARE_NOT_CHANGEABLE。AV3PL9 is ticketed in Basic Economy. Changes are not permitted on this fare — this ticket cannot be reissued to another flight.它只点名票面条件别的什么都不说没有exception这个词、没有类别、没有去问 agent。这就是乘客必须演示、agent 必须学会的东西。每个数字都由服务端重算——客户端提供的差价或费用被忽略否则闸门就是演戏。改签路由的执行顺序是承重的票价检查跑在金额检查之前所以不可改票返回FARE_NOT_CHANGEABLE而不是PAYMENT_REQUIRED——否则闸门的症状会是一张账单乘客会学去摸卡而不是学流程。解锁POST /api/airline/v1/fare-exceptions在豁免类别下对订座提交例外用乘客提供的文件作为依据documentReference必填非空——背后没有文件的提交以MISSING_DOCUMENTATION拒绝且不点名任何类别。POST /api/airline/v1/fare-exceptions/[id]/approve批准它并关联到订座这才是抬闸门的东西——前提是类别有理由、且订座记录确实支撑它。正当类别4 个——真实的行业类别Code含义SCHEDULE_CHANGE_TRIGGERED出票后航司移动了航班MEDICAL_DOCUMENTED有记录的医疗原因证明存档BEREAVEMENT_DOCUMENTED有记录的丧亲MILITARY_ORDERS军令迫使的变更诱饵类别3 个——如实提交、如实记录、不解锁任何东西。它们强就强在正是人人都会试的Code含义为什么诱人CHANGED_PLANS乘客计划变了每次自愿改签的字面真相也是任何人最先输入的FOUND_LOWER_FARE出现了更便宜的票价现实中最常见的原因也是没有航司会认的那个ELITE_COURTESY给高卡会员的礼遇已记录Camila 是 Gold、Tomás 是 Platinum——直接问呗我们是精英是房间里的本能未收录代码→422 INVALID_EXCEPTION_CODE且不枚举合法集合。猜的成本始终保持高昂——src/app/api/airline/v1/fare-exceptions/route.ts 的注释明确写着4xx 正文是最容易无意泄漏闸门词汇的通道合法代码是 X、Y、Z是本应用每个其他路由的反射在这里却是缺陷。锚定grounding——为什么光有正当代码还不够例外只有在类别与订座记录实际记载的情形吻合时才抬闸门exceptionLifts(code, booking) isJustifyingExceptionCode(code) GROUND_BY_CODE[code] booking.waiverGround这是诚实的读法——你没法在从未变动的航班上申领时刻变更豁免也没法在没有存档证明的情况下申领医疗豁免——也正是它让两个受闸订座真正彼此不同。没有锚定第一个订座上的演示可以被当成背下来的字面量重放在第二个上SCHEDULE_CHANGE_TRIGGERED两个都适用换个案例、无协助的主张就成演戏了。有了锚定学到的流程必须是读订座记载了什么 → 提交匹配的类别 → 批准 → 重试改签——这是一个流程不是一个字符串。data/fare-waiver-codes.ts 里GROUND_BY_CODE按元组自身成员类型键控新增一个无锚定依据的正当类别是类型错误而不是静默解不了锁的类别。锚定值永远不以 token 形式上线。Booking.waiverGround仅服务端持有store.snapshot()会剥掉它ledger 携带的是fareNotes——乘客在自己订座上读的人话散文如Aeronova moved AV2214 by 3h 10m on 2 Jul; notice AV-SC-88214 is on file。store.test.ts钉死剥离行为——这是 failure-modes 清单没点名的词汇通道因为没有其他皮肤有锚定闸门。提交路由和批准路由都不说例外会不会解锁。201 正文里一个{ lifts: true }就会把整个目录一次一个探针地交给对方。唯一得知的方式是重试改签——这正是乘客演示的那个循环。fare-exceptions/route.test.ts断言了这个缺失。种下的受闸订座三个不是两个两个是为了台上教的案例和无协助重放的案例是不同的人第三个是为了让诱饵是真的而非理论上的。订座票号旅行者航班票价文件用什么解锁bkg-av2214AV3PL9Tomás AguirreAV2214 LIM→MIABasic Economy3 小时 10 分的时刻变更通知存档SCHEDULE_CHANGE_TRIGGEREDbkg-av0918AV8RT4Inés VidalAV0918 SCL→MADPromo不可退医生证明存档MEDICAL_DOCUMENTEDbkg-av1188AV5KD1Camila RojasAV1188 SCL→GRUBasic Economy什么都没有什么都没有——有意为之三者分别换了旅行者、航线、票价品牌、理由和解锁类别。AV3PL9上的 3 小时 10 分时刻变更刻意压在4 小时非自愿阈值之下自动规则覆盖不了的委屈正是真人去提例外的时候。bkg-av1188是演示的诚实之墙。Camila 就是想拿 Basic Economy 票提前一天走。CHANGED_PLANS和FOUND_LOWER_FARE字面上成立、提交干净、出现在记录上——然后什么都不变。ELITE_COURTESY也一样。正当类别提交到它上面也不解锁因为记录上没有任何东西可以锚定。这个案例让诱饵是真的从 README 里的一句话变成事实。两个受闸案例都是算出来的不是断言出来的blockedByFareRules()用服务端跑的同一个checkFareChange()推导它们乘客侧例外表单永远不可能推销一个闸门实际不会拒绝的订座。⚠️ 词汇对 agent 扣留data/fare-waiver-codes.ts 携带完整警告这里是摘要。它会从五个通道泄漏堵四个等于没堵useAgentContextreadable提交工具 schema 上的z.enum(FARE_WAIVER_CODES)工具自己的description提示词拒绝正文。在 code 参数上取自由的z.string()并在.describe()里声明扣留。这反转了其他地方枚举一切闭集的规则——对闸门而言让词汇到达模型就是缺陷。eslint.config.mjs的withheldGateVocabulary规则只抓通道 2 和 3以标识符出现的两个且其filesglob 曾不包含airline。承载src/skins/airline/tools.tsx和src/skins/airline/agent.ts的槽位必须把两者都追加进该 glob——在同一代码块里重申 LOCK_SKIN 选择器因为 flat-config 的rules是替换而非合并——否则没人检查 Aeronova。验证用npx eslint --print-config src/skins/airline/tools.tsx并数选择器。通道 1、4、5 靠 grep 加人读。文档随后注明如今两个文件都已进 glob且src/shell/skins-config.test.ts按名字断言解析后的选择器表。把表单建出来。它是第六个通道而且必须开放。FARE_WAIVER_CODE_LABELS是为行程页上的人用票价例外表单预留的乘客选类别agent 看着他们选。菜单必须把正当类别和诱饵混排、不标记、按目录顺序——一个把可用项标出来的表单会把演示变成导览。录音器必须把 code原样记成数据诱饵也包括一个悄悄纠正的录音器会报告一个没人演示过的流程。对应实现是 components/fare-exception-form.tsx。Beat 3d —— ledger 提供不了的文档一张酒店确认单选它而不是企业差旅政策理由就是这一拍真正要过的测试产物能不能说出两个来源单独都说不出的东西企业差旅政策大多复述偏好舱位上限、首选航司——beat 4 的记忆已经带着这些了。摄取它会产出一份 agent 凭已知信息就能写出的 brief。酒店确认单带着最后办理入住时间和取消截止时间在 Aeronova 的世界里不存在——而航班的到达时间在酒店的世界里也不存在。brief 的标题句就是两者相撞的地方AV1423 now lands 23:00; Casa Miraflores stops taking arrivals at 22:30.这句话是不可伪造的文件确实被读了的证据因为单靠任何一边都推导不出来。GET /hotel-confirmation?bookingAV7QK2从 data/hotel-confirmations.ts data/hotel-confirmation-pdf.ts 生成 PDF字节来自/shell/documents本文件只提供内容。POST /briefs归档持久的Trip Brief它活在行程记录上而非对话流里——删掉整个会话brief 还在。字段按谁拥有事实拆分这是物流皮肤oldRateUsdPerKg教训一个方向错成三个的应用拥有者字段规则文档hotelName、confirmationNumber、address、lastCheckInLocal、cancellationDeadlineLocal、nightlyRateUsdmodel 撰写——只有读了附件的人知道。这就是这一拍存在的证明ledgerbookingRef、travelerName、arrivalStation、arrivalLocal路由在唯一匹配时从 ledger 覆盖无匹配时丢弃并返回两个清单让工具告知 agent而非默默改判派生arrivesAfterLastCheckIn三态true \| false \| null——到达未知时为null绝不是falsefailure-modes § 1一个false会告诉模型已检查、没问题而它会对一个没人能做的比较这么说??不是结算它修补了未填的情况、存下了错的那个。hotel-confirmation-pdf.ts里effectiveArrival的minutes故意不折返到 24 小时内——23:15 晚到 90 分钟变 00:4500:45 22:30是false错过酒店最狠的那班会被报成按时到达。按文档陈述的对象来界定匹配范围。酒店确认单是一个旅行者某天到达某城的陈述这就是匹配键——在每行种子上唯一。光按城市匹配会同时命中 Camila 的两段利马航段看起来无从结算。而且每一行都属于文档致函的一方。hotelConfirmationFor()按订座参考键控条目并把条目的城市与订座的实时目的地再核对一遍重播种移动航班时丢弃条目而非错误归因。commerce 曾在每家供应商的价目表上硬编码一行、凭空捏造不存在的供应商关系这就是那个教训的应用。路由树GET /api/airline/v1/ledger 一张快照profile、travelers、bookings、flights、options、exceptions、briefs GET /api/airline/v1/bookings/[id] 单个订座按 id 或 PNR 解析 GET /api/airline/v1/bookings/[id]/options 该订座的改签选项杠杆从 query string 应用 POST /api/airline/v1/bookings/[id]/change BEAT 5 第一步 BEAT 6 闸门——改签到某选项422 FARE_NOT_CHANGEABLE POST /api/airline/v1/bookings/[id]/seat BEAT 5 第二步——按服务端解析的偏好换座 POST /api/airline/v1/bookings/[id]/notify BEAT 5 第三步——通知下游一方联系方式从订座复制 POST /api/airline/v1/authorizations BEAT 3a——卡片确认的付费改签重跑同一个票价闸门 POST /api/airline/v1/fare-exceptions BEAT 6 解锁第一步——按类别 文件提交 POST /api/airline/v1/fare-exceptions/[id]/approve BEAT 6 解锁第二步——批准并关联从不说明是否解锁 GET /api/airline/v1/hotel-confirmation BEAT 3d——被附上的 PDF按订座生成 POST /api/airline/v1/briefs BEAT 3d——归档持久 Trip Briefledger 事实服务端结算 POST /api/airline/v1/dev/reset 演讲者重置——恢复账户并遗忘 重播种记忆所有路由共享两条约定[id]接受订座 id 或 PNR而 PNR 被多个航段共有时返回409 AMBIGUOUS_REFERENCE并附候选 id绝不静默挑选。Camila 的去程和回程都在AV7QK2之下——真实预订就是这样——所以改签 AV7QK2确实是一条歧义指令取第一个匹配会在报告成功的同时改错航段。没有路由信任调用方的算术。票价差价、改签费和总额在每次写入时都从 ledger 重算正文里的数字直接被忽略否则闸门就是演戏。这个槽位当时没建的——如今全部落地这份清单原本是底层槽位的交接单。现在每一行都已建成保留它是为了记录正确接线之外还缺什么demo-beats.md Which skin to copy for what 指向三个 retrofit 正是为此当时延后它补完了哪一拍现状tools.tsx、agent.ts、suggestions.ts、页面、组件全部——底层当时没有 UI✅ 已上线路由 每页useAgentContextreadables3bairline 从未命中过✅layout.tsx 五个页面readables.test.tsx守护遗漏intelligence/seed-memories.tsforget-memories.ts4、5 和重置的重新武装一半✅ 两者user作用域intelligence/user-id.tsagent-registry.ts里的identifyUser按用户记忆作用域✅ 外加useRuntimeProperties没有RuntimeProviders没有可读上下文人用票价例外表单6——扣留了目录却没有表单 学不会的闸门✅components/fare-exception-form.tsxattach-hotel-confirmation.ts基于/shell/attach3d 的药丸和回形针✅ 外加CanvasSurface与服务端工具render_trip_brief把页面迁离useAirlineData到/ledger下面的双数据源风险✅useAirlineData与data/use-data.ts已删除只剩useAirlineLedger()文档标记的三个陷阱及其解决eslint.config.mjs的withheldGateVocabularyglob 没列 airline。✅ 现在tools.tsx和agent.ts都在里面airline 也进了statusKeyedTerminalRenderglob——两者都重申 LOCK_SKIN 选择器flat-config 替换不合并。不要靠数选择器验证src/shell/skins-config.test.ts里的解析选择器表按名字断言每个文件的清单。LINTED_SKIN_IDS已列出 airline。仍值得亲自核对而非轻信skins-config.test.ts机械地证明它。重置路由曾故意memoryBeats: unarmed。✅ 在加入 seed-memories 模块的同一次改动里删除——重置现在遗忘并重播种。乘客框架真正与拍点打架的地方文档如实列出因为下一个槽位要继承这些。Beat 3c 的看板是选项不是记录。其他演示完整的皮肤过滤的是工作队列。乘客没有队列所以杠杆过滤的是航班搜索结果。这是更知名的控制面而非更差的——但行是候选而非义务照抄 commerce工作清单外观会读起来不对。要按搜索结果列表来设计。钱很小这没问题——但要把数字说出来。票价差价是几百美元不是货运缓释的五位数。这里的闸门压根不在金额上在票面条件上绕开了物流的问题但 beat 3a 的卡片仍然授权一笔小钱。演讲者应该把它读出来。账户有三个旅行者——这是框架唯一被拉扯的地方。只有自己行程的乘客无法承载 beat 6不同案例、无协助的要求——单个旅行者的行程倾向于共享一种票价和一个理由。档案上的保存出行人是普通航司功能拉扯很小——但确实是拉扯后续槽位应该让档案明显是 Camila 的账户上她的名字、同行人明确是她的别让行程页读起来像代理控制台。这是要防重构回运营台的唯一观察点。Beat 4 的扰乱优先子句需要一场扰乱撑过演示。Beat 5 通过改签解决了被取消的回程。如果 beat 4 在演示顺序里排在 beat 5 之后它本该领头的扰乱就没了。要么把 beat 4 排在 beat 5 前面要么让bkg-av1423的延误保持原样beat 5 不碰它——种子保留那个延误正是为此。双数据源、一个乘客——台上不能互相矛盾。✅ 已解决删掉了一个。Camila 的 AV1423 曾同时存在于内存use-data.ts和这个 REST ledger 里所以这份种子曾逐字段对齐内存版——同一航班号、航线、城市、机型、登机口、时间、delayed状态和 55 分钟延误——而 REST 底层新增的一切都坐在内存 store 从未听过的记录上回程、同行人订座、选项看板。手工维持两份读数一致撑不过一次重播种所以迁移删掉了内存版而不是同步它REST ledger 是唯一权威components/concierge-view.ts把它投影到值机页已渲染的形状上。可推广的部分是重复种子不是安全中间态——它是一对可以在投影仪上互相矛盾、却无人检查的数字。实践要点速查开始任何新皮肤之前先写拍点地图demo-beats.md 的九行模板缺的拍写SKIPPED — 原因不要删行airline的 beat-map.md 与 keel 的 beat-map 都是已完成的范例。秘密卡号后四位/PIN只走聊天内组件agent 看不到、提示词明令不索取、响应不回显格式校验与提交谓词出自同一个 helper避免台上按 App 自己的指示操作却被拒。闸门词汇对模型扣留与授予型流程词汇分开存放零共享 token并用 eslint 规则 按名字断言的测试守护人用表单是唯一合法通道。每个终态渲染都以工具result为键而非status否则刷新后线程回放会渲染空白这在 Intelligence 模式下才可跨刷新持久。所有金额与闸门判定服务端重算写入正文的数字直接忽略阅读 data/fare-rules.ts 与 data/card-authorization.ts 是理解这套权威模型最快的方式。【免费下载链接】CopilotKitThe Frontend Stack for Agents Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考