scene-capability-engine 3.4.5 → 3.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.4.6] - 2026-03-02
11
+
12
+ ### Added
13
+ - Default problem-closure gate and policy baseline:
14
+ - new script: `scripts/problem-closure-gate.js`
15
+ - new managed config: `.sce/config/problem-closure-policy.json` (and template counterpart)
16
+ - `studio verify/release` standard profiles now execute `problem-closure-gate` when spec context is available
17
+ - Spec domain modeling now includes a mandatory machine-readable problem contract artifact:
18
+ - `.sce/specs/<spec>/custom/problem-contract.json`
19
+ - generated via spec domain init/refresh/bootstrap flow
20
+
21
+ ### Changed
22
+ - Problem evaluation policy and runtime now enforce stronger closure dimensions by default:
23
+ - mandatory dimensions: `problem_contract`, `ontology_alignment`, `convergence`
24
+ - stage-level block policy tightened for plan/apply/release closure control
25
+ - Errorbook record contract now captures structured attempt context (`attempt_contract`) for better trial/verification traceability.
26
+ - Errorbook release gate now incorporates quality policy checks (`min_quality`) for unresolved verified entries.
27
+ - Takeover/adoption baseline and file classification now manage `problem-closure-policy.json` as first-class config.
28
+ - README/README.zh/command reference updated to document problem-domain closed loop, two-failed-round debug escalation, and closure gate workflows.
29
+
10
30
  ## [3.4.5] - 2026-03-02
11
31
 
12
32
  ### Fixed
package/README.md CHANGED
@@ -30,6 +30,7 @@ SCE is designed for teams that want AI agents to deliver software end-to-end wit
30
30
  | Autonomous delivery | `auto close-loop`, `close-loop-program`, `close-loop-controller` | Unattended bounded convergence |
31
31
  | Multi-agent orchestration | DAG scheduling, retries, 429 adaptive parallel control | Reliable parallel execution at scale |
32
32
  | Domain/ontology governance | problem-domain chain + scene template + gate validation | Fewer semantic regressions |
33
+ | Problem closure loop | problem-domain map + chain + `problem-contract` + closure gate | Root-cause-first fixes with bounded convergence |
33
34
  | Problem evaluation routing | Stage-level risk/evidence/readiness scoring with mandatory policy | Adaptive execution strategy with guarded apply/release |
34
35
  | Local timeline safety | `timeline save/auto/list/show/restore/push` + key-event auto checkpoints | Recoverable local history |
35
36
  | Errorbook-driven repair | Local + registry-backed error patterns and release gates | Faster diagnosis and safer fixes |
@@ -94,6 +95,20 @@ git push origin vX.Y.Z
94
95
 
95
96
  ---
96
97
 
98
+ ## Default Problem-Solving Loop
99
+
100
+ SCE now enforces a domain-closed diagnosis and repair route by default:
101
+
102
+ 1. Scope the problem first with scene artifacts (`problem-domain-map.md`, `scene-spec.md`, `problem-domain-chain.json`, `problem-contract.json`).
103
+ 2. Keep trial-and-error history in incident staging (`.sce/errorbook/staging/incidents/`) to avoid repeating failed attempts.
104
+ 3. Use problem evaluation to prioritize likely impact areas before applying/releasing changes.
105
+
106
+ Hard rule defaults:
107
+ - After two failed rounds on the same problem fingerprint, debug evidence is required in subsequent attempts.
108
+ - `studio verify/release` run `problem-closure-gate` by default when a spec is bound.
109
+
110
+ ---
111
+
97
112
  ## AI Agent Compatibility
98
113
 
99
114
  SCE is tool-agnostic and works with Codex, Claude Code, Cursor, Windsurf, VS Code Copilot, and other CLI-capable agents.
@@ -103,6 +118,7 @@ SCE is tool-agnostic and works with Codex, Claude Code, Cursor, Windsurf, VS Cod
103
118
  - Spec work is attached as child sessions and auto-archived.
104
119
  - Startup now auto-detects adopted projects and aligns takeover baseline defaults automatically.
105
120
  - Problem evaluation policy is enabled by default (`.sce/config/problem-eval-policy.json`) and evaluates every Studio stage.
121
+ - Problem closure policy is enabled by default (`.sce/config/problem-closure-policy.json`) and blocks verify/release bypass when required domain/problem evidence is missing.
106
122
  - Error handling now follows a full incident loop by default: every record attempt is staged first and auto-closed on verified/promoted outcomes.
