Skip to content

feat(content-ops): add media providers and Goal artifact production - #6876

Open
loopx-agent wants to merge 8 commits into
mainfrom
codex/content-media-generation-20261019
Open

loopx-agent wants to merge 8 commits into
mainfrom
codex/content-media-generation-20261019

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

When a Goal has prepared media, the owner should preview and revise it beside the work that produced it. The previous implementation put video/image production inside capability settings. This proposal moves it to Goal → Files → Content artifacts, keeps settings on their existing configuration surface, and binds preview/downloads to a saved revision and SHA256.

内容制作现位于 Goal → 成果 → 内容作品。返回当前对话会保留 Goal 并聚焦输入框;浏览、重新读取和打开来源信息不会提交生成任务。Goal 交付检查复用现有验收组件,读取失败明确显示未知并提供刷新入口。

Delivery and ownership

  • Retain the optional loopx-media local renderer and explicit Ark image/footage providers. Immutable preparation identities, request receipts, reference digests, watermarks, lost-response recovery and no-duplicate-submit rules remain owned by the existing typed Content-Ops contract. Python handles transport and file/provider IO.
  • Preview, subtitles, transcript and downloads use the exact saved revision and file hash. Reject stale/mixed identities, incomplete or duplicate version keys, changed bytes and out-of-scope files. Preserve existing unpinned current-file URLs. Hash the actual opened descriptor before serving full or ranged bytes.
  • Opening saved work performs reads. A saved render request is labelled as awaiting a result; only a render explicitly started in this mounted view enables local observation. Leaving the view or a failed read stops observation. Cloud generation remains an explicit action with its existing permission and budget boundaries.
  • Goal delivery checks opens the existing Goal acceptance owner shared with other deliverables. The current Goal plan determines its scope. No media-specific approval state or new acceptance schema is introduced. Existing non-media results and their unavailable states remain visible; only successfully read, empty report lists are omitted in this production view.

Validation

Packaged browser capture source: 9c73a0a6e76bc46113caefa45e70c854e50fc79f. Final source fbc71e7728cc24b9d376695296bfbdd9bccbc484 additionally stops render observation when switching production tabs; its dashboard typecheck/build and native quick premerge are verified separately. The first-screen presentation is unchanged. Based on target main at 82b1a8c72159dedcbca3a3b69fac2c7eaad3192d.

  • 29 Python media/provider tests passed; 2 opt-in tests skipped in the default run. The optional renderer/encoder/HTTP test was also run explicitly and passed, including partial bytes, revised compositions and stale links.
  • 18 TypeScript media tests passed. Dashboard typecheck, packaged Chat build, configured mypy (17 source files), Ruff and the native quick premerge checks passed.
  • Existing delivery-review and Goal-acceptance API/export smokes passed after integrating the current acceptance owner. Default-absent checks, stale/source identity and association-versus-verification behavior remain covered.
  • Real Ego Lite checks against the built Chat bundle and owning local backend: decoded local video; desktop and 390px layouts without document overflow; keyboard disclosure and reduced-motion setting; settings make no media requests; unknown image state exposes recovery without another submit; returning to the same conversation focuses the composer and sends nothing. Opening the checks disclosure was read-only and displayed the fixture's unavailable acceptance state truthfully.
  • Earlier author test/setup failures remain in local evidence. Two additional checks expose inherited limits: the static workspace source-contract assertion about the registered display name fails on both the unchanged baseline and this proposal; an extra, non-configured mypy invocation reports the same 16 host-mixin attribute errors on baseline and changed transport. Neither is reported as passing. The configured typing and native premerge checks above passed.

Visual evidence

These captures use the same synthetic media-demo artifacts. The App bundle and native backend are real; the synthetic Goal does not supply a live model run or a complete acceptance plan. Before captures show the former settings route; after captures show the Goal results route. Desktop: 1440px. Narrow: 390px. The final preview image is scrolled to the video; the separate narrow first screen is also included. No public preview service or installed-runtime qualification is claimed.

Before: production inside settings After: artifacts in the current Goal
Before desktop After desktop
Before narrow After narrow first screen
Narrow preview Acceptance read unavailable
Decoded local preview Explicit unavailable readback

Settings retain configuration only

Remaining boundary

This is an independently reviewable Goal-scoped stage. Ordinary project conversations without a Goal, generic exact-artifact feedback/resubmission/versioned acceptance, and the corresponding non-media journey still require integration with the shared Workspace delivery owner. This implementation creates no placeholder Goal. Live Ark image success, creative comparison of complete films, installed App readback and public release qualification remain unfinished. Local render/decode success does not settle those requirements.

当前交付仅覆盖 Goal 内制作与版本保护;无 Goal 工作区的通用产物反馈、修订后重提及验收仍在原工作范围内。保留既有失败和未知状态,本 PR 未进行新的收费调用、安装或发布。

…tputs

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…onses

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
@loopx-agent loopx-agent changed the title feat(content-ops): add durable cloud footage generation and composition feat(content-ops): add durable cloud image and footage intents Oct 19, 2026

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

Request changes conclusion (author-owned PR; GitHub blocks formal self-review)

REQUEST_CHANGES — #6876,审阅提交 ee380c58657121dca25792d1b38c1141ef274620。

