Treatment 的 Temporal Validity 与 Reauthorization
解释一个 Treatment proposal 为什么可能在生成时合法、在稍后接受时却已经失效,以及如何用 source/current BodyState revision、Diagnosis freshness 与 material change 做重新授权。
[!info] related notes
- 所属 MOC: bodysense-moc
- 前置概念: treatment-proposal-acceptance-execution-authority、agent-decision-authority
- 并列概念: bodysense-treatment-aggregate-and-immutable-revisions
- 易混淆概念: 数据库版本冲突 vs 业务时间失效
- 关系笔记: intervention-outcome-body-state-feedback-loop
Treatment 的 Temporal Validity 与 Reauthorization
一句话定义
Temporal Validity 指一个 Treatment proposal 的业务资格只对特定历史上下文成立;当时间推进、BodyState 或 Diagnosis 状态变化后,系统必须在 Acceptance 或继续执行前重新验证,而不能把过去的授权当成永久授权。
最简公式:
Valid when generated
≠
Still valid now
为什么“时间”本身不是问题,世界变化才是问题
一个 Proposal 放了 10 分钟不一定失效,放了 1 天也不一定必然失效。
真正关心的是:
从 Proposal World 到 Current World
发生了哪些 decision-relevant change?
因此 temporal validity 不应该简单实现成:
created_at > 1 hour
→ invalid
时间阈值可以作为重新检查触发器,但最终决定应基于业务状态变化。
为什么 Treatment 比 Diagnosis 更容易遇到时间问题
DiagnosisAnalysis 在产生以后主要成为历史分析事实:
R42 → D100
用户状态变化后:
R43 → D101
而 Treatment Proposal 常常会在系统里等待用户行动:
R50 + D20
→ Proposal P3
→ [等待几小时/几天]
→ User Accept
这个等待窗口让“生成时上下文”和“接受时上下文”自然分离。
Proposal 应保存自己的 Source Context
一个可审计 TreatmentRevision 应知道自己是基于什么生成的,例如:
source_body_state_revision = R50
source_diagnosis_analysis_id = D20
agent_configuration_id = C7
这些字段不是普通 metadata,而是未来重新授权的锚点。
没有 source identity,就无法回答:
“当前 R54 和当初 P3 看到的世界到底差了什么?”
Source Context 最好包含“决策依赖”,而不是仅一个时间戳
例如:
source_body_state_revision
source_diagnosis_analysis_id
candidate assessment identity/state
safety state reference
configuration ID
因为 created_at=10:00 无法告诉系统 Proposal 当时依赖什么事实。
Temporal reauthorization 的基础是可比较的 source identity。
Acceptance 时需要比较两个世界
Proposal World
R50 + D20
Current World
R54 + current Diagnosis freshness + current SafetyState
Acceptance Policy 要判断:
从 source world 到 current world 的变化,是否会实质影响这个 proposal 的适用性?
这就是 temporal authorization。
World Diff 应该是 Domain-aware,而不是 JSON Diff
如果 BodyState 有 100 个字段,R50→R54 变化 5 个字段,不代表这 5 个都影响 Treatment。
例如:
preferred_workout_time changed
可能只影响 UX。
而:
new radiating pain
new neurological red flag
new load intolerance
可能直接改变 action eligibility。
所以更合理:
BodyStateRevisionDelta
→ Domain Materiality Policy
→ material / non-material for this Treatment
而不是:
JSON != JSON
→ reject
Revision 变化本身不是自动 Blocker
一个常见误解是:
current_revision > source_revision
→ proposal 一律失效
这太保守。
例如 R50 → R51 只是:
补充了一个与当前 Treatment 无关的低风险观察
可能不影响 Proposal。
因此当前 BodySense 的 acceptance 逻辑更接近:
if current_revision > source_revision:
inspect revisions after source
evaluate material review policy
也就是:
Revision drift 是重新评估的触发器,不一定是最终拒绝理由。
为什么 current_revision < source_revision 应 Fail Closed
正常历史应满足:
current revision >= proposal source revision
如果出现:
current R49
source R50
说明可能有:
- 数据恢复错误;
- stale replica;
- revision parsing bug;
- cross-user/cross-environment mix-up。
这不是普通“状态没变”,而是历史顺序本身异常,应该阻断 acceptance 并调查,而不是继续推断。
Material Change:什么变化值得阻断旧 Proposal
Material change 指足以让原 Proposal 的依据、风险或动作资格发生实质变化的状态变化。
例如:
Safety Material Change
R50: no active red flag
R51: new red flag
旧 Proposal 即使当时完全合理,也不应继续直接接受。
Diagnosis Material Change
D20 originally fresh
↓
new BodyState evidence
↓
D20 becomes stale
Treatment 基于的 diagnosis epistemic foundation 已经发生变化。
Constraint Material Change
例如新的用户约束使某 intervention 不再适合:
previously able to load shoulder
↓
new injury / movement restriction
Prescription-relevant Change
例如原计划:
load progression permitted
但当前用户出现:
post-intervention worsening
可能不要求整个 Diagnosis 重做,但会让原 prescription 不再适用。
Non-material Change
例如:
新增长期不相关的普通 profile 信息
可能不需要废弃旧 Proposal。
Materiality 是“相对于某个 Action”的,不一定是全局属性
同一个 BodyState change 对两个 Treatment 的影响可能不同。
例如:
new wrist limitation
对:
lower-body walking plan
可能 non-material。
对:
loaded wrist mobility plan
则 material。
所以更成熟的 policy 可以考虑:
MaterialChange(change, treatment/intervention context)
而不是每个 change 全局固定 material=true/false。
Diagnosis Freshness 为什么是 Treatment 的输入
Treatment 不应该把历史 DiagnosisAnalysis 看成永远正确的静态前提。
更准确的关系是:
DiagnosisAnalysis D20 = immutable historical artifact
DiagnosisFreshness(D20, current BodyState) = current applicability judgment
因此:
D20 itself never changes
but
D20 may become stale against R54
这延续了 Diagnosis 阶段的重要原则:
Historical truth
≠
Current applicability
Treatment 正是第一个强烈依赖这个差异的下游系统。
Freshness 不应该通过修改 DiagnosisAnalysis 表达
不要:
D20.status = stale
并修改里面的 candidate/gap
更合理:
D20 remains immutable
FreshnessEvaluation(D20, R54) = stale
或者保存独立 applicability/freshness projection。
这避免下游 temporal logic 篡改上游历史事实。
为什么 Acceptance Authority 必须重新读取 durable truth
错误做法:
Frontend sends:
{
revision_id: P3,
safe: true,
diagnosis_fresh: true
}
然后服务端信任这些状态。
正确做法:
AcceptProposal(P3)
↓
Go loads current BodyState
↓
loads source Diagnosis
↓
evaluates freshness
↓
loads candidate assessments
↓
evaluates revisions since source
↓
DecisionPolicy
原因是 Acceptance 是一个业务命令边界,authority facts 应由 durable owners 重新读取,而不是从 UI 带回来。
为什么 Client-supplied Facts 不可信,即使 UI 是自己写的
不是因为前端“恶意”,而是因为:
- UI cache 可能 stale;
- 多标签页并发;
- 网络延迟;
- API retry;
- 其他客户端可能更新 BodyState;
- 未来有移动端/自动化客户端。
所以 command boundary 必须以 server-side durable truth 为准。
Reauthorization 不等于重新调用 LLM
这是非常重要的区别。
重新授权首先是 deterministic:
current facts
+
versioned policy
→ allow / block
它不应该默认变成:
“让模型再看看旧 Proposal 现在还行不行。”
如果 Policy 判断旧 Proposal 已超出有效范围,系统可以:
block old acceptance
→ request/generate new revision
而不是让 LLM 临时给旧 Proposal 一个免责说明。
什么时候才需要重新生成 Proposal
Reauthorization 结果可能分:
Still valid
→ accept old proposal
Stale but repairable
→ maybe generate revised proposal
Safety-invalid
→ block/escalate, no ordinary generation
这避免“只要状态变了就自动重新问 LLM”的过度调用。
Ongoing Authorization:接受以后也不是永久有效
Temporal Validity 不只存在于:
Proposal → Acceptance
还存在于:
Accepted Treatment → Continued Execution
Treatment 可能经历:
active
→ review_recommended
→ paused
→ superseded
→ completed
新的 Outcome / BodyState revision 可能触发:
EvaluateCurrentReview
也就是说:
现实世界中的 action authorization 往往需要持续维持,而不是一次性永久授予。
为什么 Ongoing Review 不应该每次都重新调用 LLM
很多变化可以先 deterministic 判断:
new red flag
source diagnosis stale
material constraint changed
先决定:
pause/review required
之后如果需要新方案,再进入 Agent proposal。
这让 safety control 不依赖模型是否及时意识到风险。
时间轴例子
09:00
BodyState R70
Diagnosis D30 fresh
→ P8 generated
→ allow-proposal
12:00
BodyState R71
新增轻微、无关信息
13:00
User Accept P8
→ inspect R71-R70 delta
→ non-material
→ allow-acceptance
Day 3
Outcome O12 recorded
→ BodyState R72
→ material worsening signal
→ Treatment status = review_recommended
注意三个时间点对应三个不同问题:
09:00: Can we propose?
13:00: Can we accept now?
Day 3: Can this Treatment continue unchanged?
第二个例子:时间很短也可能失效
10:00 P9 generated
10:02 user reports new severe neurological sign
10:03 click Accept
只过去 3 分钟,但 source/current world 已 materially changed。
所以:
age of proposal
≠
validity of proposal
与 optimistic locking 的区别
Temporal Validity 不是单纯数据库并发控制。
Optimistic locking 问:
“我读取以后这行数据库有没有被别人修改?”
Temporal Validity 问:
“即使没有写冲突,现实业务状态发生的变化是否让旧 Proposal 失去资格?”
两者可以同时使用,但解决的是不同问题。
与 Revision Guard 的组合
典型 acceptance:
1. Load proposal source R50
2. Load current R54
3. Evaluate materiality/freshness
4. Produce allow decision
5. Transaction checks current still R54
6. Commit acceptance/current pointer
第 3 步是 temporal validity;第 5 步是 concurrency guard。
如果只做第 3 步,validation 后到 commit 前仍可能有 R55 出现。
TOCTOU:检查通过后世界又变了
这就是 time-of-check-to-time-of-use 问题。
check current=R54
→ allow
→ before commit, R55 arrives
→ accept based on stale check ❌
所以关键 transition 要在事务中再次确认 revision/invariant。
这是一条非常典型的“领域 authorization + 数据库并发控制”组合。
Temporal Validity 与 Configuration Validity 也是两个轴
Proposal 可能:
source BodyState still valid
但生成它的 Agent Configuration 已被紧急撤回。
是否允许历史 proposal 被继续接受,需要独立 policy。
可能:
configuration revoked for safety
→ pending proposals blocked
也可能某些非安全升级不影响已经 generated proposal。
所以 production system 还需要明确:
state freshness
vs
configuration lifecycle
Behavioral Contract
可以为 Treatment 定义:
proposal acceptance state != proposed
→ acceptance forbidden
current BodyState revision < source revision
→ fail closed
source Diagnosis stale
→ acceptance forbidden
active safety concern
→ proposal/acceptance forbidden
current revision advanced + material change
→ old proposal acceptance forbidden
current revision advanced + no material change
→ acceptance may remain allowed
再加:
validation revision must equal commit-time revision
避免 TOCTOU。
DecisionTrace 应保存两个世界
Acceptance trace 至少需要能解释:
source_body_state_revision = R50
current_body_state_revision = R54
material_change = false
source_diagnosis = D20
freshness = fresh
safety = clear
policy = treatment-go-acceptance-v1
→ ALLOW
这样以后才能回答为什么一个 revision drift 的 proposal 仍然被接受。
Temporal Policy 也应该版本化
“什么 change 算 material”会演化。
例如 v1:
any new radiating symptom → material
v2 可能细分:
transient low-severity observation → review only
progressive neurological sign → block/escalate
这是行为变化,应通过 policy revision/qualification 管理,而不是在代码里静默修改。
测试 Temporal Validity 应覆盖什么
Same revision
current == source
→ no drift path
Advanced but non-material
R50→R51 unrelated update
→ acceptance may pass
Advanced and material
new red flag / relevant constraint
→ block
Diagnosis stale
immutable D20 still exists
freshness(D20,current)=stale
→ block
Impossible revision order
current < source
→ fail closed
TOCTOU
validation on R54
commit sees R55
→ transaction must fail/retry
Ongoing review
accepted treatment + worsening Outcome
→ review_recommended / pause path
自测题
- 为什么 Proposal 放了很久不一定失效,刚生成也可能立刻失效?
- Source Context 为什么不能只保存 created_at?
- Revision drift 为什么只是 re-evaluation trigger,不一定是 blocker?
- Material Change 为什么应相对于当前 Treatment/Intervention 判断?
- DiagnosisAnalysis immutable 与 Diagnosis freshness 如何同时成立?
- 为什么 acceptance 必须重新读取 server-side durable truth?
- Reauthorization 为什么不等于重新调用 LLM?
- Temporal validity 和 optimistic locking 各解决什么?
- TOCTOU 为什么要求 commit-time revision guard?
- Accepted Treatment 为什么仍需要 ongoing authorization/review?
最值得记住的一句话
Action-oriented artifacts have a validity window.
对现实世界采取行动的方案,不仅要问“当时生成得对不对”,还要问“在现在这个世界里,它是否仍然有资格生效”。