107
123
  - You can inspect or force-align baseline explicitly:
108
124
  - `sce workspace takeover-audit --json`
@@ -110,6 +126,17 @@ SCE is tool-agnostic and works with Codex, Claude Code, Cursor, Windsurf, VS Cod
110
126
 
111
127
  ---
112
128
 
129
+ ## Important Version Changes
130
+
131
+ - `3.4.6`: Added default `problem-closure-gate` + `problem-contract` baseline and strengthened mandatory problem evaluation dimensions (`problem_contract`/`ontology_alignment`/`convergence`) for verify/release convergence control.
132
+ - `3.4.5`: `git-managed-gate` now treats worktree checks as advisory in default relaxed CI mode (`CI/GITHUB_ACTIONS`, non-strict), preventing false release blocking.
133
+ - `3.4.4`: Added `SCE_GIT_MANAGEMENT_ALLOW_UNTRACKED=1` / `--allow-untracked`; release workflow uses it for npm publish after generating release evidence artifacts.
134
+ - `3.4.3`: Introduced mandatory problem evaluation across Studio stages (`plan/generate/apply/verify/release`) with policy file `.sce/config/problem-eval-policy.json` and stage report artifacts.
135
+ - `3.4.2`: Errorbook incident flow moved to full staging closed-loop (attempt history, incident inspection, resolved archive).
136
+ - `3.4.1`: Added workspace takeover baseline automation (`takeover-audit` / `takeover-apply`) and startup alignment defaults.
137
+
138
+ ---
139
+
113
140
  ## Documentation Map
114
141
 
115
142
  Start here:
@@ -147,5 +174,5 @@ MIT. See [LICENSE](LICENSE).
147
174
 
148
175
  ---
149
176
 
150
- **Version**: 3.4.5
177
+ **Version**: 3.4.6
151
178
  **Last Updated**: 2026-03-02
package/README.zh.md CHANGED
@@ -30,6 +30,7 @@ SCE 面向希望让 AI Agent 端到端推进交付、同时保持治理可控的
30
30
  | 自动闭环交付 | `auto close-loop`、`close-loop-program`、`close-loop-controller` | 无人值守有界收敛 |
31
31
  | 多 Agent 编排 | DAG 调度、重试、429 自适应并行 | 并行执行稳定可控 |
32
32
  | 领域/本体治理 | problem-domain chain + scene template + gate 校验 | 降低语义回归 |
33
+ | 问题闭环治理 | problem-domain map + chain + `problem-contract` + closure gate | 根因优先修复,过程有界收敛 |
33
34
  | 问题评估路由 | 分阶段风险/证据/就绪度评分 + 强制策略 | `apply/release` 可控阻断,执行路径自适应 |
34
35
  | 本地时间线安全 | `timeline save/auto/list/show/restore/push` + 关键节点自动打点 | 本地历史可回放可恢复 |
35
36
  | Errorbook 修复体系 | 本地 + 注册表错题库 + 发布门禁 | 定位更快、修复更稳 |
@@ -94,6 +95,20 @@ git push origin vX.Y.Z
94
95
 
95
96
  ---
96
97
 
98
+ ## 默认问题解决闭环
99
+
100
+ SCE 默认按“问题域闭环”推进诊断与修复:
101
+
102
+ 1. 先收敛问题域边界:`problem-domain-map.md`、`scene-spec.md`、`problem-domain-chain.json`、`problem-contract.json`。
103
+ 2. 试错过程进入 incident staging(`.sce/errorbook/staging/incidents/`),避免重复犯错。
104
+ 3. 由 problem evaluation 在变更前优先排序高相关区域,再进入 apply/release。
105
+
106
+ 默认硬规则:
107
+ - 同一问题指纹失败两轮后,后续尝试必须补充 debug 证据。
108
+ - 当 spec 绑定时,`studio verify/release` 默认执行 `problem-closure-gate`。
109
+
110
+ ---
111
+
97
112
  ## AI Agent 适配
98
113
 
99
114
  SCE 对工具无锁定,可接入 Codex、Claude Code、Cursor、Windsurf、VS Code Copilot 等。
@@ -103,6 +118,7 @@ SCE 对工具无锁定,可接入 Codex、Claude Code、Cursor、Windsurf、VS
103
118
  - Spec 执行作为子会话自动归档,支持跨轮次追踪。