[P2] 用经过时间约束“自动查询最多 5 分钟”,不要只数成功发起的查询。 在 ContentGeneratedFootage 的定时分支 中,pending.current 为真时先返回,只有空闲 tick 才增加 checks.current。因此一次 GET 若耗时 16 秒,十秒定时器会跳过繁忙 tick,30 次查询会延长到约十分钟。我使用当前提交重新构建页面、真实 HTTP 路由和真实扩展进程,仅把云端传输接到本地合成服务:每次 GET 延迟 16 秒且返回 running。开始约 371 秒时仍显示“停止自动查询”;18 次自动 GET 的首尾间隔约 346 秒,证明五分钟之后仍有新请求发出。手动停止有效,没有重复生成 POST。请在开启观察时保存截止时间,在忙碌分支之前检查经过时间,到期停止发起新 GET,并补充延迟请求跨越截止时间的组件或打包页面回归;不要用取消云端任务来实现停止本机观察。

动机

需要用生成图像或短镜头制作讲解视频的内容作者,以及替其准备素材的 Agent。

原来只能导入既有素材并在本机合成;生成请求若在调用方中断后丢失结果,作者必须另行核对任务与文件,贸然重发还可能产生重复付费。

本次把生成意图、已有云任务或已收到的图像响应保存在本机,并让作者通过现有内容制作页面继续保存和下载;这条机械恢复链路已有独立验证,但自动查询的五分钟时限仍未兑现。

本 PR 不授予发布、借用其他账户凭据或重新提交未知付费请求的权限,也不以解码成功证明画面创意、教学效果或实际账单。

真实 Seedream 图像产出及其后续成片、创意与账单验收仍待原素材任务完成;当前合成响应验证只证明产品链路与恢复,不替代这些验收。

改动思路

复用现有 content-ops 与扩展生命周期、把准入和状态留在 TypeScript、把云端传输和媒体解码留在可选 provider,责任划分成立;浏览器自动查询只是可停止的观察策略,不能把次数预算冒充经过时间。

当前 PR 的可交付边界是私有生成意图、不可重复提交的恢复与现有页面的文件读回;先在本 PR 修正查询截止时间,真实生图及创意和账单验收保留在已有素材任务。

入口仍是 content-ops image/generation 与 Goal 的内容制作面板。Agent 先准备提示词、来源摘要和估价,作者在已有作用域内执行一次生成并读回文件,不必再在浏览器填写 JSON、来源目录、hash 或凭据。新增的有成本操作保留明确上传与费用说明;已有完整意图之后没有额外的实现细节确认轮次。设置导航是发现入口,不是一次新的模型调用授权。

两种请求不能硬合并成一个远程作业:视频保存真实 task ID,再 GET 原任务和下载;同步图像没有云端 task ID,恢复只读已保存的 HTTP 响应。下表是本提交的源码流程说明,实际执行证据见后文。

触发 持久化与效果 后续恢复
准备意图 校验摘要、来源、尺寸与报价,无网络效果 读取同一私有意图
提交视频 先记录 submitting,再保存 provider 返回的 task ID 查询同一任务,成功后下载并解码
提交图像 先记录 submitting,再保存原始响应、摘要与解释后的缓存 collect 只读本机响应,不重发 POST
结果未知或文件损坏 保留原状态和来源;损坏文件禁止下载 已知响应可修复;无响应先核对外部结果

具体改动

已完整检查 36 个文件、+3127/-58,包括 CLI/目录、两个 TypeScript 准入与转换 owner、Python 私有 I/O、可选 provider、真实 HTTP 路由、两个页面和共享样式、文档及九张提交内截图。截图中的图像 fixture 已标注为本机合成示例;较早视频页面截图属于前一视频提交,未据此推断当前全部路径通过。

关键代码讲解

  • validateGenerationPlan / transitionGeneration 绑定模型、首尾帧、定价时间与不可变摘要;acknowledge 只代表已提交,observe 校验同一 task,collect 另行验证实际媒体。远端成功与本机 ready 没有混为一谈。
  • GenerationWorkspace.operate 在独占锁中先保存提交状态,通过已有扩展 owner 执行一次效果,随后检查文件 bytes/hash。重复 submit 不产生第二个任务,取消分支保留 Ark 记录,避免 GET/DELETE 竞态误删已完成结果。
  • validateImagePlan / transitionImage 是图像局部状态:单张图、精确尺寸、独立图像凭据和原始文件摘要。它不捏造远程 job,也不从 watermark 请求或机械解码推断创意通过。
  • 图像 provider 的 api 与 collect 在解释之前保存原始 HTTP 字节;collect 校验响应绑定、完整 PNG 解码和实际尺寸。独立 CLI 测试验证损坏文件可在移除图像凭据后从缓存恢复,原始 SHA 不变,网络 POST 数没有增加。
  • ContentGeneratedFootage 与图像页面复用原工作台、Goal 及文件路由,显示不可用、未知、保存与下载。其有缺陷的观察定时分支是本次阻塞项;不需要重写后端状态机才能修好。

规范映射先读取了改动前的 docs/reference/protocols/content-ops-media-v0.md,不可变规范版本为 907ede6e1faff1fab0f2f765dde4358c69c7b7ff,没有用本 PR 新增的文案倒推旧验收:

  • Plan and source scope:implemented。 原本机计划的摘要与源文件约束保留;抽出的 snapshot_sources 沿用原校验。五组相同生产 CLI 输入在基线和 head 的准备、列表、读取、缺 provider 和错误 revision 分支一致,只归一化时间与临时 fixture 路径。
  • Execution and readback:implemented。 真实扩展渲染、重复效果拦截、解码、损坏字节恢复和下载均执行。当前核心还通过已安装的基线 0.2 provider 渲染了 36 帧本机视频;新云端路径需要新增 permission,旧 provider 不自动获得它。
  • HTTP boundary and failure behavior:implemented。 真实 loopback 请求拒绝其他 Goal、旧或布尔 revision、私有 path、混合媒体 ID、外部 Origin 和原始响应文件;Range 下载返回 PNG 字节且为 no-store,页面下载的图像和视频均与核心记录 hash 相等。
  • Blender shot and project readback:out_of_scope。 Blender 准入、builder、重开项目和既有协议未改变;原有普通本机渲染已实际回归。没有运行 Blender 可选专项,也没有把这一跳过写成通过。

