Compare commits

...
Author SHA1 Message Date
QiuSW 80ed9b71e8 docs(tasks): complete T-010 parallel gate 2026-08-04 14:39:27 +08:00
QiuSW 3f2e0e5598 docs: allow safe admin parallelism 2026-08-04 14:38:20 +08:00
QiuSW d27fda6be3 docs(tasks): record T-103 sanitizer v3 2026-08-04 14:08:10 +08:00
QiuSW acf7e11114 fix(client): bind T-103 XML coordinates 2026-08-04 14:04:49 +08:00
9 changed files with 121 additions and 50 deletions
@@ -21,7 +21,7 @@ from ..pdd.product_url import ProductUrl, ProductUrlError, parse_product_url
from ..pdd.sku_panel_state import HUMAN_DECLARED_STATES
SANITIZER_VERSION = "t103-privacy-v2"
SANITIZER_VERSION = "t103-privacy-v3"
EXPECTED_GOODS_ID = "937122477375"
EXPECTED_PDD_VERSION = "8.17.0"
EXPECTED_DEVICE_MODEL = "PKG110"
@@ -29,7 +29,7 @@ EXPECTED_ANDROID_VERSION = "16"
EXPECTED_SCREENSHOT_WIDTH = 1080
EXPECTED_SCREENSHOT_HEIGHT = 2376
EXPECTED_XML_WIDTH = 1080
EXPECTED_XML_HEIGHT = 2400
EXPECTED_XML_HEIGHT = 2376
_ARTIFACT_FILES = ("screenshot.png", "hierarchy.xml")
_SHA256_RE = re.compile(r"[0-9a-f]{64}\Z")
_BOUNDS_RE = re.compile(r"\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]\Z")
@@ -48,7 +48,7 @@ def _default_xml() -> str:
return (
"<hierarchy rotation='0'>"
f"<node bounds='[0,0][1080,540]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE} {FULL_PHONE}' />"
f"<node bounds='[0,540][1080,2400]' text='{SAFE_TEXT}' />"
f"<node bounds='[0,540][1080,2376]' text='{SAFE_TEXT}' />"
"</hierarchy>"
)
@@ -117,17 +117,17 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
self.assertNotIn(MASKED_PHONE, derived_xml)
self.assertIn(f'"human_declared_state": "{state}"', manifest)
self.assertIn('"privacy_tier": "SANITIZED"', manifest)
self.assertIn('"sanitizer_version": "t103-privacy-v2"', manifest)
self.assertIn('"sanitizer_version": "t103-privacy-v3"', manifest)
self.assertIn('"screenshot_space": {', manifest)
self.assertIn('"xml_coordinate_space": {', manifest)
self.assertIn('"height": 2376', manifest)
self.assertIn('"height": 2400', manifest)
self.assertIn('"height": 2376', manifest)
self.assertIn('"privacy_mask_rectangle": [', manifest)
self.assertIn('"removed_nodes": 1', manifest)
self.assertIn('"cleared_crossing_nodes": 0', manifest)
self.assertIn('"retained_below_nodes": 1', manifest)
self.assertIn('"max_right": 1080', manifest)
self.assertIn('"max_bottom": 2400', manifest)
self.assertIn('"max_bottom": 2376', manifest)
self.assertNotIn("canonical_url", manifest)
self.assertNotIn(TEST_SERIAL, manifest)
self.assertNotIn("serial", manifest)
@@ -138,9 +138,9 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
def test_crossing_container_keeps_lower_children_but_clears_its_text(self) -> None:
xml = (
"<hierarchy>"
f"<node bounds='[0,0][1080,2400]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE}'>"
f"<node bounds='[0,0][1080,2376]' text='{TEST_ADDRESS}' content-desc='{MASKED_PHONE}'>"
f"<node bounds='[0,0][1080,540]' text='{FULL_PHONE}' />"
f"<node bounds='[0,540][1080,2400]' text='{SAFE_TEXT}' />"
f"<node bounds='[0,540][1080,2376]' text='{SAFE_TEXT}' />"
"</node></hierarchy>"
)
with TemporaryDirectory() as temporary:
@@ -151,7 +151,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
self.assertIsNotNone(crossing)
assert crossing is not None
self.assertEqual(crossing.attrib, {"bounds": "[0,0][1080,2400]"})
self.assertEqual(crossing.attrib, {"bounds": "[0,0][1080,2376]"})
self.assertEqual(len(list(crossing)), 1)
self.assertEqual(list(crossing)[0].get("text"), SAFE_TEXT)
manifest = json.loads(result.manifest_path.read_text(encoding="utf-8"))
@@ -162,7 +162,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
"cleared_crossing_nodes": 1,
"retained_below_nodes": 1,
"max_right": 1080,
"max_bottom": 2400,
"max_bottom": 2376,
},
)
@@ -195,9 +195,9 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
manifest["xml_coordinate_space"],
{
"width": 1080,
"height": 2400,
"height": 2376,
"privacy_mask_rectangle": [0, 0, 1080, 540],
"observed_max": {"right": 1080, "bottom": 2400},
"observed_max": {"right": 1080, "bottom": 2376},
},
)
@@ -243,10 +243,10 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
("bounds", "<hierarchy><node text='missing bounds' /></hierarchy>"),
(
"private-parent-with-lower-child",
"<hierarchy><node bounds='[0,0][1080,540]'><node bounds='[0,540][1080,2400]' text='x' /></node></hierarchy>",
"<hierarchy><node bounds='[0,0][1080,540]'><node bounds='[0,540][1080,2376]' text='x' /></node></hierarchy>",
),
("full-phone-below", f"<hierarchy><node bounds='[0,540][1080,2400]' text='{FULL_PHONE}' /></hierarchy>"),
("masked-phone-below", f"<hierarchy><node bounds='[0,540][1080,2400]' text='{MASKED_PHONE}' /></hierarchy>"),
("full-phone-below", f"<hierarchy><node bounds='[0,540][1080,2376]' text='{FULL_PHONE}' /></hierarchy>"),
("masked-phone-below", f"<hierarchy><node bounds='[0,540][1080,2376]' text='{MASKED_PHONE}' /></hierarchy>"),
)
for kind, xml in malformed:
with self.subTest(kind=kind), TemporaryDirectory() as temporary:
@@ -281,7 +281,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
xml = (
"<hierarchy>"
"<node bounds='[0,0][1080,540]' text='private' />"
f"<node bounds='[0,540][1080,2400]' text='{value}' />"
f"<node bounds='[0,540][1080,2376]' text='{value}' />"
"</hierarchy>"
)
with self.subTest(value=value), TemporaryDirectory() as temporary:
@@ -294,7 +294,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
"<hierarchy>"
"<node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,1000]' text='138' content-desc='0013' />"
"<node bounds='[0,1000][1080,2400]' text='8000' />"
"<node bounds='[0,1000][1080,2376]' text='8000' />"
"</hierarchy>"
)
with TemporaryDirectory() as temporary:
@@ -305,7 +305,7 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
def test_privacy_geometry_requires_removed_and_retained_nodes(self) -> None:
scenarios = (
("no-private", f"<hierarchy><node bounds='[0,540][1080,2400]' text='{SAFE_TEXT}' /></hierarchy>"),
("no-private", f"<hierarchy><node bounds='[0,540][1080,2376]' text='{SAFE_TEXT}' /></hierarchy>"),
("no-below", "<hierarchy><node bounds='[0,0][1080,540]' text='private' /></hierarchy>"),
)
for name, xml in scenarios:
@@ -321,26 +321,26 @@ class SkuEvidenceSanitizerTests(unittest.TestCase):
(
"short-width",
"<hierarchy><node bounds='[0,0][1079,540]' text='private' />"
"<node bounds='[0,540][1079,2400]' text='safe' /></hierarchy>",
"1079x2400",
"<node bounds='[0,540][1079,2376]' text='safe' /></hierarchy>",
"1079x2376",
),
(
"short-height",
"<hierarchy><node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,2376]' text='safe' /></hierarchy>",
"1080x2376",
"<node bounds='[0,540][1080,2375]' text='safe' /></hierarchy>",
"1080x2375",
),
(
"wide-width",
"<hierarchy><node bounds='[0,0][1081,540]' text='private' />"
"<node bounds='[0,540][1081,2400]' text='safe' /></hierarchy>",
"1081x2400",
"<node bounds='[0,540][1081,2376]' text='safe' /></hierarchy>",
"1081x2376",
),
(
"tall-height",
"old-v2-xml-height",
"<hierarchy><node bounds='[0,0][1080,540]' text='private' />"
"<node bounds='[0,540][1080,2401]' text='safe' /></hierarchy>",
"1080x2401",
"<node bounds='[0,540][1080,2400]' text='safe' /></hierarchy>",
"1080x2400",
),
)
for name, xml, observed in scenarios:
+9 -7
View File
@@ -85,22 +85,24 @@ cmbuyer 是一个自动化采购系统:**采购服务**(网页端,`admin/`
## 当前阶段
**Phase 0 · 地基。** 采购服务与采购工具骨架均已初始化,尚无采购业务代码。
**Phase 1 · 真机可行性,并行启动采购服务基础能力。** 两端骨架与核心数据模型已完成,T-103
仍是当前真机关键路径;与真机可读字段无关的采购服务能力不再空等。
执行按任务依赖驱动,**不按 Phase 整段串行等待**。当前优先路径:
1. T-003 统一入口与 T-004 核心数据模型已完成;T-101 真机环境盘点已就绪,但尚未真机验收。
2. T-002 已完成,立即推进 **T-101 → T-102 → T-103 真机取证**。
3. T-103 结论确认后,才开始依赖真机可读字段的 Phase 2 生产页面;采购服务核心与
T-104 → T-107 后续真机安全判据按依赖并行推进。
1. T-001~T-004 与 T-101~T-102 已完成,继续推进 **T-103 真机取证**。
2. T-103 进行时并行推进 T-201 管理会话 → T-202 `DRAFT` 建单与基础列表;两者不得启动试选,
也不得引入机器结果、规格面板单价或证据字段。
3. T-103 结论确认后,再推进 T-203 批量开始试选、T-204 试选证据详情、T-205~T-207,
并按依赖推进 T-104 → T-107 后续真机安全判据。
4. Phase 3:双端打通与**第一趟试选**端到端。
5. Phase 4:**第二趟下单**与收尾。
6. V2 及以后:图搜、Excel、ERP、订单自动核对、AI 辅助。
> **M2 是本项目的生死线**:真机能按链接打开商品、精确勾选颜色分类和尺码、
> **读到该 SKU 的单价**(T-103)。前序项目正是卡在选规格和读价。
> M2 不通过之前不要写 Phase 2 的生产页面;Phase 0 原型只确认流程与信息架构,实际可读
> 字段仍以真机证据为准。
> M2 不通过之前不要写**依赖真机可读字段或会启动试选**的 Phase 2 功能;T-201 与仅创建
> `DRAFT` 的 T-202 可以并行。原型只确认流程与信息架构,实际可读字段仍以真机证据为准。
## 领取任务规则
+5 -3
View File
@@ -154,8 +154,10 @@ PDD 规格面板不可避免显示收货区域和掩码手机号。原始截图/
T-103 已在 `client/src/cmbuyer_client/device/sku_evidence_sanitizer.py` 实现本机确定性脱敏器,并由
`client/scripts/sanitize_sku_panel_evidence.py` 提供离线 CLI。第一组新 raw 的 PNG 头部由人确认实际为
1080×2376,推翻了 v1 将截图和 XML 坐标空间都写成 1080×2400 的假设;v1 正确拒绝且原始证据未重采。
当前 `t103-privacy-v2` 配置精确绑定 PKG110 / Android 16 / 拼多多 8.17.0 / goods_id
`937122477375`,分别约束 screenshot space=1080×2376、XML coordinate space=1080×2400,二者都把
随后 v2 在同一份 raw 上安全报告 XML `observed 1080x2376` 并拒绝发布,证明其独立配置的
1080×2400 假设同样不成立。当前 `t103-privacy-v3` 配置精确绑定 PKG110 / Android 16 / 拼多多
8.17.0 / goods_id `937122477375`;screenshot space 与 XML coordinate space 仍分别建模、分别校验,
当前都精确为 1080×2376,且都把
`[0,0,1080,540)` 作为整宽隐私带。截图覆盖该区域;XML 递归移除区域内节点,跨界容器只清空自身
敏感属性并保留下方子节点。地址依靠已确认的整块几何隔离而不是易漏的关键词表;完整、掩码、带分隔符
或跨节点手机号残留会被自动复检拒绝。
@@ -165,7 +167,7 @@ T-103 已在 `client/src/cmbuyer_client/device/sku_evidence_sanitizer.py` 实现
```
脱敏器校验源 manifest 与文件哈希、设备/App/商品/人工状态、截图分辨率和 XML 隐私结构;XML 所有
节点观察到的最大 right/bottom 必须精确为 1080×2400,否则只报告非敏感 observed 尺寸并拒绝。它只允许发布到
节点观察到的最大 right/bottom 必须精确为 1080×2376,否则只报告非敏感 observed 尺寸并拒绝。它只允许发布到
同级且尚不存在的 `derived`;失败或发布竞态不覆盖已有目录、不留下 staging。派生 manifest 记录
`privacy_tier=SANITIZED`、sanitizer 版本、两个坐标空间、清理计数及本机 source/derived 哈希,不记录
原始路径、serial 或页面正文。只有自动复检通过并经人确认状态对应性的派生物,agent 才能读取并提取
+4 -2
View File
@@ -96,8 +96,10 @@
- V2 / V3 只记录,不实现。**图搜、Excel、ERP、订单自动核对、AI 辅助全部不在 MVP。**
- 需求明确排除的非目标不得实现。
- 不为「将来可能用到」提前抽象。
- **Phase 1 真机结论出来之前不写 Phase 2 的页面**——确认页显示什么,取决于真机上
能读到什么,尤其是规格面板上的单价。
- **Phase 1 真机结论出来之前,不写依赖真机可读字段或会启动试选的 Phase 2 功能。** T-201
管理会话与只创建 `DRAFT` 的 T-202 基础建单 / 列表可以并行;T-203 的批量开始试选、T-204
的试选证据详情以及 T-205 以后仍等待 T-103。T-201 / T-202 不得夹带机器实际规格、规格面板
单价、证据、授权、提交或付款字段。
## 5. 架构纪律
+9 -6
View File
@@ -10,8 +10,9 @@
把验收要点展开成可执行、可观察的步骤,再开始实现。
2. **每个 agent 一次只做一个任务**:领取、状态流转、执行记录、完成定义遵循
[`tasks/README.md`](tasks/README.md) 和[编码规则](05-coding-rules.md)。
3. **不跳步**:依赖未完成的任务不能开工。Phase 0 可先做使用假数据的低保真交互原型;
**Phase 1 的真机结论出来之前不写 Phase 2 的生产页面**,T-103 若改变可读字段则先修订原型与 IX。
3. **不跳步**:依赖未完成的任务不能开工。T-103 完成前可并行 T-201 管理会话与只创建 `DRAFT`
的 T-202 基础建单 / 列表;**依赖真机可读字段或会启动试选的 Phase 2 功能仍不得抢跑**,
T-103 若改变可读字段则先修订原型与 IX。
4. **本文只在规划变化时修改**:单个任务开工或完成**不**修改本文。
5. **动手前**先读 `00-ai-start-here.md`、`05-coding-rules.md` 和 `current-state.md`。
@@ -24,14 +25,15 @@
| --- | --- | --- | --- |
| 1 | T-001 初始化 `admin/` | T-002 初始化 `client/` | - |
| 2 | T-001 后立即做 T-004 | T-002 后立即做 T-101 → T-102 → **T-103** | T-001、T-002 都完成后做 T-003 |
| 3 | T-103 通过后推进 T-201 → T-207 | T-103 后推进 T-104 → T-107 | T-208 等待 T-207 与 T-107 均完成 |
| 3 | 与 T-103 并行做 T-201 → T-202;T-103 后做 T-203 → T-207 | T-103 后推进 T-104 → T-107 | T-208 等待 T-207 与 T-107 均完成 |
| 4 | T-301 → T-302 | T-302 后交接 T-303 → T-304 → T-306 | T-306 与 T-104 完成后做 T-305 |
| 5 | - | T-305、T-208、T-306 后做 T-401 | T-401 后并行 T-402 / T-403,再做 T-404 → T-405 |
**T-103 是当前最高优先级和 MVP 生死线。** T-002 一完成就启动 T-101,不等待 T-003、
T-004 或整个 Phase 0 收尾。并行只优化等待关系,不改变下列门禁:
- T-103 的真机结论出来前,不写依赖真机可读字段的 Phase 2 生产页面。
- T-103 的真机结论出来前,T-201 / T-202 只能落管理会话、`DRAFT` 建单与基础列表;不启动试选,
不展示机器规格、规格面板单价或证据。T-203~T-207 继续等待 T-103。
- `needs_device: true` 的任务仍只能由人完成验收。
- 不复用前序项目页面判据,不放宽三道价格闸门,不让第一趟引用任何下单函数。
- 第三个 agent 优先做写路径独立的集成任务或只读复核,不与两端任务争写共享文档。
@@ -126,8 +128,9 @@ T-004 或整个 Phase 0 收尾。并行只优化等待关系,不改变下列
- **M5**:第一趟试选端到端跑通,任务能停在「等你确认」。(T-305)
- **M6**:MVP 闭环——第二趟下单成功,任务停在「待付款」。(T-401)
**M2 是本项目的生死线。** 前序项目正是卡在选规格和读价;M2 不通过之前不要写 Phase 2
的生产页面。Phase 0 原型只用于确认信息架构,T-103 若改变可读字段必须先回修原型与 IX。
**M2 是本项目的生死线。** 前序项目正是卡在选规格和读价;M2 不通过之前只允许 T-201 与
不启动试选的 T-202,不写依赖真机字段或会推进任务执行的 Phase 2 功能。原型只用于确认信息架构,
T-103 若改变可读字段必须先回修原型与 IX。
## 待办池(Backlog)
+6 -4
View File
@@ -33,8 +33,9 @@
打开规格面板;T-110 已获项目所有者批准,只把该证据/版本绑定的精确唯一入口作为第一趟可逆导航,
数量、确认页、提交订单、付款与通用点击能力仍不可达。T-103 的原始证据本机隔离与确定性脱敏器
已完成离线实现;人工确认面板刚打开时目标颜色和尺码已经自动选中,取证三态已据此修正。下一步
用 v2 对已采第一态 raw 重新脱敏并验证 XML 坐标空间,再采集“两个维度改为非目标 / 两个维度恢复
目标”的 raw,由人只核对 derived。在派生证据验收前不写页面判据,Phase 2 仍不能抢跑。
用 v3 对已采第一态 raw 重新脱敏;截图与 XML 已分别安全观测为 1080×2376,仍作为两个独立坐标
空间严格校验。第一态 derived 经人验收后,再采集“两个维度改为非目标 / 两个维度恢复目标”的 raw,
并由人只核对 derived。在派生证据验收前不写页面判据,Phase 2 仍不能抢跑。
## 当前目录要点
@@ -161,8 +162,9 @@ T-103 已确认当前衣服商品只能从精确文案“快要抢光”进入
```
原始 screenshot/XML 可能包含 PDD 固定展示的地址和掩码手机号,只能留在本机 `raw` 目录,不能由
agent、fixture、业务或上传端消费。`t103-privacy-v2` 分别固定截图 1080×2376 与 XML 坐标
1080×2400,并校验源哈希、设备/App/商品/人工状态与隐私结构,在 sibling
agent、fixture、业务或上传端消费。`t103-privacy-v3` 把截图与 XML 保持为两个独立坐标空间,
并根据同一第一态 raw 的两次 fail-closed 安全观测将二者分别精确固定为 1080×2376;它校验源哈希、
设备/App/商品/人工状态与隐私结构,在 sibling
`derived` 目录原子发布 screenshot/XML 与 manifest;任何不匹配、手机号残留或已有目标均拒绝发布。
自动复检通过且人确认三态对应性后,agent 才能读取派生物并提取最小 fixture、编写判据。
人工确认中还必须记录中间态实际选择的非目标颜色和尺码,不能只写“已切换”。
+52
View File
@@ -0,0 +1,52 @@
---
id: T-010
title: 收窄 Phase 2 门禁并启动安全并行
phase: 0
deps: [T-009]
status: DONE
created: 2026-08-04
vikunja_task_id: 25
context_ref: d27fda6
work_branch: task/t-010-parallel-gate
needs_device: false
needs_human_review: false
write_paths:
- docs/tasks/T-010.md
- docs/00-ai-start-here.md
- docs/05-coding-rules.md
- docs/06-tasks.md
---
<!-- BEGIN VIKUNJA EXPORT id=25 synced=2026-08-04T06:39:13Z sha256=b3485476184414158cac243aa3f2d08567372179c2e91c2cf4bb5552e7191510 -->
## 问题 / 背景
客户要求加快 MVP。现有全局门禁把全部 Phase 2 页面都冻结到 T-103 完成,范围过宽;管理员登录与只创建 DRAFT 的基础建单不读取真机页面字段,也不会启动试选或触发下单。
## 方案
1. 把门禁收窄为:T-103 前允许 T-201 管理会话与 T-202 DRAFT 建单/基础列表;T-203 的批量开始试选、T-204 试选证据详情和 T-205 以后继续冻结。
2. 同步 AI 入口、编码规则和路线图;不修改 T-103 安全边界,不放宽不付款、第一趟禁下单、真机判据先取证或隐私证据分层。
3. 并行启动 admin T-201;共享文档由本任务所有者统一修改。
## 验收要点
- 门禁文字不存在“冻结全部 Phase 2”的歧义。
- T-201/T-202 不得出现 PDD 页面判据、试选结果、证据、授权、提交或支付能力。
- 上下文校验、Vikunja 导出检查与 diff-check 通过。
## 执行记录
### 2026-08-04T06:38:51Z · ila
2026-08-04 完成并行门禁收窄:提交 3f2e0e5 更新 AI 入口、编码规则和路线图。T-103 期间只放行 T-201 管理会话与不启动试选的 T-202 DRAFT 建单/基础列表;T-203~T-207 继续等待 T-103。不付款、第一趟禁下单、真机判据与证据边界均未放宽。主 agent 复跑 agent-context validator、冲突文案检索和 git diff --check,全部通过。
<!-- END VIKUNJA EXPORT -->
## 边界
- 本任务只调整依赖门禁和并行顺序,不实现任何采购服务、真机自动化或页面判据代码。
- T-103 完成前只放行与真机可读字段无关的 T-201 管理会话,以及只创建 `DRAFT`、不启动试选的
T-202 基础建单与列表壳。
- T-203 的批量 `DRAFT → PENDING`、T-204 的试选证据详情以及 T-205 以后仍由 T-103 阻塞;不得用
假字段、原型假数据或前序项目结论提前固化生产契约。
- 不付款、第一趟不可达下单、价格读取位置、真机判据先取证、原始证据本机隔离与派生物消费边界
均保持不变。
+9 -1
View File
@@ -25,7 +25,7 @@ write_paths:
- docs/current-state.md
---
<!-- BEGIN VIKUNJA EXPORT id=23 synced=2026-08-04T03:59:02Z sha256=07472f92450a4ec3e062f779461c29a40f0078b17a6d58745e529f0c855a64cf -->
<!-- BEGIN VIKUNJA EXPORT id=23 synced=2026-08-04T06:06:05Z sha256=9de2a35beba4cca7a29cf4dc6b5ff643d829f6467ff576da976589a8d1f3dcad -->
## 问题 / 背景
T-102 已证明 canonical 链接可进入目标商品。T-103 随后在 PKG110 / Android 16 / 拼多多 8.17.0、衣服商品 goods_id `937122477375` 上确认:规格面板只能从详情页右下角精确文案“快要抢光”进入,面板固定显示收货区域和掩码手机号。T-110 经项目所有者批准,将该已取证点击定义为可逆、能力受限的规格面板导航;这不是通用购买入口豁免,不授权其他文案、数量、确认页、提交订单或支付。 项目所有者随后确认,面板刚打开时已自动选中目标颜色“黑色CHA(纯棉)”和尺码“M(建议100-115)”;因此本任务不再假设存在“未选择”或“只选择一个维度”的初始状态。
@@ -123,6 +123,14 @@ T-103 新三态离线实现与主审:提交 dbb69a7 将人工声明状态集
### 2026-08-04T03:58:54Z · ila
T-103 sanitizer v2 坐标修正与主审:提交 44c027a 将 screenshot space 固定为人确认的 1080x2376,并把 XML coordinate space 独立严格配置为待验证的 1080x2400;sanitizer_version 升为 t103-privacy-v2。XML 会统计所有 bounds 的 max right/bottom,任何小于或大于配置的偏差均 fail closed,错误只含非敏感 observed 尺寸。派生 manifest 记录两个坐标空间和各自遮罩。主 agent 独立复跑 69 项 client 单测、compileall、CLI help 与 diff-check,全部通过;未读取真实 raw 正文、未连接设备。等待人用同一第一态 raw 重跑 v2 脱敏。
### 2026-08-04T06:01:02Z · ila
人使用 sanitizer v2 对同一第一态 raw 重跑,安全诊断返回 XML observed 1080x2376;未发布 derived。由此确认本次 screenshot space 与 XML coordinate space 均为 1080x2376,v2 对 XML=1080x2400 的待验证假设被真机证据否定。原始证据继续保留且不重采;下一步升级 v3,仍分离建模两个空间但分别精确绑定当前相同尺寸。
### 2026-08-04T06:05:53Z · ila
2026-08-04 T-103 sanitizer v3 坐标修正与主审:人用同一第一态 raw 运行 v2,脱敏器安全拒绝并仅报告 observed 1080x2376,证明 XML 实际坐标与截图相同;原始证据仍有效、未重采、未读取正文。提交 acf7e11 将 sanitizer_version 升为 t103-privacy-v3;截图与 XML 仍分别建模并分别严格校验,但当前均精确绑定 1080x2376,隐私带保持 [0,0,1080,540)。合成测试明确拒绝旧截图 1080x2400、旧 v2 XML 1080x2400,以及较小/较大坐标;手机号残留、哈希/状态/版本、确定性、原子发布和不覆盖门禁保持不变。主 agent 独立复跑 69 项 client 单测、compileall、CLI help、静态禁用能力检索与 diff-check,全部通过;未连接设备、未读取真实 raw。T-103 保持 DOING,等待人用同一第一态 raw 重跑 v3 并只核对 derived。
<!-- END VIKUNJA EXPORT -->
## 边界