104
119
  - 启动时会自动识别已接管项目并对齐接管基线默认配置。
105
120
  - 问题评估策略默认启用(`.sce/config/problem-eval-policy.json`),Studio 各阶段都会执行评估。
121
+ - 问题闭环策略默认启用(`.sce/config/problem-closure-policy.json`),缺失必要问题/领域证据时会在 verify/release 阶段阻断。
106
122
  - 错误处理默认进入完整 incident 闭环:每次记录先落到 staging 试错链路,verified/promoted 后自动收束归档。
107
123
  - 也可显式审计/修正接管基线:
108
124
  - `sce workspace takeover-audit --json`
@@ -110,6 +126,17 @@ SCE 对工具无锁定,可接入 Codex、Claude Code、Cursor、Windsurf、VS
110
126
 
111
127
  ---
112
128
 
129
+ ## 重要版本变更
130
+
131
+ - `3.4.6`:新增默认 `problem-closure-gate` + `problem-contract` 基线,并强化问题评估强制维度(`problem_contract`/`ontology_alignment`/`convergence`),提升 verify/release 收敛控制。
132
+ - `3.4.5`:`git-managed-gate` 在默认 CI 放宽模式下(`CI/GITHUB_ACTIONS` 且非 strict)对工作区变更改为告警,不再误阻断发布。
133
+ - `3.4.4`:新增 `SCE_GIT_MANAGEMENT_ALLOW_UNTRACKED=1` / `--allow-untracked`;发布工作流在 npm publish 前生成证据资产时可放行未跟踪文件。
134
+ - `3.4.3`:Studio 全阶段接入强制问题评估(`plan/generate/apply/verify/release`),并引入策略文件 `.sce/config/problem-eval-policy.json` 与评估报告落盘。
135
+ - `3.4.2`:Errorbook 升级为完整 incident staging 闭环(尝试记录、incident 查询、resolved 归档)。
136
+ - `3.4.1`:新增 workspace takeover baseline 自动化(`takeover-audit` / `takeover-apply`)与启动对齐能力。
137
+
138
+ ---
139
+
113
140
  ## 文档导航
114
141
 
115
142
  建议先看:
@@ -147,5 +174,5 @@ MIT,见 [LICENSE](LICENSE)。
147
174
 
148
175
  ---
149
176
 
150
- **版本**:3.4.5
177
+ **版本**:3.4.6
151
178
  **最后更新**:2026-03-02
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Quick reference for all `sce` commands
4
4
 
5
- **Version**: 3.4.5
5
+ **Version**: 3.4.6
6
6
  **Last Updated**: 2026-03-02
7
7
 
8
8
  ---
@@ -11,7 +11,7 @@
11
11
 
12
12
  The CLI provides three command aliases:
13
13
  - `sce` - **Recommended primary command** (use this in all documentation)
14
- - `sce` - Legacy short alias (compatible)
14
+ - `sco` - Legacy short alias (compatible)
15
15
  - `scene-capability-engine` - Legacy full alias (compatible)
16
16
 
17
17
  **Always use `sce` in new examples and documentation.**
@@ -24,7 +24,7 @@ The CLI provides three command aliases:
24
24
  npm install -g scene-capability-engine