对主干的风险

新状态属于内容生成局部意图,不能扩张为 Goal、Todo、活跃 Agent 或内容发布状态。provider 明示的价格只是名义估算,不能声称硬账单上限;图像凭据与视频凭据分离,读取、页面加载和普通合成都没有获得自动生成功能。CLI 和页面中的新增能力描述是已披露的可用性说明,真正外部效果仍由扩展、permission、凭据和显式动作共同约束。

独立验证结果:30 项 Python 通过、1 项 Blender opt-in 跳过;18 项 TypeScript 通过;16 项实际 CLI/HTTP/provider 观察通过;五组基线/head 生产 CLI 对比一致;types、CSS 检查和打包通过;标准 premerge 的 6 个直接检查、10 个目录检查、8 个风险 smoke 与 1 个公开边界检查通过。首轮真实渲染因临时环境缺少可选 FFmpeg 包失败,按现有文档加载 source provider 后重新通过;另一次旧 provider 验证使用了已退出的临时环境,改用持久的隔离环境后通过,均未归因成 PR 缺陷。

1440px 桌面与 390px 页面已使用实际打包代码和真实 backend 查看;完整检查了图像未知请求没有重提按钮、已保存文件下载、扩展禁用后保留草稿并禁用生成。图像与视频下载都做了 hash 对照,视频为实际 FFmpeg 完整解码的 120 帧、5 秒合成媒体。云端传输使用合成服务,所以这些证据不证明 Ark 账户、真实模型产出、文字和水印质量、原作者那次未知请求的用量或账单;评审没有重试任何真实付费请求。

语义与 CI 对齐

当前新增协议、provider 指南及按钮约定观察“最多五分钟”,但定时器实际约束的是空闲时发起请求的次数。慢响应反例在全部现有检查通过时仍成立,属于新增调用方契约未兑现。最小修复是独立的经过时间截止条件,并保留显式手动查询和停止行为。请对实际组件运行延迟响应跨过 300 秒的回归,断言截止后没有新 GET,并验证 terminal、切换 Goal/镜头、手动停止仍清理定时器。本次遵守 wait_for_ci=false,没有读取、轮询或等待 CI;本地检查通过也不能替代这个反例。

我的整体评价

交付判断为 justified_increment:意图、私有结果与后续恢复已贯穿真实 CLI、扩展、HTTP 和页面,是可用的增量;真实图片及创意验收仍按前述边界留待完成。long_horizon=improved:中断后可接着读回已保存响应和原任务,重复提交受阻;user_experience=regression:承诺五分钟结束的自动观察会继续,需要作者额外手动停止。先修复本 PR 的截止时间再复审,不能以绿色检查或已保存字节代替这一用户行为。

规模判断是 proportionate,已有 content-ops、文件路由和扩展生命周期得到复用;提取 snapshot_sources 是有实际调用方和默认路径对照的同域简化。图像同步响应与视频 task 语义有实质差别,不宜为了少几个类再造通用 job 框架。保留旧本机 renderer 协议和持久记录有独立升级边界;当前没有必须迁移旧数据的新版本分支。新增 schema 与状态词汇保持内容能力局部,未新增通用控制面义务;语义目录检查通过,时间属性仍需上述专门回归。

该精确提交的结论是 REQUEST_CHANGES,阻塞项只有上述 P2 时间上限。没有执行合并、安装本机生产版本、清退他人 review 或真实云端生成。

English verdict: REQUEST_CHANGES — ee380c58657121dca25792d1b38c1141ef274620. The packaged footage watcher exceeds its advertised five-minute limit because busy ticks do not count; a 16-second synthetic GET reproduced new queries after 300 seconds and an active watcher at 371 seconds. Persist an elapsed-time deadline and add delayed-response regression coverage. Core/provider/HTTP recovery, base/head local-render parity, types/build and premerge checks passed; live image quality and billing remain unverified.

Comment on lines +63 to +65
if (pending.current) return;
if (++checks.current > 30) {setWatch(false); return;}
void operate("check");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Bound automatic checks by elapsed time. The pending guard skips counter increments while a GET is in flight, so 30 requests are not a five-minute window. With real packaged UI/backend/provider processes and a synthetic 16-second GET, automatic requests continued beyond 300 seconds and the watcher remained enabled at 371 seconds. Record a watch-start deadline and check expiry before the pending guard/new GET; add delayed-response coverage crossing the deadline. Manual local stop must remain distinct from cancelling or deleting the cloud task.

@songoow songoow left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; OpenAI GPT-6 Astra; provider OpenAI; self_reported.

Reviewed head: ee380c58657121dca25792d1b38c1141ef274620

动机

本 PR 为已有 Content-Ops 增加可选云端镜头与单张图像生成,供 Agent 准备意图、用户检查后提交,再把已验证的本机文件用于已有成片流程。现有本机 renderer 不能提供这类生成素材;付费请求一旦发出,又不能用普通重试掩盖提交结果未知。因此,主要受益者是从 Goal 工作台生成和收集素材的用户,以及在主进程中断后恢复原请求的 Agent。

期望的前后变化是:从只有本机合成,变为有明确提交边界、持久 journal 和本机收集凭证的可选生成;原有成片仍走同一 workspace 和扩展生命周期。长任务的状态、回执丢失和文件损坏各有不同恢复方式,避免把“云端成功”直接显示成“本机可下载”。非目标包括自动发布、自动接受创意质量、把名义估价当作实际账单上限,以及以生成图片证明产品执行轨迹。

