package counterfactual import ( "errors" "time" ) // MetadataKey is the canonical map key under which a Deliverable should be // stored when embedded in staging.Record.Metadata or any other free-form // map[string]any context bag. Cross-package consumers reading the bag use // this constant instead of stringly-typed literals. // // MetadataKey 是 Deliverable 嵌入 staging.Record.Metadata (或其他自由格式 // map[string]any 上下文袋) 时的规范 map key. 跨包消费方读取时统一用本常量, // 避免散落的字面量. const MetadataKey = "flyto.counterfactual.deliverable" // Verdict is the reverse-thinking conclusion strength. Values mirror the // Anthropic-compatible JSON schema returned by ~/.claude/skills/reverse-think/ // (see SKILL.md prompt template). // // Verdict 是反向思维结论的强度. 取值对齐 ~/.claude/skills/reverse-think/ // 返回的 Anthropic 兼容 JSON schema (见 SKILL.md 的 prompt 模板). type Verdict string const ( // VerdictAHolds means the original recommendation survives the // reverse pass; proceed. // // VerdictAHolds 表示原方案经反向思维仍成立, 可继续. VerdictAHolds Verdict = "A_holds" // VerdictBWins means the alternative wins; switch course. // // VerdictBWins 表示替代方案胜出, 应换路径. VerdictBWins Verdict = "B_wins" // VerdictDepends means the answer hinges on a dimension neither // option fully captures; needs more information before commit. // // VerdictDepends 表示答案取决于双方都未充分捕捉的维度, 需补足信息再决定. VerdictDepends Verdict = "depends_on_X" ) // Step labels which of the five CLAUDE.md article-1 phases produced this // Deliverable. Values are open-ended strings (not strict enum) so future // or industry-specific phases can extend without breaking schema. // // Step 标记本 Deliverable 出自 CLAUDE.md 原则 1 五步中的哪一步. 取值是 // 开放字符串 (非严格枚举), 未来或行业特定步骤可扩展不破坏 schema. const ( StepRead = "read" // 读早期方案代码 StepCleverUgly = "clever_ugly" // 标精妙 / 操蛋 StepElevated = "elevated" // 升华思维 (CLI/SDK/API/跨行业) StepReverse = "reverse" // 反向思维 StepConfirmation = "confirmation" // 等待确认 ) // Deliverable is the structured product of one reverse-thinking pass. // JSON tag names mirror the prompt template in // ~/.claude/skills/reverse-think/SKILL.md so a raw MiniMax response can // be unmarshaled directly into this struct (after wrapping the metadata // fields, see reverse_think.Client.Run in core/pkg/skills/reverse_think). // // Deliverable 是一次反向思维的结构化产物. JSON tag 字段名对齐 // ~/.claude/skills/reverse-think/SKILL.md 的 prompt 模板, 让 MiniMax 原始 // 返回可直接 unmarshal 入本结构 (元数据字段由 reverse_think.Client.Run // 包装, 见 core/pkg/skills/reverse_think). type Deliverable struct { // HiddenAssumptions lists the implicit assumptions the original // recommendation depends on. Surfaced by the reverse pass. Empty // slice = no hidden assumptions found (a clean run). // // HiddenAssumptions 列原方案依赖但未明说的隐含前提. 反向思维识别. // 空 slice = 没找到隐含假设 (反向思维通过). HiddenAssumptions []string `json:"hidden_assumptions"` // FailureScenarios lists concrete failure modes if the original // recommendation is wrong. // // FailureScenarios 列原方案如果错了具体怎么挂. FailureScenarios []string `json:"failure_scenarios"` // Verdict is the conclusion: original holds, alternative wins, // or depends on more information. // // Verdict 是结论: 原方案成立 / 替代胜出 / 依赖更多信息. Verdict Verdict `json:"verdict"` // VerdictReason is a one to two sentence summary justifying Verdict. // // VerdictReason 是一两句话总结 Verdict 的依据. VerdictReason string `json:"verdict_reason"` // ToolName is the tool whose call site triggered this reverse pass. // Empty when the Deliverable is not associated with a tool call // (e.g. a free-standing design review). // // ToolName 是触发本次反向思维的工具调用名. 不与工具调用关联时 // (例如独立设计 review) 留空. ToolName string `json:"tool_name,omitempty"` // Step labels which of the five article-1 phases produced this. // See the Step* constants. Empty = unspecified (caller did not tag). // // Step 标记五步中哪一步产出. 见 Step* 常量. 空 = 未指定. Step string `json:"step,omitempty"` // DecisionID optionally correlates this Deliverable with a // staging.Record or any decision identifier the consumer assigns. // Empty when no decision context is in scope. // // DecisionID 可选, 把 Deliverable 与 staging.Record 或消费方约定的 // 决策 id 关联. 无决策上下文时留空. DecisionID string `json:"decision_id,omitempty"` // OccurredAt is when the reverse pass produced this Deliverable. // Set by the producer (e.g. reverse_think.Client.Run uses time.Now() // at response receipt). Never auto-stamped by Validate or any // downstream consumer -- a missing timestamp signals a programming // error in the producer rather than a sane default. // // OccurredAt 是反向思维产出本 Deliverable 的时刻. 由生产方设置 (例如 // reverse_think.Client.Run 在收到响应时取 time.Now()). Validate 和下游 // 消费方都不自动补全 -- 时间戳缺失表示生产方编码 bug, 不应静默兜底. OccurredAt time.Time `json:"occurred_at"` } // ErrVerdictRequired is returned by Validate when Verdict is empty. // // ErrVerdictRequired 在 Verdict 为空时由 Validate 返回. var ErrVerdictRequired = errors.New("counterfactual: verdict required") // ErrVerdictReasonRequired is returned by Validate when VerdictReason is empty. // // ErrVerdictReasonRequired 在 VerdictReason 为空时由 Validate 返回. var ErrVerdictReasonRequired = errors.New("counterfactual: verdict reason required") // ErrOccurredAtZero is returned by Validate when OccurredAt is the zero time. // Producers must stamp the response receipt timestamp; missing one signals a // producer bug, not a default-able field. // // ErrOccurredAtZero 在 OccurredAt 为零值时由 Validate 返回. 生产方必须打 // 响应到达时间戳, 缺失表示生产方 bug, 本字段不该有默认. var ErrOccurredAtZero = errors.New("counterfactual: occurred_at must be set by producer") // Validate checks the minimal contract for downstream consumption: // non-empty Verdict, non-empty VerdictReason, non-zero OccurredAt. // HiddenAssumptions and FailureScenarios are allowed empty (a clean // reverse pass legitimately finds nothing). ToolName / Step / DecisionID // are optional context fields. // // CLEVER: Validate intentionally does NOT enforce Verdict ∈ {A_holds, // B_wins, depends_on_X} so MiniMax (or future LLMs) can introduce new // verdict labels without core release. The cost is downstream consumers // must handle unknown verdicts; the benefit is forward compatibility // without coordinated upgrade. // // Validate 检查下游消费的最小契约: Verdict 非空, VerdictReason 非空, // OccurredAt 非零值. HiddenAssumptions 与 FailureScenarios 允许空 // (干净的反向思维 pass 合法地找不到东西). ToolName / Step / DecisionID // 是可选上下文字段. // // 精妙之处 (CLEVER): Validate 刻意不强制 Verdict ∈ {A_holds, B_wins, // depends_on_X}, 让 MiniMax (或未来 LLM) 引入新 verdict 标签时不必等 core // 发版. 代价是下游要处理未知 verdict; 收益是无须协调升级的向前兼容. // // 替代方案 (已否决): Validate 把 Verdict 限定在三个常量 -- 否决: 任何 // 新 verdict 都得改 core 发版, 与 docs/api-reference.md "schema-agnostic" // 原则冲突. func (d *Deliverable) Validate() error { if d.Verdict == "" { return ErrVerdictRequired } if d.VerdictReason == "" { return ErrVerdictReasonRequired } if d.OccurredAt.IsZero() { return ErrOccurredAtZero } return nil } // Clone returns a deep copy of the Deliverable. Slices are duplicated // so consumers can mutate the returned value without affecting the // original (relevant for audit trails that snapshot at multiple stages). // // Clone 返回 Deliverable 的深拷贝. slice 复制, 消费方可改返回值不影响 // 原值 (审计链多阶段快照场景需要). func (d *Deliverable) Clone() *Deliverable { if d == nil { return nil } out := *d if d.HiddenAssumptions != nil { out.HiddenAssumptions = append([]string(nil), d.HiddenAssumptions...) } if d.FailureScenarios != nil { out.FailureScenarios = append([]string(nil), d.FailureScenarios...) } return &out }