25
25
  ```
26
26
 
27
- This creates the `sce` command globally. Legacy aliases `sce` and `scene-capability-engine` are still available.
27
+ This creates the `sce` command globally. Legacy aliases `sco` and `scene-capability-engine` are still available.
28
28
 
29
29
  ---
30
30
 
@@ -61,6 +61,7 @@ sce spec bootstrap --name 01-00-feature-name --scene scene.customer-order-invent
61
61
  # - .sce/specs/<spec>/custom/problem-domain-map.md
62
62
  # - .sce/specs/<spec>/custom/scene-spec.md
63
63
  # - .sce/specs/<spec>/custom/problem-domain-chain.json (machine-readable chain model)
64
+ # - .sce/specs/<spec>/custom/problem-contract.json (problem closure contract)
64
65
 
65
66
  # Run pipeline for one Spec
66
67
  sce spec pipeline run --spec 01-00-feature-name --scene scene.customer-order-inventory
@@ -92,11 +93,12 @@ Spec session governance:
92
93
  - `spec bootstrap|pipeline run|gate run` must bind to an active scene primary session (`--scene <scene-id>` or implicit binding from latest/unique active scene).
93
94
  - When multiple active scenes exist, you must pass `--scene` explicitly.
94
95
  - Multi-Spec orchestrate fallback (`--specs ...`) follows the same scene binding and writes per-spec child-session archive records.
95
- - `spec bootstrap` always generates problem-domain and scene-spec artifacts to force domain-first exploration.
96
+ - `spec bootstrap` always generates problem-domain, scene-spec, and `problem-contract` artifacts to force domain-first exploration.
96
97
  - `spec gate` now hard-fails when either of the following is missing or structurally incomplete:
97
98
  - `.sce/specs/<spec>/custom/problem-domain-map.md`
98
99
  - `.sce/specs/<spec>/custom/scene-spec.md`
99
100
  - `.sce/specs/<spec>/custom/problem-domain-chain.json`
101
+ - `problem-contract.json` is enforced by Studio stage gates (`problem-closure-gate`) during `verify/release`.
100
102
  - Closed-loop scene research baseline is now part of domain modeling artifacts:
101
103
  - `problem-domain-map.md` must include `Closed-Loop Research Coverage Matrix`
102
104
  - `scene-spec.md` must include `Closed-Loop Research Contract`
@@ -567,13 +569,17 @@ Stage guardrails are enforced by default:
567
569
  Problem evaluation mode (default required):
568
570
  - Studio now runs problem evaluation on every stage: `plan`, `generate`, `apply`, `verify`, `release`.
569
571
  - Default policy file: `.sce/config/problem-eval-policy.json` (also provisioned by template/adopt/takeover baseline).
570
- - Default hard-block stages: `apply`, `release`.
572
+ - Mandatory dimensions: `problem_contract`, `ontology_alignment`, `convergence`.
573
+ - Default hard-block behavior:
574
+ - stage block policy: `apply`, `release`
575
+ - dimension block policy: `problem_contract` blocks on `plan/apply/release`; `ontology_alignment` blocks on `apply/release`; `convergence` blocks on `release`
571
576
  - Evaluation combines risk/evidence/readiness and emits adaptive strategy:
572
577
  - `direct-execution`
573
578
  - `controlled-execution`
574
579
  - `evidence-first`
575
580
  - `explore-and-validate`
576
581
  - `debug-first`
582
+ - Default retry rule: after two failed rounds on the same fingerprint, subsequent attempts must include debug evidence.
577
583
  - Evaluation report artifact is written to `.sce/reports/problem-eval/<job-id>-<stage>.json`.
578
584
  - Stage metadata and event payload now include `problem_evaluation` summary plus artifact pointer.
579
585
  - Environment overrides:
@@ -581,11 +587,35 @@ Problem evaluation mode (default required):
581
587
  - `SCE_PROBLEM_EVAL_DISABLED=1`
582
588
 
583
589
  Studio gate execution defaults:
584
- - `verify --profile standard` runs executable gates (unit test script when available, interactive governance report when present, scene package publish-batch dry-run when handoff manifest exists)
585
- - `release --profile standard` runs executable release preflight (npm pack dry-run, git managed gate, errorbook release gate, weekly ops gate when summary exists, release asset integrity when evidence directory exists, scene package publish-batch ontology gate, handoff capability matrix gate)
590
+ - `verify --profile standard` runs executable gates (unit test script when available, interactive governance report when present, scene package publish-batch dry-run when handoff manifest exists, `problem-closure-gate` when spec context is available)
591
+ - `release --profile standard` runs executable release preflight (npm pack dry-run, git managed gate, errorbook release gate, weekly ops gate when summary exists, release asset integrity when evidence directory exists, scene package publish-batch ontology gate, handoff capability matrix gate, `problem-closure-gate` when spec context is available)
586
592
  - `verify/release --profile strict` fails when any required gate step is skipped (for example missing manifest/evidence/scripts)
587
593
  - Required gate failures are auto-recorded into `.sce/errorbook` as `candidate` entries (tagged `release-blocker`) for follow-up triage.
588
594
 
595
+ Problem closure gate (default policy):
596
+ - Script: `node scripts/problem-closure-gate.js`
597
+ - Policy file: `.sce/config/problem-closure-policy.json` (auto-provisioned by `init/adopt/takeover`)
598
+ - Checks:
599
+ - verify stage: `problem-contract` + spec domain validation + domain coverage
600
+ - release stage: verify checks + verify report pass signal + governance high-alert block (configurable)
601
+
602
+ ```bash
603
+ # Verify-stage closure gate
604
+ node scripts/problem-closure-gate.js \
605
+ --stage verify \
606
+ --spec 01-00-customer-order-inventory \
607
+ --fail-on-block \
608
+ --json
609
+
610
+ # Release-stage closure gate
611
+ node scripts/problem-closure-gate.js \
612
+ --stage release \
613
+ --spec 01-00-customer-order-inventory \
614
+ --verify-report .sce/reports/studio/verify-<job-id>.json \
615
+ --fail-on-block \
616
+ --json
617
+ ```
618
+
589
619
  Authorization model (optional, policy-driven):
590
620
  - Enable policy: `SCE_STUDIO_REQUIRE_AUTH=1`
591
621
  - Secret env key: `SCE_STUDIO_AUTH_PASSWORD` (or override key name with `SCE_STUDIO_PASSWORD_ENV`)
Binary file
@@ -119,6 +119,7 @@ class AdoptionStrategy {
119
119
  'config/session-governance.json',
120
120
  'config/spec-domain-policy.json',
121
121
  'config/problem-eval-policy.json',
122
+ 'config/problem-closure-policy.json',
122
123
  'specs/SPEC_WORKFLOW_GUIDE.md',
123
124
  'hooks/sync-tasks-on-edit.sce.hook',
124
125
  'hooks/check-spec-on-create.sce.hook',
@@ -168,6 +168,7 @@ class DetectionEngine {
168
168
  'config/session-governance.json',
169
169
  'config/spec-domain-policy.json',
170
170
  'config/problem-eval-policy.json',
171
+ 'config/problem-closure-policy.json',
171
172
  'README.md',
172
173
  'ultrawork-application-guide.md',
173
174
  'ultrawork-integration-summary.md',
@@ -69,7 +69,8 @@ class FileClassifier {
69
69
  'config/takeover-baseline.json',
70
70
  'config/session-governance.json',
71
71
  'config/spec-domain-policy.json',
72
- 'config/problem-eval-policy.json'
72
+ 'config/problem-eval-policy.json',
73
+ 'config/problem-closure-policy.json'
73
74
  ];
74
75
 
75
76
  // Generated directory patterns
@@ -289,6 +289,7 @@ class SmartOrchestrator {
289
289
  'config/session-governance.json',
290
290
  'config/spec-domain-policy.json',
291
291
  'config/problem-eval-policy.json',
292
+ 'config/problem-closure-policy.json',
292
293
  'README.md'
293
294
  ];
294
295
 
@@ -48,6 +48,7 @@ const ONTOLOGY_TAG_ALIASES = Object.freeze({
48
48
  action_chain: 'execution_flow'
49
49
  });
50
50
  const DEFAULT_PROMOTE_MIN_QUALITY = 75;
51
+ const DEFAULT_RELEASE_GATE_MIN_QUALITY = 70;
51
52
  const ERRORBOOK_RISK_LEVELS = Object.freeze(['low', 'medium', 'high']);
52
53
  const DEBUG_EVIDENCE_TAGS = Object.freeze([
53
54
  'debug-evidence',
@@ -515,11 +516,21 @@ async function writeIncident(paths, incident, fileSystem = fs) {
515
516
  }
516
517
 
517
518
  function createIncidentAttemptSignature(payload = {}) {
519
+ const attemptContract = payload && payload.attempt_contract && typeof payload.attempt_contract === 'object'
520
+ ? payload.attempt_contract
521
+ : {};
518
522
  const basis = JSON.stringify({
519
523
  root_cause: normalizeText(payload.root_cause),
520
524
  fix_actions: normalizeStringList(payload.fix_actions),
521
525
  verification_evidence: normalizeStringList(payload.verification_evidence),
522
526
  notes: normalizeText(payload.notes),
527
+ attempt_contract: {
528
+ hypothesis: normalizeText(attemptContract.hypothesis),
529
+ change_points: normalizeStringList(attemptContract.change_points),
530
+ verification_result: normalizeText(attemptContract.verification_result),
531
+ rollback_point: normalizeText(attemptContract.rollback_point),
532
+ conclusion: normalizeText(attemptContract.conclusion)
533
+ },
523
534
  source: {
524
535
  spec: normalizeText(payload?.source?.spec),
525
536
  files: normalizeStringList(payload?.source?.files),
@@ -600,6 +611,15 @@ async function syncIncidentLoopForRecord(paths, payload = {}, entry = {}, option
600
611
  tags: normalizeStringList(payload.tags || entry.tags),
601
612
  ontology_tags: normalizeOntologyTags(payload.ontology_tags || entry.ontology_tags),
602
613
  notes: normalizeText(payload.notes || entry.notes),
614
+ attempt_contract: {
615
+ hypothesis: normalizeText(payload?.attempt_contract?.hypothesis || entry?.attempt_contract?.hypothesis),
616
+ change_points: normalizeStringList(payload?.attempt_contract?.change_points || entry?.attempt_contract?.change_points),
617
+ verification_result: normalizeText(
618
+ payload?.attempt_contract?.verification_result || entry?.attempt_contract?.verification_result
619
+ ),
620
+ rollback_point: normalizeText(payload?.attempt_contract?.rollback_point || entry?.attempt_contract?.rollback_point),
621
+ conclusion: normalizeText(payload?.attempt_contract?.conclusion || entry?.attempt_contract?.conclusion)
622
+ },
603
623
  source: {
604
624
  spec: normalizeText(payload?.source?.spec || entry?.source?.spec),
605
625
  files: normalizeStringList(payload?.source?.files || entry?.source?.files),
@@ -695,6 +715,19 @@ function scoreQuality(entry = {}) {
695
715
  if (normalizeText(entry.symptom).length >= 24 && normalizeText(entry.root_cause).length >= 24) {
696
716
  score += 2;
697
717
  }
718
+ const attemptContract = entry && typeof entry.attempt_contract === 'object'
719
+ ? entry.attempt_contract
720
+ : {};
721
+ const attemptContractComplete = Boolean(
722
+ normalizeText(attemptContract.hypothesis)
723
+ && normalizeStringList(attemptContract.change_points).length > 0
724
+ && normalizeText(attemptContract.verification_result)
725
+ && normalizeText(attemptContract.rollback_point)
726
+ && normalizeText(attemptContract.conclusion)
727
+ );
728
+ if (attemptContractComplete) {
729
+ score += 5;
730
+ }
698
731
 
699
732
  return Math.max(0, Math.min(100, score));
700
733
  }
@@ -712,6 +745,24 @@ function validateRecordPayload(payload) {
712
745
  if (!Array.isArray(payload.fix_actions) || payload.fix_actions.length === 0) {
713
746
  throw new Error('at least one --fix-action is required');
714
747
  }
748
+ const attemptContract = payload.attempt_contract && typeof payload.attempt_contract === 'object'
749
+ ? payload.attempt_contract
750
+ : {};
751
+ if (!normalizeText(attemptContract.hypothesis)) {
752
+ throw new Error('attempt contract requires hypothesis');
753
+ }
754
+ if (!Array.isArray(attemptContract.change_points) || attemptContract.change_points.length === 0) {
755
+ throw new Error('attempt contract requires change_points');
756
+ }
757
+ if (!normalizeText(attemptContract.verification_result)) {
758
+ throw new Error('attempt contract requires verification_result');
759
+ }
760
+ if (!normalizeText(attemptContract.rollback_point)) {
761
+ throw new Error('attempt contract requires rollback_point');
762
+ }
763
+ if (!normalizeText(attemptContract.conclusion)) {
764
+ throw new Error('attempt contract requires conclusion');
765
+ }
715
766
 
716
767
  const status = normalizeStatus(payload.status, 'candidate');
717
768
  if (status === 'promoted') {
@@ -807,6 +858,42 @@ function normalizeRecordPayload(options = {}, fromFilePayload = {}) {
807
858
  })
808
859
  };
809
860
 
861
+ const attemptContractFromFile = fromFilePayload && typeof fromFilePayload.attempt_contract === 'object'
862
+ ? fromFilePayload.attempt_contract
863
+ : {};
864
+ payload.attempt_contract = {
865
+ hypothesis: normalizeText(
866
+ options.attemptHypothesis
867
+ || attemptContractFromFile.hypothesis
868
+ || payload.root_cause
869
+ ),
870
+ change_points: normalizeStringList(
871
+ options.attemptChangePoints,
872
+ attemptContractFromFile.change_points,
873
+ attemptContractFromFile.changePoints,
874
+ payload.fix_actions
875
+ ),
876
+ verification_result: normalizeText(
877
+ options.attemptVerificationResult
878
+ || attemptContractFromFile.verification_result
879
+ || attemptContractFromFile.verificationResult
880
+ || (payload.verification_evidence[0] || '')
881
+ || 'pending-verification'
882
+ ),
883
+ rollback_point: normalizeText(
884
+ options.attemptRollbackPoint
885
+ || attemptContractFromFile.rollback_point
886
+ || attemptContractFromFile.rollbackPoint
887
+ || 'not-required'
888
+ ),
889
+ conclusion: normalizeText(
890
+ options.attemptConclusion
891
+ || attemptContractFromFile.conclusion
892
+ || payload.notes
893
+ || payload.root_cause
894
+ )
895
+ };
896
+
810
897
  return payload;
811
898
  }
812
899
 
@@ -872,6 +959,30 @@ function mergeEntry(existingEntry, incomingPayload) {
872
959
  ontology_tags: normalizeOntologyTags(existingEntry.ontology_tags, incomingPayload.ontology_tags),
873
960
  status: selectStatus(existingEntry.status, incomingPayload.status),
874
961
  notes: normalizeText(incomingPayload.notes) || existingEntry.notes || '',
962
+ attempt_contract: {
963
+ hypothesis: normalizeText(incomingPayload?.attempt_contract?.hypothesis)
964
+ || normalizeText(existingEntry?.attempt_contract?.hypothesis)
965
+ || normalizeText(incomingPayload.root_cause)
966
+ || normalizeText(existingEntry.root_cause),
967
+ change_points: normalizeStringList(
968
+ existingEntry?.attempt_contract?.change_points,
969
+ incomingPayload?.attempt_contract?.change_points,
970
+ incomingPayload.fix_actions
971
+ ),
972
+ verification_result: normalizeText(incomingPayload?.attempt_contract?.verification_result)
973
+ || normalizeText(existingEntry?.attempt_contract?.verification_result)
974
+ || normalizeStringList(incomingPayload.verification_evidence, existingEntry.verification_evidence)[0]
975
+ || '',
976
+ rollback_point: normalizeText(incomingPayload?.attempt_contract?.rollback_point)
977
+ || normalizeText(existingEntry?.attempt_contract?.rollback_point)
978
+ || 'not-required',
979
+ conclusion: normalizeText(incomingPayload?.attempt_contract?.conclusion)
980
+ || normalizeText(existingEntry?.attempt_contract?.conclusion)
981
+ || normalizeText(incomingPayload.notes)
982
+ || normalizeText(existingEntry.notes)
983
+ || normalizeText(incomingPayload.root_cause)
984
+ || normalizeText(existingEntry.root_cause)
985
+ },
875
986
  source: {
876
987
  spec: normalizeText(incomingPayload?.source?.spec) || normalizeText(existingEntry?.source?.spec),
877
988
  files: normalizeStringList(existingEntry?.source?.files, incomingPayload?.source?.files),
@@ -1395,7 +1506,7 @@ function evaluateEntryRisk(entry = {}) {
1395
1506
  if (hasHighRiskTag) {
1396
1507
  return 'high';
1397
1508
  }
1398
- if (status === 'candidate' && qualityScore >= 85) {
1509
+ if (status === 'candidate' && qualityScore > 85) {
1399
1510
  return 'high';
1400
1511
  }
1401
1512
  if (status === 'candidate' && qualityScore >= 75 && ontologyTags.includes('decision_policy')) {
@@ -1404,7 +1515,7 @@ function evaluateEntryRisk(entry = {}) {
1404
1515
  if (status === 'candidate') {
1405
1516
  return 'medium';
1406
1517
  }
1407
- if (qualityScore >= 85 && ontologyTags.includes('decision_policy')) {
1518
+ if (qualityScore > 85 && ontologyTags.includes('decision_policy')) {
1408
1519
  return 'high';
1409
1520
  }
1410
1521
  return 'medium';
@@ -1417,6 +1528,9 @@ async function evaluateErrorbookReleaseGate(options = {}, dependencies = {}) {
1417
1528
  const index = await readErrorbookIndex(paths, fileSystem);
1418
1529
  const minRisk = normalizeRiskLevel(options.minRisk || options.min_risk || 'high', 'high');
1419
1530
  const includeVerified = options.includeVerified === true;
1531
+ const minQuality = Number.isFinite(Number(options.minQuality || options.min_quality))
1532
+ ? Math.max(0, Math.min(100, Number(options.minQuality || options.min_quality)))
1533
+ : DEFAULT_RELEASE_GATE_MIN_QUALITY;
1420
1534
 
1421
1535
  const inspected = [];
1422
1536
  const mitigationInspected = [];
@@ -1474,6 +1588,14 @@ async function evaluateErrorbookReleaseGate(options = {}, dependencies = {}) {
1474
1588
  block_reasons: ['risk_threshold']
1475
1589
  }));
1476
1590
 
1591
+ const curationBlocked = inspected
1592
+ .filter((item) => item.status === 'verified' && Number(item.quality_score || 0) < minQuality)
1593
+ .map((item) => ({
1594
+ ...item,
1595
+ block_reasons: ['curation_quality'],
1596
+ policy_violations: [`quality_score<${minQuality}`]
1597
+ }));
1598
+
1477
1599
  const blockedById = new Map();
1478
1600
  for (const item of riskBlocked) {
1479
1601
  blockedById.set(item.id, {
@@ -1497,6 +1619,19 @@ async function evaluateErrorbookReleaseGate(options = {}, dependencies = {}) {
1497
1619
  }
1498
1620
  blockedById.set(existing.id, existing);
1499
1621
  }
1622
+ for (const item of curationBlocked) {
1623
+ const existing = blockedById.get(item.id);
1624
+ if (!existing) {
1625
+ blockedById.set(item.id, {
1626
+ ...item,
1627
+ policy_violations: normalizeStringList(item.policy_violations)
1628
+ });
1629
+ continue;
1630
+ }
1631
+ existing.block_reasons = normalizeStringList(existing.block_reasons, item.block_reasons);
1632
+ existing.policy_violations = normalizeStringList(existing.policy_violations, item.policy_violations);
1633
+ blockedById.set(existing.id, existing);
1634
+ }
1500
1635
 
1501
1636
  const blocked = Array.from(blockedById.values())
1502
1637
  .sort((left, right) => {
@@ -1519,12 +1654,14 @@ async function evaluateErrorbookReleaseGate(options = {}, dependencies = {}) {
1519
1654
  mode: 'errorbook-release-gate',
1520
1655
  gate: {
1521
1656
  min_risk: minRisk,
1657
+ min_quality: minQuality,
1522
1658
  include_verified: includeVerified,
1523
1659
  mitigation_policy_enforced: true
1524
1660
  },
1525
1661
  passed: blocked.length === 0,
1526
1662
  inspected_count: inspected.length,
1527
1663
  risk_blocked_count: riskBlocked.length,
1664
+ curation_blocked_count: curationBlocked.length,
1528
1665
  mitigation_inspected_count: mitigationInspected.length,
1529
1666
  mitigation_blocked_count: mitigationBlocked.length,
1530
1667
  blocked_count: blocked.length,
@@ -1609,6 +1746,7 @@ async function runErrorbookRecordCommand(options = {}, dependencies = {}) {
1609
1746
  source: normalized.source,
1610
1747
  temporary_mitigation: mitigationPayload,
1611
1748
  notes: normalized.notes || '',
1749
+ attempt_contract: normalized.attempt_contract,
1612
1750
  occurrences: 1
1613
1751
  };
1614
1752
  entry.quality_score = scoreQuality(entry);
@@ -2823,6 +2961,7 @@ function registerErrorbookCommands(program) {
2823
2961
  .command('release-gate')
2824
2962
  .description('Block release on unresolved high-risk entries and temporary-mitigation policy violations')
2825
2963
  .option('--min-risk <level>', 'Risk threshold (low|medium|high)', 'high')
2964
+ .option('--min-quality <n>', `Minimum quality for unresolved entries (default: ${DEFAULT_RELEASE_GATE_MIN_QUALITY})`, parseInt)
2826
2965
  .option('--include-verified', 'Also inspect verified (non-promoted) entries')
2827
2966
  .option('--fail-on-block', 'Exit with error when gate is blocked')
2828
2967
  .option('--json', 'Emit machine-readable JSON')
@@ -2873,6 +3012,7 @@ module.exports = {
2873
3012
  HIGH_RISK_SIGNAL_TAGS,
2874
3013
  DEBUG_EVIDENCE_TAGS,
2875
3014
  DEFAULT_PROMOTE_MIN_QUALITY,
3015
+ DEFAULT_RELEASE_GATE_MIN_QUALITY,
2876
3016
  ERRORBOOK_INCIDENT_INDEX_API_VERSION,
2877
3017
  ERRORBOOK_INCIDENT_API_VERSION,
2878
3018
  resolveErrorbookPaths,