改动思路

主体分工合理:共享 TypeScript owner 校验不可变计划、来源、报价及状态转换;Python workspace 负责锁、快照和本机状态;可选 provider 负责一次有界网络效果和原始回执;工作台展示同一 Goal 下的草稿、操作与固定文件下载。图像的同步响应缓存与视频的异步 task journal 分开处理,符合两类外部效果不同的恢复条件。

这次增量没有新建调度器或发布状态 owner,云端功能通过扩展与独立的 content_media_generate permission 进入。这个边界值得保留;当前需要修复的是两个局部实现与既有用户承诺不一致的问题,不需要重构整套媒体架构:自动查询的时间预算必须按经过时间限制,持久化 helper 必须尊重支持平台的文件系统原语。

具体改动

关键代码讲解

  • GenerationWorkspace.operate(loopx/capabilities/content_ops/generation.py):在锁内检查 revision,并在 provider POST 前保存 submitting;再次 submit 不会静默生成新的付费任务。查询和收集沿原 task 的身份推进,只有本机文件哈希通过后才变为 ready。这保留了提交结果不明确时的人工核对责任。
  • ImageWorkspace.operate(loopx/capabilities/content_ops/images.py):同步生图没有虚构 task ID 或轮询协议,未知结果只能从已保存响应恢复。collect 复用原始字节,不重新发出付费请求;已接受图像损坏后的修复还必须保持原 SHA 身份。
  • provider save / operate(packages/loopx-media/src/loopx_media/generation.py 和 images.py):journal 在网络发送前写入,视频回执保存 task ID,图像保存响应与摘要。这是正确的效果顺序,但新 helper 的无条件目录 os.open 在 Windows 上会使这个顺序停在第一次发送前。
  • ContentGeneratedFootage(apps/presentation/dashboard/src/features/personal-workspace/content-generated-footage.tsx):提交、原任务查询、收集和停止本机查询都调用既有 HTTP owner;epoch/read sequence 防止旧 Goal 响应覆盖新读回。然而,自动检查的上限用请求次数表达,没有实现 UI 和协议所说的五分钟。

P2:自动查询会超过承诺的五分钟。 content-generated-footage.tsx:63–65 在 pending.current 为真时直接返回,然后才递增 checks.current。这使“30 次”变成最多约 30 个完成后的发起机会,不是从启用开始的五分钟。用当前文件中原样提取的 interval 回调、10 秒 tick 和 16 秒请求完成时间做确定性时钟验证,30 次请求在第 10–590 秒发起,watcher 到第 610 秒才关闭;承诺的 300 秒之后仍发起了 15 次查询。它也与 docs/reference/protocols/content-ops-media-v0.md 和 provider guide 的五分钟描述不符。已有同 head 的 review 指出了此问题;本次复查确认相关代码仍未修复。最小修复是在开始查询时记录单调时钟截止时间,且在忙碌检查之前判断截止;重新启用时重置期限。补一条查询耗时超过 tick 的回归测试,验证 300 秒后不再发起新请求。

P2:Windows 的首次生成会在 journal 写入后、POST 之前失败。 两个新 provider 的 save 都在原子 rename 后直接 os.open(path.parent, os.O_RDONLY) 并 fsync 目录。Windows CRT 的 _open / _wopen 对目录路径返回 EACCES,因此这一调用不是可移植的持久化操作;参见 Microsoft 官方返回值说明。generation.operate 与 images.operate 首次 save(journal, saved) 均在调用 API 前且在请求异常处理之外,core 已保存的 submitting 随后落到未知结果,journal 又会拒绝重复提交。结果是用户连一次请求都没有发出,却被要求核对不可恢复的请求。

对此执行了两份当前 provider 的实际 operate,仅对目录 os.open 注入上述 Windows 原语的 PermissionError(errno=13):两者均保存了 state="sending",API 调用数为 0,第二次 submit 被现有 journal 拒绝。这个验证在 Linux 上执行,证明代码对该平台错误的处理路径;没有声称运行了原生 Windows。现有 loopx/file_lock.py::write_bytes_atomic 已把目录 fsync 限于 POSIX。新 provider 可采用同样的平台条件或包内等价 helper,保留文件 fsync 和原子替换,并覆盖两种 provider 的 Windows 提交路径。

对主干的风险

阅读范围包括所有生产改动、增加的 Python/TypeScript 测试、HTTP action/file 分派、扩展 manifest、CLI、两组工作台组件和文档。正常成片仍按 content_media_write binding 进入;生成使用额外 permission,旧 provider 继续本机 render,生成不可用时保留草稿。文件路由维持固定文件名和 Goal 范围,浏览器读回不暴露 credential、私有路径或临时下载 URL。名义估价、服务报告用量和真实账单也没有被合并成同一个事实。

较强的失败场景是外部效果前后的中断:代码已明确禁止自动 paid retry,并保留原 task 或原响应作为恢复证据。上述 Windows 问题说明持久化错误也必须区分“没有发送”与“可能发送”;上述 watcher 问题则说明短操作 timeout 不等于整段自动行为的截止时间。两个问题均可在各自现有 owner 局部修复。此次未重新运行付费云生成、完整打包 UI 或原生 Windows 环境;截图和作者历史测试不作为我已执行的验证。依本次要求,结论以代码与上述两个离线反例为依据,不以 CI 作为前提。

我的整体评价

