26a3a4a82a
fix: 修复上游健康巡检并同步已审查的市场引用 ( #207 )
...
Upstream health checks could select a different branch ending in `main`,
index internal skills or lowercase reference documents, treat a valid
pinned path as broken after an upstream move, and close the tracking
issue while unpinned or unknown entries remained. Resolve exact refs and
tracked filenames, compare the installed snapshot, and drive issue
decisions from structured results.
Refresh 26 reviewed pointers, pin DingTalk and Matt Pocock collections,
remove the deleted merge-conflict skill, and add the six new public
children with reviewed translations. Synchronize sidecars, observed
release facts, license evidence and external dependency disclosures.
Identical-tree Agent pins and their versions remain unchanged. The
per-entry review is recorded in
`docs/upstream-health-189-remediation.md`.
Validation: 99 unit tests pass; all nine pinned collections reproduce;
catalog/i18n and translation-freshness checks pass; online health checks
all 41 external entries with zero broken, actionable or unknown results.
Existing catalog warnings decrease from 123 to 105 with no new warning
groups. Python/workflow shell syntax and whitespace checks pass.
Closes #189 .
2026-10-05 23:13:13 +08:00
81aa0cf450
Add transparent portrait avatars for market digital employees ( #202 )
...
The catalog previously showed opaque icons or text fallbacks for the
seven digital employees, and source-pointer installs could still get
different avatars. This updates every catalog card to a 512×512
transparent portrait, bumps the listed releases, and pins the six source
repositories to commits that include the matching AgentFS avatar.
Validation: `validate_catalog_metadata.py --require-complete` (0
errors), `validate-i18n.py` (0 errors), and `translate.py --check`. All
seven WebP files have an alpha channel with fully transparent pixels and
are below 50 KB each.
2026-09-30 15:53:13 +08:00
daa95424e7
chore(invoice-organizer): 1.1.1 —— pin 到内容仓库合并提交 ( #201 )
...
三处 ref 改为内容仓库 9dd9af4b(desirecore-agent/invoice-organizer#8),latestVersion 与 release.version 改为 1.1.1。
2026-09-30 10:27:09 +08:00
4804ce364c
chore(invoice-organizer): 1.1.0 —— pin 到内容仓库合并提交 ( #200 )
...
三处 ref 改为内容仓库 3e5a530f(desirecore-agent/invoice-organizer#7),latestVersion 与 release.version 改为 1.1.0。
2026-09-30 09:55:26 +08:00
e3d3fe2eda
chore(invoice-organizer): 1.0.9 —— pin 到内容仓库合并提交 ( #199 )
...
三处 ref 改为内容仓库 fc02a348(desirecore-agent/invoice-organizer#6),latestVersion 与 release.version 改为 1.0.9。
2026-09-30 09:29:36 +08:00
d176e84a96
chore(invoice-organizer): 1.0.8 —— pin 到内容仓库合并提交 ( #198 )
...
三处 ref 改为内容仓库 0796408b(desirecore-agent/invoice-organizer#5),latestVersion 与 release.version 改为 1.0.8。
2026-09-29 23:24:46 +08:00
2ef4f9ada3
chore(invoice-organizer): 1.0.7 —— pin 到内容仓库合并提交 ( #197 )
...
三处 ref pin 到
[desirecore-agent/invoice-organizer#4 ](https://github.com/desirecore-agent/invoice-organizer/pull/4 )
的合并提交 `27c1896036e9a8988aa4d112ab7092d4dee75cda`,`latestVersion` /
`release.version` 升到 1.0.7。
Pins the invoice-organizer pointer entry to content commit `27c1896`
(1.0.7).
1.0.7 修正 1.0.6 真机验证暴露的三个行为问题:换用目录只 `add` 不设主目录导致 Plan
与增量自动化写进旧目录;模型中途自行收工;自写脚本直连邮件服务打错端口。内容仓库 PR 经独立子代理两轮审查,结论可合并。校验器 0
error。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
2026-09-29 08:09:39 -04:00
40bd2dc1cf
chore(invoice-organizer): 1.0.6 —— pin 到内容仓库合并提交 ( #196 )
...
## 改动 / Changes
- `entry.json` 与 `catalog-metadata.v1.json` 的三处 ref pin 到
[desirecore-agent/invoice-organizer#3 ](https://github.com/desirecore-agent/invoice-organizer/pull/3 )
的合并提交 `53b8a745bd008ab8be4833e28724db6b0801976f`
- `latestVersion` / `release.version` 1.0.5 → 1.0.6
Pins the invoice-organizer pointer entry to content commit `53b8a74`
(1.0.6).
## 1.0.6 内容 / What's in 1.0.6
1.0.5 在开发实例从零整理真实邮箱只入账 51 张(应约 67 张),逐封对照本地缓存后定位到:列候选直接拿
`messages/fetch`
的翻页结果(按序号窗口取会漏一类发票邮件)、按信头日期停翻、下载前按文件名预筛掉航空电子行程单、每封只下一个载体。1.0.6:
- 列候选改为两步:先把本地缓存补齐到覆盖整个时间段(Gmail 用放宽一天的服务端日期查询翻到底;IMAP 收件箱 / Outlook
每页后重数「晚于起始日期的邮件数」G,`offset` 达到 G 之后连续两页不增长才停;IMAP 自定义文件夹不走缓存),再按日期搜缓存、按
`total` 翻到底
- 候选邮件的附件全部下载,不按文件名预判、不挑载体;判定阶梯新增否定条挡住行程明细
- 旧客户端(列表响应只剩「省略 1 行」)逐级降 `limit`,读不了的记入待处理;失败计数与「判为非发票」分开
- 内容仓库 PR 经独立子代理五轮审查(高 5 / 中 12 / 低若干,全部处理),第五轮结论可合并
## 验证 / Verification
- `validate_catalog_metadata.py --require-complete`:0 error
- `agent.json` 用 dev 的 `validateAgentConfig` 实跑 `success: true`
🤖 Generated with [Claude Code](https://claude.com/claude-code )
2026-09-29 07:21:27 -04:00
149174e127
chore(invoice-organizer): 1.0.5 —— pin 到内容仓库合并提交 ( #195 )
...
## 改动 / Changes
- `entry.json` 与 `catalog-metadata.v1.json` 的三处 ref pin 到
[desirecore-agent/invoice-organizer#2 ](https://github.com/desirecore-agent/invoice-organizer/pull/2 )
的合并提交 `b2e6d69f0df2345e282ceea424afce0b9345d97f`
- `latestVersion` / `release.version` 1.0.4 → 1.0.5
- `compatibility.requirements` 的 runtime 一条补上 XML 解析与系统自带解压工具(unzip /
python3 / bsdtar),与 Agent 的 USAGE 口径一致
Pins the invoice-organizer pointer entry to content commit `b2e6d69`
(1.0.5) and syncs the runtime requirement text with the Agent's USAGE.
## 1.0.5 内容 / What's in 1.0.5
- 收下 `.zip` / `.xml` 附件:12306 把铁路电子客票打成 zip 发,此前整封邮件被跳过(真实邮箱实测漏 8 张);zip
只经技能自带的 `unpack-zip.sh` 解包,附件名与条目名不进命令行,四种模式 × 9
个用例(含命令注入、输出伪造、符号链接、加密、压缩炸弹、GBK 名、macOS 元数据)实测
- 数电票 XML 作为优先级最高的载体;「电子发票(铁路电子客票)」按 8 张真票重写并新增 D 族键表
- 识别红字发票(`InIssuType`、备注、`RedEInvoice`、负数合计),台账成对标出已红冲 / 部分红冲
- 下载失败与解不开的内层压缩包落盘到 `ledger.json#unprocessed`;附件逐个下载;不把整理流程委派给别的 Agent
- 内容仓库 PR 经独立子代理三轮审查(高 1 / 中 9 / 低若干,全部处理),第三轮结论「可以合并」
## 验证 / Verification
- `validate_catalog_metadata.py --require-complete`:0 error(agents=7)
- 新增文本无真实邮箱、税号、发票号码或公司名
🤖 Generated with [Claude Code](https://claude.com/claude-code )
2026-09-29 05:40:51 -04:00
0d0e745ef0
chore(invoice-organizer): 1.0.4 —— pin 到内容仓库合并提交 ( #191 )
...
发票整理助手内容仓库 desirecore-agent/invoice-organizer#1 已合并为 553ce87。
三处 ref 同步 pin 到该提交;latestVersion 与 release.version 升到 1.0.4;
reviewedAt 两处与各时间戳更新到 2026-09-29,保持结构化形态。
校验器 0 error,agents=7。
1.0.4:同票多载体先读 OFD、补两族 OFD 标引键名、航空电子行程单按计税依据拆分
(民航发展基金记入新增的 otherCharges)、必印字段为空不算通过且多轮只补全不清空。
2026-09-29 04:05:13 -04:00
74466d1ac9
refactor(invoice-organizer): 迁出为独立仓库,条目转真 pointer / move to standalone repo ( #140 )
...
## 背景 / Background
`invoice-organizer` 是 market 里最后一个 inline 形态的第三方 Agent。它的
`provenance.content` 是指向 market 自身的 **self-pointer**(`url=market.git`,
`path=agents/invoice-organizer`)——治理字段已经按 pointer 写,内容却还在 market 里。本 PR
让两者一致。
## 改动 / Changes
内容迁到
**[desirecore-agent/invoice-organizer](https://github.com/desirecore-agent/invoice-organizer )**
@ `5ac1e29`,条目收敛为 3 个文件(`entry.json` + `catalog-metadata.v1.json` +
`assets/avatar.webp`)。
## pointer 形态的三处强制改动
`agent.json` 直接过 `agentConfigSchema`(根级 `additionalProperties:
false`),实测改完 `validateAgentConfig` 返回 `success: true`:
- **剥离 10 个市场字段**:`category` / `tags` / `updatedAt` / `maintainer` /
`installPolicy` / `updatePolicy` / `i18n` / `persona` / `changelog` /
`contentSource`
- **`id` 从 slug 换 UUID**
- **`avatar` 从 `{t,bg,image}` 换 `{char,color}`**:原 `bg` 是 `#34C759 →
#007AFF` 的绿蓝渐变,取起点绿色
## ⚠️ 一个必须补的兜底 / A gap this migration would otherwise open
原条目只有 `USAGE.zh-CN.md` 和 `USAGE.en-US.md`,**没有无后缀版本**。
pointer 形态下这两份都抓不到:
1. `fetchAgentRepoData` 总是抓无后缀 `USAGE.md`;`USAGE.<locale>.md` 的探测列表由
`extractDeclaredLocales` 从**上游 agent.json 的 `i18n.locales`** 得出
2. 而 pointer 的 `agent.json` 不能有 `i18n`——`agentConfigSchema` 顶层无此键且根级
`additionalProperties: false`
3. `readAgentDetailFromPointer` 调
`resolveUsageDocFromFiles(repoData.usage, locale)` 时**不传
localeHints**,回退链只剩 `locale → 无后缀`
不处理的话,使用说明会在详情页**彻底消失**。因此上游新增无后缀 `USAGE.md` 兜底(内容同
`zh-CN`,`source_locale` 就是 zh-CN),两份 locale 版本作为事实源保留。
**代价如实记录**:en-US 用户会从看到英文版降级为看到中文版。根因在平台侧,建议后续补齐
`readAgentDetailFromPointer` 的 locale hints,届时两份 locale 版本自动生效,无需再动条目。
## 准入 / Availability
**维持 `installable`**,证据六项自检全部通过:
```
content.ref == entry.source.ref ✅ 5ac1e29d3
compliance.reviewedRef == content.ref ✅
timestamps.reviewedAt == compliance.reviewedAt ✅ 2026-09-06
availability ✅ installable
license known + licenseEvidencePath: LICENSE ✅
provenance 不再指向 market 自身 ✅
```
`LICENSE` 随内容迁到上游——与 `feishu-orchestrator` 一致,`licenseEvidencePath`
相对的是内容仓库而非条目目录。
## 已安装用户影响 / Impact on existing installs
`timestamps.releasePublishedAt` 是
2026-09-06(今天),实际安装量应为零。`provenance.content.url` 变更会影响已安装条目的 ownership
校验,但在这个时间窗内无实际影响。
## 结果 / Result
market 的 `agents/` 现在只剩官方 `desirecore` 一个 inline 条目,其余全部 pointer。
2026-09-06 14:17:21 -04:00
31e4ca4611
chore(invoice-organizer): 1.0.3 —— 重新 pin 到已合并 commit ( #144 )
...
接 #138。那个 PR 只改技能内容,刻意没动版本号与 pin——contentSource.ref 必须
指向已合并的 commit,而那个 SHA 在合并前并不存在。这是同一条两步约束的第四次
(#117/#118、#120/#121、#127/#130 之后)。
- agent.json#contentSource.ref、sidecar 的 provenance.content.ref 与
governance.compliance.reviewedRef 三处同步 pin 到 450be28
- 版本 1.0.2 → 1.0.3(agent.json 与 sidecar release.version 两处一致)
- 补 1.0.3 changelog(中英双语)
1.0.3 修的是:PDF 文本层在「两栏先输出全部标签、再输出全部取值」的版式下,
按「标签后紧跟的下一段文字」配对会把字段标签本身抽成销售方/购买方名称,且
所有形式勾稽都不会报错。新增硬校验:名称命中已知标签模式即判定提取失败,
先同页重定位、再整页视觉阅读,仍不能确定则按必填字段缺失隔离。
timestamps 三项已经是 2026-09-06,本次无需变更;结构化形态原样保持。
2026-09-06 11:24:13 -04:00
450be28a2b
fix(invoice-organizer): PDF 文本层名称字段标签污染防护 ( #138 )
...
PDF 的文本层顺序由生成它的开票系统决定,不保证等于版面阅读顺序。部分模板会把
「两栏的全部标签」连续输出完,再输出「两栏的全部取值」,按「标签后紧跟的下一段
文字」配对会把字段标签本身当成取值——一次真实批量整理里,某网约车平台开具的两张
「旅客运输服务」数电票的 sellerName 被抽成了字面的「统一社会信用代码/纳税人识别号:」。
新增「名称字段的标签污染防护(PDF 文本层)」一节:名称等于或主要由已知字段标签
组成时判定为提取失败,先在同页重新定位、再退到整页视觉阅读,仍不能确定则按技能
既定的「抽不齐必填字段就隔离」处理,不得从主题/文件名/相邻税号猜公司名。
明确标注这条只针对 PDF 文本层:OFD 页面文本按坐标重建阅读顺序、模板标签与取值
已合并到同一行,结构化来源更不依赖文本顺序,均不受此问题影响,OFD 章节未改动。
2026-09-06 10:46:46 -04:00
84df5e7056
chore(invoice-organizer): 1.0.2 —— 重新 pin 到已合并 commit / repin to merged commit ( #130 )
...
## 背景 / Background
接 [#127 ](https://github.com/desirecore/market/pull/127 )。那个 PR
只改了技能内容,**刻意没动版本号与 pin**——`contentSource.ref` 必须指向**已合并的 commit**,而那个
SHA 在合并前并不存在。先 bump 版本会让目录声称 1.0.2、却仍按指向 1.0.1 内容的 pin 去取文件,比不 bump 更糟。
这是 `#117`/`#118`、`#120`/`#121` 之后**第三次**走同一条两步约束——inline agent 声明为
`installable` 就必须 pin 到已存在的 commit,所以内容与 pin 永远得分两个 PR。
Third time through the same two-step constraint: an inline agent
declared `installable` must pin to an already-merged commit, so content
and pin can never land in one PR.
## 改动 / Changes
- `agent.json#contentSource.ref`、sidecar 的 `provenance.content.ref` 与
`governance.compliance.reviewedRef` **三处同步** pin 到 `406bc10`
- 版本 `1.0.1` → `1.0.2`(`agent.json` 与 sidecar `release.version` 两处一致)
- `reviewedAt` / `timestamps.reviewedAt` 同步到 `2026-09-06`
- 补 1.0.2 changelog(中英双语)
## 1.0.2 修了什么 / What 1.0.2 fixes
两条都来自真机对比两个版本产出的索引文件:
| 缺陷 | 现象 | 危害 |
| --- | --- | --- |
| 索引顶层结构没钉死 | 同一个 Agent 分别产出过 `{…, records:[…]}`(数组)与 `{…, invoices:{…},
suspectedDuplicates, quarantined}`(字典),`updatedAt` 也时有时无 |
台账、报告、幂等判据全部从索引派生,键名一漂,跨轮次和外部工具都对不上 |
| `emails.json` 的 key 有前缀歧义 | key 是 `<provider>:<email>:<mailId>`,而 IMAP
的 `mailId` 自带 `imap:` 前缀 → `imap:me@example.com:imap:123` | 服务端对裸 UID
也接受,所以某轮抄原值、某轮剥前缀,**同一封邮件产生两个 key,「已处理」判定失效、邮件被重复入账** |
两条的修法都是**只钉规则、不改语义**:旧形态原样沿用、不重写容器;key
用列表接口返回的原值、一个字符不改。因此**对任何已落盘的索引文件零影响**——改语义会让存量用户的已处理邮件全部失配、整个邮箱重新入账一遍。
Both fixes pin the rule without changing semantics, so existing on-disk
index files are unaffected.
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
2026-09-06 03:29:10 -04:00
406bc10bcc
fix(invoice-organizer): 钉死三个索引文件的顶层结构 / pin the index file schemas ( #127 )
...
## 问题 / Problem
真机对比同一个 Agent 两个版本产出的索引文件:
```
v1.0.0 ledger.json {schemaVersion, updatedAt, records:[…]} 数组
v1.0.1 ledger.json {schemaVersion, invoices:{…}, suspectedDuplicates, quarantined} 字典
v1.0.0 files.json {schemaVersion, updatedAt, files} emails.json {schemaVersion, updatedAt, emails, note}
v1.0.1 files.json {schemaVersion, files} emails.json {schemaVersion, emails}
```
技能原文只写了一句「`ledger.json` 发票主键 → 记录(主索引)」——**容器键名从未钉死**,于是同一个 Agent
在不同轮次产出了两种不兼容结构,`updatedAt` 也时有时无。
台账、报告、幂等判据**全部从索引派生**,键名一漂,跨轮次和外部工具就都对不上。这与已修的归档路径那条同源:**规格没说死的地方就会漂**。
The skill only said "`ledger.json` maps invoice key to record" — the
container key was never pinned, so the same agent produced two
incompatible shapes across runs. Everything (ledger spreadsheet, report,
idempotency) derives from these index files.
## 改动 / Changes
- 把三个索引文件的顶层形状写成 jsonc 范例,**容器键名与嵌套形态钉死**(`invoices` / `files` /
`emails` 都是字典,外加 `schemaVersion` 与 `updatedAt`;`ledger.json` 另有
`suspectedDuplicates` 与 `quarantined`)
- 明确 **「读到旧形态时不要重写容器」**:顶层出现 `records` 就一路沿用
`records`,只增改里面的条目。理由写进技能——容器一换,旧版写过、当前规格没覆盖的字段会**静默丢掉**,而用户毫无察觉
- 判据给成一句可照做的话:`records` → 沿用 `records`;`invoices` → 用 `invoices`;新建才用
`invoices`
## 为什么不做自动迁移 / Why no auto-migration
升级路径已实测(v1.0.0 目录被 v1.0.1 接手):Agent
**读得懂旧结构并原样保留**,台账记录数不变、幂等成立,还顺手修正了历史上错误的归档路径。既然读兼容已经成立,把容器强行规范化只会带来「丢字段」风险而没有收益。本
PR 把这个已被验证正确的行为**写成规则**,而不是引入新行为。
The upgrade path was verified in a real run: v1.0.1 reads the legacy
shape and preserves it, with zero duplicate ledger entries. This PR
codifies that verified behavior instead of introducing migration.
## 版本与 pin / Version and pin
同 #120 / #121 的两步发布约束:本 PR 只改技能内容,**不动版本号与 `contentSource.ref`**——pin
必须指向已合并的 commit,那个 SHA 在本 PR 合并前不存在。版本号、pin 与 changelog 留到后续 PR。
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com >
2026-09-06 02:57:31 -04:00
1f9e0b0a03
chore(invoice-organizer): 1.0.1 —— 重新 pin 到已合并 commit / repin to merged commit ( #121 )
...
## 背景 / Background
接 [#120 ](https://github.com/desirecore/market/pull/120 )。那个 PR
只改了技能内容,**刻意没动版本号与 pin**——`contentSource.ref` 必须指向**已合并的 commit**,而那个
SHA 在合并前并不存在。先 bump 版本会让目录声称 1.0.1、却仍按指向 1.0.0 内容的 pin 去取文件,比不 bump
更糟。这是 #117/#118 那次两步发布的同一条约束。
Follow-up to #120 , which deliberately left the version and pin
untouched: `contentSource.ref` must point at an already-merged commit,
which did not exist until #120 landed.
## 改动 / Changes
- `agent.json#contentSource.ref`、sidecar 的 `provenance.content.ref` 与
`governance.compliance.reviewedRef` **三处同步** pin 到 `7560a58`
- 版本 `1.0.0` → `1.0.1`(`agent.json` 与 sidecar `release.version` 两处一致)
- 补 1.0.1 changelog(中英双语)
## 1.0.1 修了什么 / What 1.0.1 fixes
三条都是真机整理实测撞出来的 Agent 侧规格洞,不是模型不听话:
| 缺陷 | 实测现象 | 根因 |
| --- | --- | --- |
| 归档路径 | 五轮里有两轮归成 `<工作目录>/2022/11/增值税电子普通发票/2022-11-18_….pdf`——同时违反「缺
`归档/` 首层」「多插一层 `<发票类型>/`」「日期用 `YYYY-MM-DD` 而非八位连写」三条 |
原文只用一句散文说了路径,上下文压缩后凭印象重建就会漂 |
| 报告文件名 | 三轮全部写出 `报告/整理报告-<运行日期>.md` | 技能只定义了 `报告/<YYYY-MM>.md`,多月场景无从填写
|
| 台账表数量 | 只有 1 张发票时产出三张表(省了「按销售方汇总」) | 技能写了「四张表,顺序固定」但没说「即使为空也必须存在」 |
前两条的共同点:**只跑一遍、或只跑成功的那遍,都看不见**。归档路径在早期几轮全对,是在同一会话被压缩之后才走样的。
All three are spec gaps found by repeated real-machine runs, not model
disobedience — and the first two are only visible across multiple runs
in the same (compacted) conversation.
🤖 Generated with [Claude Code](https://claude.com/claude-code )
2026-09-04 12:22:39 -04:00
7560a58642
fix(invoice-organizer): 统一报告文件名约定 / unify report file naming ( #120 )
...
## 问题 / Problem
真机三轮整理实测:Agent 全部写出 `报告/整理报告-<运行日期>.md`,而技能里只定义了 `报告/<YYYY-MM>.md`。
**原因不是模型不听话。** 测试范围跨了 2020-08 到 2024-09 共十几个月,`<YYYY-MM>`
在多月场景下无从填写——技能没给出这种情况的文件名,Agent 只能自己发明一个。
**后果不是难看,而是会攒垃圾。** `invoice-automation` 的定时任务每月重建的是 `报告/<上月
YYYY-MM>.md`;与按运行日期命名的手工报告永远不会互相覆盖,用户目录里会留下一堆内容重叠、无从分辨新旧的报告。
Three real-machine runs all produced `报告/整理报告-<run date>.md`, while the
skills only define `报告/<YYYY-MM>.md`. The runs spanned 2020-08 through
2024-09, so `<YYYY-MM>` was unfillable and the agent improvised. Because
the scheduled monthly job rebuilds `报告/<last month YYYY-MM>.md`, the two
naming schemes never overwrite each other and the user's directory
accumulates overlapping reports with no way to tell which is current.
## 改动 / Changes
- `invoice-workflow` 第 7 步补一张表,把两种文件名钉死:
| 本次覆盖 | 文件名 | 例 |
| --- | --- | --- |
| 恰好一个自然月 | `报告/<YYYY-MM>.md` | `报告/2024-08.md` |
| 跨多个月 | `报告/<起始 YYYY-MM>_<结束 YYYY-MM>.md` | `报告/2020-08_2024-09.md` |
并写明月份按**开票日期**归属(与台账同口径)、同名直接覆盖(报告是从 `.index/ledger.json`
全量重建的派生产物,不像台账需要先备份)、以及**为什么不许用运行日期命名**
- `invoice-ledger` 与 `references/月度报告模板.md` 同步该约定
- 目录布局示例与中英 USAGE 一并标注跨月形态
## 为什么不在本 PR 里 bump 版本 / Why no version bump here
`contentSource` 的 `ref` 必须 pin 到**已合并的 commit**,本 PR 合并前拿不到那个 SHA。先 bump
版本会让目录声称 1.0.1、却仍按指向 1.0.0 内容的 pin 去取文件——比不 bump 更糟。版本号、pin 与 changelog
留到后续 PR 一起改。
The `contentSource` ref must pin to an already-merged commit, which does
not exist until this PR lands. Bumping the version now would advertise
1.0.1 while still serving 1.0.0 content. Version, pin and changelog
follow in a second PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---
## 追加:台账四张表必须全建 / Also: all four ledger sheets must always exist
真机压测(只有 1 张发票)产出的 `台账.xlsx` **只有三张表**——Agent 判定「按销售方汇总」在单一销售方下冗余而略过:
```
发票测试(23 张) → ['明细', '月度汇总', '按销售方汇总', '异常']
发票压测(1 张) → ['明细', '月度汇总', '异常'] ← 少一张
```
技能写的是「四张表,顺序固定」,但没说「即使为空也必须存在」。台账是要被重复重建、被用户自己写公式引用、被下一次整理覆盖的产物;表的位置一旦随数据量浮动,「第
3 张是按销售方汇总」这类引用就会在下个月悄悄指错。已在 `invoice-ledger` 里把这条钉死。
A stress run with a single invoice produced only three sheets — the
agent judged the per-seller summary redundant. The skill said "four
sheets, fixed order" but never said "even when empty". Sheet positions
that float with row count silently break user formulas and rebuilds; now
pinned.
2026-09-04 11:56:48 -04:00
6c303ef21f
feat(invoice-organizer): 声明为可安装并 pin 到已合并 commit / make Invoice Organizer installable ( #118 )
...
接 #117。那个 PR 只登记了条目本身(`availability: listing-only`),用户在市场里**看得到、装不了**。
本 PR 补齐可安装所需的证据链。
Follow-up to #117 , which only listed the entry (`listing-only`) —
visible in the marketplace but
not installable. This PR supplies the evidence chain required for
installation.
## 改动 / Changes
- `agent.json` 新增 `contentSource`,pin 到 #117 的合并 commit `42a29e9`
- sidecar 的 `provenance.content` 与之**归一化后逐字一致**
(两侧字段名按各自 schema 写:agent.json 侧是 `repoUrl`(`marketPointerSchema`
的必填字段名),
sidecar 侧是 `url`;`normalizeContentSource` 里 `input.url ?? input.repoUrl`
把两者统一成 `url`,
比对发生在归一化之后,取的是 `{kind, url, path, ref, sha256}` 五元组)
- `governance.compliance.reviewedRef` 绑定同一 commit;`reviewedAt` 与
`timestamps.reviewedAt` 取同一天
- `governance.availability` 翻成 `installable`
- **移除** sidecar 的 `stewardship: "official"` 与 `branding.relationship:
"official"`
## 为什么移除 `stewardship: official`(这一条值得单独看)
`normalize/sidecar.ts` 只允许 `bundled-builtin` / `system-agent` /
`official-pointer`
三种受信来源自称 official。**inline agent 不在其中**,命中即
`catalog.sidecar-forbidden-provider-claim` —— **整份 sidecar 被丢弃**,
静默回落到 `agent.json` 派生的 legacy 默认值。
在 dev 实例上实测确认(同步到 `42a29e9` 之后):
```
客户端拿到的 governance:
{"availability":"listing-only","license":{"state":"unknown"},
"redistribution":"verify-package-terms","listingMaintainer":{...}}
sidecar 里写的却是:
license: {state:"known", value:"MIT", evidencePath:"LICENSE"}, redistribution: "allowed"
catalogDiagnostics: null
```
也就是说:sidecar 被整份丢弃,而**维护者没有任何反馈**。
`dingtalk-workspace` 也有同样的 `stewardship: "official"`,它的 sidecar
**同样一直在被静默丢弃**
(实测它的客户端 governance 与本条目改之前完全一样)。这条不在本 PR 范围内,
建议单独处理;DesireCore 主仓库那边我另外记了一条「sidecar 被拒的诊断没有透出到任何 API」的缺口。
## 验证 / Verification
直接调用客户端的真实代码路径(不是照着文档推断):
```
hydrateCatalogSidecar success=true diagnostics=[]
compareMarketCatalogConsistency consistent=true mismatches=[]
evaluateCatalogAcquisitionEligibility allowed=true reasons=[]
legacy provenance.content: {"kind":"git","url":"https://github.com/desirecore/market.git ",
"path":"agents/invoice-organizer","ref":"42a29e99a0…"}
sidecar provenance.content: {同上,逐字一致}
```
`compareMarketCatalogConsistency` 对 inline agent 会**逐字比对**
`provenance.content`;
不一致会让整份 sidecar 静默降级回 listing-only,而 Python 校验器对 inline 条目
直接跳过这项比对(`_validate_content_consistency` 对 inline 早退),抓不到。
所以这一项只能靠上面这条客户端侧的验证兜住。
## 校验 / Validation
```
validate_catalog_metadata.py --require-complete 0 error, 129 warning, agents=4, sidecars=74
validate-i18n.py 0 error, 129 warning
translate.py --check ok
gen-collection-children.py --check ok
```
129 条 warning 全部来自 `skills/*` 的存量条目,`invoice-organizer` **零命中**。
2026-09-04 09:03:21 -04:00
42a29e99a0
feat(invoice-organizer): 新增发票整理助手 Agent / add Invoice Organizer agent ( #117 )
...
新增官方 inline Agent「发票整理助手」(`invoice-organizer`)。
Adds an official inline Agent, **Invoice Organizer**, that turns
invoices scattered across
mailboxes and local folders into a reconcilable, reusable ledger.
## 它做什么 / What it does
七步固定流程,每一步幂等:**接入检查 → 收集 → 解析 → 去重 → 归档 → 台账 → 报告**。
- **收集**:从已接入的邮箱(Gmail / Outlook / IMAP)找出候选发票邮件,附件用 `MailOperations` 的
`save_to` 直接落盘,base64 不进模型上下文
- **解析**:OFD(三个结构化来源分别处理)、PDF 文字层、扫描件走视觉;每个字段带 `extractedBy` 与置信度
- **去重**:发票号码为主键,跨格式识别同一张票(PDF 与照片、重复下载);近重复不自动合并
- **归档**:`归档/年/月/YYYYMMDD_销售方_金额_发票号码.ext`,原件一个不动
- **台账**:xlsx 四张表(明细 / 月度汇总 / 按销售方汇总 / 异常),发票号码强制文本格式;
装不上 openpyxl 时降级为 UTF-8 BOM CSV
- **报告**:Markdown 月度报告,收尾固定报五个计数(范围 / 候选 / 成功 / 待复核 / 失败)
- **自动化**:邮件规则 `agent_handle` + `ManageSchedule` 定时出账
## 明确的能力边界 / Explicit boundaries
- **不做发票真伪查验**,也不暗示做过——只做形式校验与勾稽校验,给出官方查验平台入口让用户自己核验
- **不删除、不移动、不转发用户的邮件**
- **票面内容不外流**:不外发、不代发、不上传第三方接口或在线查验站点
- **不做汇率换算**;不承诺「已找全」,只报「在给定范围内找到 N 封候选、成功解析 M 张」
## 结构 / Structure
```
agents/invoice-organizer/
├── agent.json persona.md principles.md LICENSE
├── USAGE.zh-CN.md USAGE.en-US.md
├── catalog-metadata.v1.json # availability: listing-only(可安装化见下)
├── assets/avatar.webp
└── skills/
├── invoice-workflow/ # 七步总纲、目录布局、落盘顺序、去重主键、幂等
├── invoice-extract/ # 三种载体的解析细则、置信度分档、特殊票据
│ └── references/票面文本形态.md
├── invoice-ledger/ # 台账结构、人民币约定(覆盖 xlsx 技能的默认口径)
│ └── references/月度报告模板.md
└── invoice-automation/ # 邮件规则与定时调度的参数模板与排查
```
`manifest.json` 的 `stats.totalAgents` 3 → 4。
## 事实性核对 / Fact-checking
技能里引用的**每一个**端点 / 工具名 / 参数名 / 返回字段都对着 DesireCore 主仓库实现逐条核对过,
并经过一轮对抗式 review。review 抓到的、已修正的主要事实错误:
- OFD 一节原本只覆盖 2020 年式样;已改为按**三个来源**分述
(内嵌附件 / 2024 数电票的 `Tags/CustomTag.xml` 标引 / `DocInfo/CustomDatas`),
键名从 `Buyer/BuyerName` 改为真实的点号路径 `Buyer.BuyerName`,置信度按来源分档
- **`DocInfo/CustomDatas` 的「合计金额」是不含税金额**,误当 `totalAmount` 会让每张 2024
数电票少记税额
——已写成硬规则
- Gmail 本地缓存搜索的 `q` **实际只按主题过滤**(正文过滤在主题收窄之后才跑),
原文写成「搜主题与正文」会导致静默漏邮件
- 「轮询只覆盖收件箱」只对 IMAP 成立,Gmail / Outlook 是整个邮箱
- `POST /rules/{id}/test` 走另一份内联实现、**没有 `matches_regex` 分支**,
不能用它验证正则规则
- 邮件列表项里 Gmail / IMAP **是带** `attachments[]` 的,只有 Outlook 不带;
`labelIds` 是 Gmail 专有
## 真机验证 / Verified on a live instance
在 dev 实例上以一句话指令处理 31 个混合文件(数电票 / 旧版票 / OFD / 扫描件 / 行程单,
外加重复、近重复、作废、零额与 4 个非发票负样本):
- 23 张归档,字段与夹具 ground truth **逐条吻合**
- 4 个非发票**全部正确拒绝**并写明理由(施工许可证 / 对账单 / 技术服务合同 / 邮件通知)
- 4 个跨格式重复(3 张扫描件 + 1 次重复下载)**全部靠发票号码主键识破**,隔离而非删除
- 近重复正确未合并;作废票归档但不计入合计;低置信度定额发票标为待复核
- 台账 xlsx 四张表、报告含五个计数、`SendUserMessage` 带附件交付
- `待整理/` 31 个原件一个未动
## 校验 / Validation
```
validate_catalog_metadata.py --require-complete 0 error, agents=4, sidecars=74 exit=0
validate-i18n.py 0 error exit=0
translate.py --check exit=0
gen-collection-children.py --check exit=0
```
129 条 warning 全部来自 `skills/*` 的存量条目,改前改后一字不差,`invoice-organizer` 零命中。
另核实:`agent.json` 过 `marketAgentSchema`(详情页)与收窄后过
`agentConfigSchema`(安装),
persona / principles 的 6 个 canonical key 用平台真实解析器全部解得出,全树敏感信息扫描通过
(所有公司名 / 税号 / 银行账号均为 `示例`/`示范`/`虚构`/`样例` 前缀的合成值)。
## 后续 / Follow-up
本 PR 为 `availability: listing-only`。可安装化需要把 `agent.json#contentSource` 与
sidecar 的 `provenance.content` **逐字一致地** pin 到本 PR 的合并 commit,
并补 `governance.compliance` 与 `timestamps.reviewedAt`——那是紧接着的第二个 PR。
2026-09-04 07:28:03 -04:00