REQUEST_CHANGES。持久提交意图、原效果恢复、本机字节验证和可选扩展分工能够支持这项增量,但当前 head 仍有两个可定位、可修复的 P2。请先实现真实的五分钟查询截止,并使两个 provider 的 journal 持久化在 Windows 上可用;修复后只需针对这两个边界复核,不需要重复付费生成来证明相同的本机逻辑。尚未完成的原生 Windows、打包 UI 和外部账单验证应继续按实际证据披露。

English verdict: REQUEST_CHANGES at ee380c58657121dca25792d1b38c1141ef274620. Two P2 issues are confirmed from the current source: the promised five-minute watcher can issue requests for roughly ten minutes when checks take 16 seconds, and unconditional directory opens in both provider journal writers fail on Windows before any generation request is sent. Validation used the exact interval callback with a deterministic clock and the actual provider entrypoints with the documented Windows directory error injected. No live paid request or native Windows run is claimed.

Comment on lines +63 to +65
if (pending.current) return;
if (++checks.current > 30) {setWatch(false); return;}
void operate("check");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 用经过时间限制自动查询,而不是只计非忙碌 tick

pending.current 为真时在计数前返回,所以 30 × 10s 并不能实现 UI/协议承诺的五分钟。当前文件中原样提取的回调配 16 秒请求、10 秒 tick,可确定性复现请求发起于 t=10,30,…,590 秒,watcher 到 t=610 秒才关闭,t>300 秒还有 15 次请求。已有同 head review 的问题仍在。请在启用时保存单调时钟截止时间,并在 pending 判断之前检查;补慢查询的回归测试,保证截止后不再发起请求。该验证使用精确回调与模拟时钟,不声称重新跑了实时浏览器。

output.flush()
os.fsync(output.fileno())
temp.replace(path)
fd = os.open(path.parent, os.O_RDONLY)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 让两个 provider 的 journal 目录同步兼容 Windows

Windows CRT _open/_wopen 对目录路径返回 EACCES,因此这里无条件 os.open(path.parent, os.O_RDONLY) 会在每次首次提交时失败;images.py::save 也有相同调用。两个 operate 都先在请求 try/except 外写 journal,然后才调用 API,而 core 此时已经持久化 submitting,最终留下 unknown/sending,重复 submit 又因 journal 已存在而被拒绝。我执行了两份当前 operate,仅注入官方 Windows 目录 open 的 PermissionError(errno13):两者均持久化 sending、API 调用数 0,重复提交被拒绝。测试在 Linux 上执行,未宣称原生 Windows 运行。请采用现有 file_lock.write_bytes_atomic 的 POSIX 目录 fsync 条件或等价平台实现,同时保留文件 fsync 与原子 rename,并覆盖 video/image 两条提交路径。原语依据:https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/open-wopen?view=msvc-170 。

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…versions

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
@loopx-agent loopx-agent changed the title feat(content-ops): add durable cloud image and footage intents feat(content-ops): add media providers and Goal artifact production Oct 20, 2026
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

Request changes conclusion (author-owned PR; GitHub blocks formal self-review)

Exact head: fbc71e7728cc24b9d376695296bfbdd9bccbc484; immutable merge base: 82b1a8c72159dedcbca3a3b69fac2c7eaad3192d; actual PR base: main.

两项 P2 仍阻塞:自动观察超过公开的五分钟截止;Windows 目录打开错误使两个 provider 在发出 API 前卡入 sending。 本轮对当前提交重新执行了真实打包页面与 provider 反例,详见风险与最小修复。

动机

希望在当前 Goal 中准备素材、生成镜头或图像、制作成片并把结果带回原对话的用户及其 Agent。此前本机成片与 Goal 产出入口分离,生成素材也不能共用这份来源与作品记录;当前把准备好的作品放入成果页,复用原对话准备和原 Goal 验收,避免用户重新填写 JSON、来源路径或另建审批。真实构建的成果页可以读取本机成片、预览并下载绑定版本的文件,返回同一对话会聚焦输入框且不发送消息;已保存生成响应可以在不重新提交的情况下恢复,但自动查询截止和 Windows 首次提交仍有下述缺陷。本轮只验证隔离的真实 Core、扩展、HTTP 与打包界面,外部服务使用合成传输;不声称真实云端生成、创意质量、最终费用、正式安装或无 Goal Workspace 的完整产品资格。

先按当前 PR frame 和旧 review 检查,再审完整 head:新增成果页、固定版本下载、停止观察与旧图片响应恢复均有实际价值,不能因还有更大 Workspace 目标就否定这个可独立交付的 Goal 阶段,也不能把两个仍在当前代码中的旧缺陷当作已解决。

改动思路

扩展最近的 content-ops 与既有 TypeScript 准备/投影 owner,Python 留在文件和专用 provider IO;Goal 作品放回成果页,并复用现有验收与原对话,而不是增加独立能力、付费审批状态或第二套发布生命周期。本次审查全部 Goal 素材/作品生产切片和共享文件入口;通用无 Goal Workspace、版本化反馈的完整旅程、真实云端成果与费用资格仍保留既有后继边界,不作为这个有用 Goal 增量必须同时完成的前提。Agent 使用已有上下文准备提示词、来源、哈希和时序;用户从当前成果读回,不必手填 provider 协议。生成意图是用户要求对应的不可变输入,provider journal 记录外部效果的不确定性,页面状态由既有记录投影;这些都不能代替授权、创意验收、费用账单或 Goal 完成。

真实任务分支 当前处理 当前资格
旧本机成片,无生成扩展 原 prepare/list/inspect 与不可用恢复 五组完整基础/当前输出一致
新图片响应丢失但字节已保存 读取本机响应并重新解码,不重发 实际 CLI/扩展/HTTP 合成传输通过
未知图片响应、无可解析结果 保留未知,不当作云任务查询或重发 页面和实际 API 调用数核验通过
手动“最多五分钟”观察慢任务 请求未完成时跳过 tick,不推进次数 超过 300 秒仍发新查询,P2
Windows 第一次 provider journal 写入 rename 后仍无条件打开目录 尚未调用 API 就卡入 sending,P2

具体改动

规范依据是变更前的 docs/reference/protocols/content-ops-media-v0.md,spec_revision 82b1a8c72159dedcbca3a3b69fac2c7eaad3192d;新云生成分支还必须兑现当前页面和 README 已公开的有界观察、明确失败与保留原请求语义。原文未编号,映射为:nearest_typed_owner implemented,保留 content-ops 与 TS/专用 Python 分工;immutable_source_and_version implemented,来源快照、摘要、当前修订与固定下载版本;ordinary_and_provider_isolation implemented,旧本地成片、禁用/旧扩展和只读不产生付费请求;usable_goal_readback implemented,成果页预览、下载、原对话返回和验收不可用提示;truthful_bounded_recovery not_met,新的观察期限与 provider 提交失败分支仍有两个下述具体缺口。这里沿用旧规范的有界执行和明确恢复要求,不把未来生成服务的创意、账单或无 Goal Workspace 目标升级为本轮的已完成义务。

关键代码讲解

  • ContentProductionResults 在当前 Goal 成果里组合成片、镜头、图像及原有验收组件;返回动作保留 Goal 与对话,只聚焦输入,不自动发消息。旧 report 错误、加载和非空作品仍显示,空态折叠不把不可读当成已验收。
  • GenerationWorkspace.operate 和 ImageWorkspace.operate 在调用扩展前保留提交意图,图片仅恢复本地缓存、视频仅检查原 task;严格修订、摘要与来源边界沿用最近 owner。未知返回绝不自动创建第二个付费请求。
  • 两个 provider 的 save 保存外部效果 journal;图片 对应实现 同样先 fsync 文件并替换,再无条件打开目录。这保住了重发约束,却遗漏平台边界,产生下述“没有发出也不能重试”的坏状态。
  • ContentGeneratedFootage 的观察 effect 每 10 秒触发一次,但 pending 时先返回,之后才累加次数。次数限制不能兑现真实经过时间上限;切换模式/手动停止清理成功并不消除这个漏洞。
  • content-media/file 将 composition/generation/image 身份互斥,并可同时绑定 revision 与 SHA256;实际打开文件描述符校验后才按 Range 返回。旧未固定版本的调用仍兼容,失效版本、错哈希、私有原响应和跨 Goal 均拒绝。

全 48 路径 +3372/-104 已按生产 owner、UI、文档、验证与截图审读。生产约束主要在两个 TS 准备/投影模块、两个 Core 文件工作区及两个专用 provider;九个 UI 文件复用现有 Goal/设置/验收组件,五个测试文件覆盖新分支。文档和截图说明打包/合成边界,不能证明真实生成质量。普通 snapshot_sources 的抽取复用了同一来源校验;无需为这两个缺陷新增通用框架,目录持久化应在可独立安装的包内做一次平台感知复用。

对主干的风险

[P2] 五分钟自动查询仍按完成请求次数计数。 当前真实构建页面连接真实 Core、安装的当前扩展和仅网络层合成的服务,每次 GET 16 秒。开始后 368.594 秒页面仍显示“停止自动查询”,服务端还记录了 317.395、337.425、357.518 秒才发起的新 GET。代码在 pending 时跳过计数,因此慢响应使 30 次请求跨越远大于五分钟的墙钟时间。这是当前 head 的实际重现,不使用虚拟时间,也不把不同 macOS 单调时钟直接相减。请保存开始时的单调时间/截止值,在 pending 判断前检查到期并停止后续查询,同时保留手动停止、任务切换和卸载清理;停止观察不等于取消云任务或费用。手动停止后的约 582 秒确实没有新 GET,说明取消清理正常,但不证明自动截止正常。

[P2] 两个 provider 的目录 fsync 让 Windows 首次提交在 API 前卡死。 Microsoft _open 文档 将目录路径列为 EACCES 情况。对两个当前 operate 入口只注入这个目录打开错误、保留真实文件 flush/replace,均得到 PermissionError、journal sending、API 调用 0 次,第二次提交又被原防重发拒绝;相同输入在普通文件系统对照分别只 POST 一次并成功。这是 macOS 上对文档所述 Windows 原语的受控复现,不是原生 Windows 执行资格。请像既有平台感知原子写入 owner 一样区分目录 fsync 支持,保留文件 fsync、原子替换与不重复提交,不可通过删除 journal 或放松未知请求保护来恢复。

语义与 CI 对齐

两处分别违反当前公开的有界观察和“明确失败、保留可恢复输入”路径。现有 loopx/file_lock.py 已将目录 fsync 限于 POSIX;独立媒体包可复用等价包内 helper,无须反向依赖整套 Core。生成计划/状态留在 content-ops 本地域,扩展 manifest 明确新增 generate permission,不能因已经安装旧 renderer 就默认为已授权云生成。当前 Core 配原 0.2.0 包真实渲染 36 帧成功,文件哈希与当前本机输出一致,两个新生成入口明确为 extension_incompatible;普通未安装 provider 的五组完整 CLI 结果也一致。没有默默把新模型、轮询或付费调用施加给旧本地成片。

本轮 29 项 Python 通过、2 项明确 opt-in 跳过,新计划及旧本地 TS 共 18 项通过,包/架构要求 7 项通过;打包 Chat build/校验、TS typecheck、Ruff、配置的 17 个 mypy 文件及标准 premerge 6 direct +19 selected 全通过。另执行了 16 项实际 CLI/扩展/HTTP检查、真实本地编码与浏览器桌面/390 窄屏读回;未知响应恢复无需再发送,foreign origin/Goal/修订/哈希/私有路径拒绝,返回对话不发送。以上绿项未覆盖上述截止和 Windows 前置写入反例,不能据此清掉阻塞。

我的整体评价

当前 REQUEST_CHANGES,delivery verdict 为 not_yet_proven。long_horizon 为 not_yet_proven:不重发未知效果的主路径成立,但慢查询超时不退出、无 API 的平台失败却永久保留 sending,会增加等待与人工恢复成本。user_experience 为 not_yet_proven:新的 Goal 成果和原对话返回确实减少重填信息,但可见的五分钟承诺和跨平台提交恢复尚不能兑现;最低修复就是上面两项,不要求重做已验证的成果页。扩展最近的 content-ops 与既有 TypeScript 准备/投影 owner,Python 留在文件和专用 provider IO;Goal 作品放回成果页,并复用现有验收与原对话,而不是增加独立能力、付费审批状态或第二套发布生命周期。本次审查全部 Goal 素材/作品生产切片和共享文件入口;通用无 Goal Workspace、版本化反馈的完整旅程、真实云端成果与费用资格仍保留既有后继边界,不作为这个有用 Goal 增量必须同时完成的前提。两个 provider 与旧 review 的对应文件字节相同,镜头文件只有版本 URL 变化;本结论仍以本轮当前 head 的独立执行为依据。没有 dismiss 旧 review,也没有合并、正式安装或付费生成。修复后重跑慢响应真实页面、Windows 前置错误/普通平台对照及当前 native suites/premerge,再检查新精确 head。

English verdict: REQUEST_CHANGES — At fbc71e7728cc24b9d376695296bfbdd9bccbc484, both prior P2s remain reproducible: slow requests keep the five-minute watcher running past its deadline, and directory-open failure leaves both providers in sending before any API call. The packaged Goal journey and ordinary local compatibility are verified within synthetic-network scope; fix these two paths and revalidate the new head.

useEffect(() => {
if (!watch || !active) return;
const timer = setInterval(() => {
if (pending.current) return;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 按经过时间截止自动查询。pending 时这里先返回,checks 不增加;当前打包页面连接真实 Core/provider、每次 GET 延迟 16 秒的合成传输,368.594 秒仍在观察,并在 317.395/337.425/357.518 秒发出新 GET。请开始时保存单调时钟截止值,在 pending 判断前检查到期;保留手动停止、切换与卸载清理,停止观察不能取消云任务或费用。补充超过 300 秒的慢响应回归。

output.flush()
os.fsync(output.fileno())
temp.replace(path)
fd = os.open(path.parent, os.O_RDONLY)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 为目录 fsync 保留支持平台边界;images.py:35 同样受影响。Windows CRT 对目录路径的 _open 返回 EACCES;对两个当前 operate 只注入这个目录错误、保留真实 file fsync/replace,均得到 API 0 次、journal sending,第二次提交又被拒;普通文件系统对照各只 POST 一次成功。请采用 POSIX 条件或平台感知的包内原子写入 helper,保留文件 fsync、replace 与防重发语义。这里是文档原语的受控复现,未声称原生 Windows 执行。

@Duang777 Duang777 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: actor_kind=model_agent; model=Codex; provider=OpenAI; declaration_source=self_reported

动机

在 Goal 内容工作台生成图像或镜头的用户,以及替用户准备生成意图并在中断后恢复原任务的 Agent,会直接经过本 PR 新增的提交、查询、保存和下载链路。

修改前只有本机合成。修改后,用户可以显式提交一次可选云端生成并读回固定文件,但当前 head 在慢查询下五分钟后仍会发起 GET,在 Windows 上又会在首次 POST 前因目录 open 失败而留下不可恢复的 sending 状态。

既有评审证明了显式提交、原任务恢复、固定文件校验和本机合成兼容性;当前 head 只新增下载 URL 的 revision/sha256,未改变两个反例,因此这些缺陷仍能阻止完整交付。

本次整理不证明真实 Ark 画面质量、实际账单、原生 Windows 全流程、安装后产品资格或发布权限,也不重新运行长测试。

仍需修复经过时间截止和 Windows journal 持久化,再针对这两个边界复核;真实生图质量、账单与发布验收仍在本 PR 声明的后续边界内。

改动思路

入口仍是 Agent 面向的 content-ops 命令和 Goal 的内容成果工作台。TypeScript 校验不可变计划和状态转换,Python workspace 持有锁、核心记录与文件校验,可选 provider 只负责有界网络和私有文件 I/O。显式 submit 先保存 core submitting,再写 provider journal,随后最多发出一次 POST;视频保存 task ID 后查询原任务,图像保存同步原始响应后本机 collect。固定文件路由把 Goal、revision、SHA256 和固定文件名绑定起来,发布与 Goal 验收仍由原 owner 管理。

生成准入、核心状态、provider I/O 与 Goal 投影已放在现有 owner 中;当前不需要重构整套媒体架构,只需在各自 owner 内修复时间截止和平台持久化。

当前 PR 应交付可选生成、同一外部效果恢复和固定文件读回,并在合入前关闭两个 P2;真实付费产出、创意质量、账单和发布资格继续保持未验证。

正向路径是“准备意图 -> 显式提交 -> 保存 task 或响应 -> 查询/收集 -> 校验本机字节 -> Goal 下载”。负向路径不得把本机观察停止当成云端取消,也不得把 POST 前的本机持久化错误当成外部请求未知。

具体改动

完整 base 到 head 变更为 48 个文件、+3372/-104。生产、配置和 UI 为 +2005/-87,测试为 +1011,公开文档为 +356/-17,另有 16 张二进制截图。当前 exact head 为 fbc71e7728cc24b9d376695296bfbdd9bccbc484;相对旧评审 head ee380c58657121dca25792d1b38c1141ef274620,三个 blocker 文件中只有 footage 的文件 URL 增加 revision 和 SHA256。

Spec basis: docs/reference/protocols/content-ops-media-v0.md @ revision 82b1a8c72159dedcbca3a3b69fac2c7eaad3192d。BASE-OWNER implemented:新状态继续由 content-ops TypeScript owner 管理,Python 与扩展保留文件和 provider I/O。BASE-READBACK implemented:core 校验固定文件的 bytes/SHA256,当前 head 又把 footage URL 固定到 revision/hash。BASE-FAILURE not_met:Windows 的目录 open 在 POST 前失败,却留下 sending 并阻止重试。BASE-FUTURE-GENERATION implemented:生成能力复用最近的 content owner,且需要扩展、permission、credential 和显式动作。

关键代码讲解

  • transitionGeneration(content_media_generation.ts:84)区分 acknowledge、observe、collect、repair 与 unknown,不从云端成功直接推断本机 ready。
  • GenerationWorkspace.operate(generation.py:165)在锁内校验 revision 和 plan digest,先保存 submitting,再调用一次 provider,并在 collect 后逐文件校验 hash。
  • footage provider operate(packages/loopx-media/src/loopx_media/generation.py:312)保存 task journal 后执行一次 POST;其 save 在 :42 无条件打开目录,是第二个 P2 的一半。
  • image provider operate(packages/loopx-media/src/loopx_media/images.py:257)保存同步响应供 collect 恢复,不虚构远端 task;其 save 在 :35 有相同 Windows 问题。
  • ContentGeneratedFootage(content-generated-footage.tsx:16)承载显式提交、原任务查询、自动观察和固定文件播放;:60-68 的 pending-first 计数仍没有实现经过时间上限。

对主干的风险

[P2,高置信度] apps/presentation/dashboard/src/features/personal-workspace/content-generated-footage.tsx:60-68:自动查询先在 pending.current 为真时返回,之后才增加 checks.current。10 秒 tick 配 16 秒 GET 时,请求可在 300 秒后继续发起,已有真实打包路径观察在约 371 秒仍显示停止按钮。最小修复是在开启时保存单调时钟 deadline,并在 pending 判断和新 GET 前检查;补跨过 300 秒的慢响应回归,同时保留 terminal、Goal/制作 tab 切换和手动停止清理。

[P2,高置信度] packages/loopx-media/src/loopx_media/generation.py:34-46 与 images.py:27-39:两个 save 都在 atomic replace 后无条件 os.open(path.parent, os.O_RDONLY)。Windows 目录 open 返回 EACCES,而两个 operate 又在 POST 前、请求异常处理外调用它。现有原语注入结果是 journal 为 sending、API 调用数 0、再次 submit 被拒绝。请复用 loopx/file_lock.py 的 POSIX-only 目录 fsync 条件或等价实现,保留文件 fsync 与原子替换,并覆盖 video/image 两条提交路径。

语义与 CI 对齐

当前 head 的 Stage2C 与 windows-powershell 已通过,但它们没有注入 Windows 目录 open 失败,也没有让 16 秒 GET 跨越 300 秒 deadline。相关 CI 不能推翻两个反例。语义上,本 PR 复用并扩展现有 content-ops media vocabulary,没有创建第二个通用 capability;扩展本身合理,但五分钟承诺和 no-duplicate recovery 在这两个边界上没有兑现,因此 semantic_alignment=violated。

旧 head 到当前 head 的失效检查只发现文件 URL pinning;轮询 lines 60-68、generation.py 与 images.py 的 save/operate 均未改变。无需重跑付费生成即可确认旧证据仍适用。

我的整体评价

这是一个 justified_increment:显式生成意图、同一外部效果恢复、真实文件校验和 Goal 读回形成了可独立评审的增量,架构归属也合理。Default-off isolation 与 authority semantics 保持正确,普通本机合成、发布权和 Goal 验收没有被云端 provider 接管。

但 long_horizon 和 user_experience 都是 regression。Windows 路径会把“没有发送”写成不可恢复的未知效果;慢查询则违反五分钟用户承诺。代码规模与问题大体相称,兼容性需要保留,不过两个 provider 应共享平台安全的 journal durability 规则。Residual risk 仍包括原生 Windows、真实 Ark 产出、创意质量、实际账单和安装后资格。

结论是 REQUEST_CHANGES,绑定 exact head fbc71e7728cc24b9d376695296bfbdd9bccbc484。修复后只需复核 delayed watcher、两个 provider 的 Windows primitive,以及相关 focused suites;本评审不授予 push 或 merge 权限。

English verdict: REQUEST_CHANGES at fbc71e7728cc24b9d376695296bfbdd9bccbc484. Two P2 blockers remain unchanged from the reviewed old head: busy ticks let the advertised five-minute watcher start requests after 300 seconds, and both provider journal writers open their directory unconditionally, which fails on Windows before POST and leaves a false unrecoverable sending state. Exact-head Stage2C and windows-powershell passed but do not exercise either counterexample.

@mergify

mergify Bot commented Oct 21, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @loopx-agent.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 21, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-rebase Mergify: the pull request has merge conflicts with its base branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants