scene-capability-engine 3.6.5 → 3.6.8
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 +38 -0
- package/README.md +3 -1
- package/README.zh.md +3 -1
- package/docs/command-reference.md +9 -1
- package/lib/commands/state.js +90 -0
- package/lib/state/sce-state-store.js +426 -0
- package/lib/state/state-migration-manager.js +141 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.6.8] - 2026-03-05
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- New one-shot state command:
|
|
14
|
+
- `sce state reconcile --all --apply --json`
|
|
15
|
+
- runs `doctor -> migrate -> doctor` with before/after drift summary in one payload
|
|
16
|
+
- Release workflow now runs state reconcile before reconciliation gate and uploads:
|
|
17
|
+
- `state-reconcile-<tag>.json`
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- Release workflow state reconciliation gate is now enforce-by-default (`KSE_RELEASE_STATE_MIGRATION_ENFORCE` defaults to `true`).
|
|
21
|
+
- Release workflow Node runtime updated to `22.x` so sqlite-backed reconciliation can run in release pipeline.
|
|
22
|
+
|
|
23
|
+
## [3.6.7] - 2026-03-05
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- `state-migration-reconciliation-gate` now treats `blocking` checks as advisory by default and only fails when `--fail-on-blocking` is explicitly set.
|
|
27
|
+
- Release workflow reconciliation step now adds `--fail-on-blocking` only in enforce mode, preventing advisory mode from failing npm publish.
|
|
28
|
+
|
|
29
|
+
## [3.6.6] - 2026-03-05
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- Stage-4 SQLite migration components for release evidence indexes:
|
|
33
|
+
- `release.evidence-runs-index` (`.sce/reports/release-evidence/handoff-runs.json`)
|
|
34
|
+
- `release.gate-history-index` (`.sce/reports/release-evidence/release-gate-history.json`)
|
|
35
|
+
- New SQLite index tables in state store:
|
|
36
|
+
- `release_evidence_run_registry`
|
|
37
|
+
- `release_gate_history_registry`
|
|
38
|
+
- New `SceStateStore` APIs:
|
|
39
|
+
- `upsert/listReleaseEvidenceRunRecords`
|
|
40
|
+
- `upsert/listReleaseGateHistoryRecords`
|
|
41
|
+
- Release workflow gate integration:
|
|
42
|
+
- new step `state_migration_reconciliation`
|
|
43
|
+
- new release asset `.sce/reports/release-evidence/state-migration-reconciliation-<tag>.json`
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
- `sce state plan/doctor/migrate/export` now includes release-evidence components in migration scope and parity summaries.
|
|
47
|
+
|
|
10
48
|
## [3.6.5] - 2026-03-05
|
|
11
49
|
|
|
12
50
|
### Added
|
package/README.md
CHANGED
|
@@ -149,11 +149,13 @@ Studio task-stream output contract (default):
|
|
|
149
149
|
- `sce state plan --json`
|
|
150
150
|
- `sce state doctor --json`
|
|
151
151
|
- `sce state migrate --all --apply --json`
|
|
152
|
+
- `sce state reconcile --all --apply --json` (doctor -> migrate -> doctor one-shot)
|
|
152
153
|
- `sce state export --out .sce/reports/state-migration/state-export.latest.json --json`
|
|
153
154
|
- reconciliation gate: `npm run gate:state-migration-reconciliation`
|
|
155
|
+
- release workflow defaults to enforce mode for state reconciliation gate and runs reconcile before publish
|
|
154
156
|
- runtime reads now prefer sqlite indexes for timeline/scene-session views when indexed data exists
|
|
155
157
|
- `state doctor` now emits `summary` and runtime diagnostics (`runtime.timeline`, `runtime.scene_session`) with read-source and consistency status
|
|
156
|
-
- migratable components now include errorbook + spec-governance indexes (`errorbook.entry-index`, `errorbook.incident-index`, `governance.spec-scene-overrides`, `governance.scene-index`)
|
|
158
|
+
- migratable components now include runtime + errorbook + spec-governance + release evidence indexes (`errorbook.entry-index`, `errorbook.incident-index`, `governance.spec-scene-overrides`, `governance.scene-index`, `release.evidence-runs-index`, `release.gate-history-index`)
|
|
157
159
|
- Write authorization lease model (SQLite-backed):
|
|
158
160
|
- policy file: `.sce/config/authorization-policy.json`
|
|
159
161
|
- grant lease: `sce auth grant --scope studio:* --reason "<reason>" --auth-password <password> --json`
|
package/README.zh.md
CHANGED
|
@@ -149,11 +149,13 @@ Studio 任务流输出契约(默认):
|
|
|
149
149
|
- `sce state plan --json`
|
|
150
150
|
- `sce state doctor --json`
|
|
151
151
|
- `sce state migrate --all --apply --json`
|
|
152
|
+
- `sce state reconcile --all --apply --json`(一键执行 doctor -> migrate -> doctor)
|
|
152
153
|
- `sce state export --out .sce/reports/state-migration/state-export.latest.json --json`
|
|
153
154
|
- 对账门禁:`npm run gate:state-migration-reconciliation`
|
|
155
|
+
- 发布工作流默认对 state reconciliation 启用 enforce,并在发布前执行 reconcile
|
|
154
156
|
- 运行时读取在存在索引数据时优先使用 SQLite(timeline/scene-session 视图)
|
|
155
157
|
- `state doctor` 新增 `summary` 与运行时诊断(`runtime.timeline`、`runtime.scene_session`),可直接读取读源与一致性状态
|
|
156
|
-
- 可迁移组件扩展到 errorbook + spec-governance 索引(`errorbook.entry-index`、`errorbook.incident-index`、`governance.spec-scene-overrides`、`governance.scene-index`)
|
|
158
|
+
- 可迁移组件扩展到 runtime + errorbook + spec-governance + release evidence 索引(`errorbook.entry-index`、`errorbook.incident-index`、`governance.spec-scene-overrides`、`governance.scene-index`、`release.evidence-runs-index`、`release.gate-history-index`)
|
|
157
159
|
- 写入授权租约模型(SQLite 持久化):
|
|
158
160
|
- 策略文件:`.sce/config/authorization-policy.json`
|
|
159
161
|
- 申请租约:`sce auth grant --scope studio:* --reason "<原因>" --auth-password <密码> --json`
|
|
@@ -719,6 +719,9 @@ sce state migrate --all --json
|
|
|
719
719
|
# apply migration writes into sqlite index tables
|
|
720
720
|
sce state migrate --all --apply --json
|
|
721
721
|
|
|
722
|
+
# reconcile in one flow (doctor -> migrate -> doctor)
|
|
723
|
+
sce state reconcile --all --apply --json
|
|
724
|
+
|
|
722
725
|
# migrate specific components
|
|
723
726
|
sce state migrate --component collab.agent-registry --component runtime.timeline-index --apply --json
|
|
724
727
|
|
|
@@ -727,7 +730,7 @@ sce state export --out .sce/reports/state-migration/state-export.latest.json --j
|
|
|
727
730
|
|
|
728
731
|
# reconciliation gate (non-blocking by default; choose strict flags as needed)
|
|
729
732
|
npm run gate:state-migration-reconciliation
|
|
730
|
-
node scripts/state-migration-reconciliation-gate.js --fail-on-alert --fail-on-pending --json
|
|
733
|
+
node scripts/state-migration-reconciliation-gate.js --fail-on-blocking --fail-on-alert --fail-on-pending --json
|
|
731
734
|
```
|
|
732
735
|
|
|
733
736
|
Current migratable components:
|
|
@@ -738,6 +741,8 @@ Current migratable components:
|
|
|
738
741
|
- `errorbook.incident-index` (`.sce/errorbook/staging/index.json`)
|
|
739
742
|
- `governance.spec-scene-overrides` (`.sce/spec-governance/spec-scene-overrides.json`)
|
|
740
743
|
- `governance.scene-index` (`.sce/spec-governance/scene-index.json`)
|
|
744
|
+
- `release.evidence-runs-index` (`.sce/reports/release-evidence/handoff-runs.json`)
|
|
745
|
+
- `release.gate-history-index` (`.sce/reports/release-evidence/release-gate-history.json`)
|
|
741
746
|
|
|
742
747
|
SQLite index tables introduced for gradual migration:
|
|
743
748
|
- `agent_runtime_registry`
|
|
@@ -747,11 +752,14 @@ SQLite index tables introduced for gradual migration:
|
|
|
747
752
|
- `errorbook_incident_index_registry`
|
|
748
753
|
- `governance_spec_scene_override_registry`
|
|
749
754
|
- `governance_scene_index_registry`
|
|
755
|
+
- `release_evidence_run_registry`
|
|
756
|
+
- `release_gate_history_registry`
|
|
750
757
|
- `state_migration_registry`
|
|
751
758
|
|
|
752
759
|
Runtime read preference:
|
|
753
760
|
- timeline/session runtime views now prefer SQLite indexes when indexed rows exist.
|
|
754
761
|
- file artifacts remain source-of-truth for content payload and recovery operations.
|
|
762
|
+
- release workflow runs `state reconcile --all --apply` before reconciliation gate.
|
|
755
763
|
- `sce state doctor --json` now includes:
|
|
756
764
|
- `summary` aggregate (`pending_components`, `total_record_drift`, `blocking_count`, `alert_count`)
|
|
757
765
|
- runtime read diagnostics (`runtime.timeline`, `runtime.scene_session`) with read-source/read-preference and consistency status
|
package/lib/commands/state.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
runStateDoctor,
|
|
9
9
|
runStateExport
|
|
10
10
|
} = require('../state/state-migration-manager');
|
|
11
|
+
const { getSceStateStore } = require('../state/sce-state-store');
|
|
11
12
|
|
|
12
13
|
function normalizeString(value) {
|
|
13
14
|
if (typeof value !== 'string') {
|
|
@@ -147,6 +148,85 @@ async function runStateExportCommand(options = {}, dependencies = {}) {
|
|
|
147
148
|
return payload;
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
async function runStateReconcileCommand(options = {}, dependencies = {}) {
|
|
152
|
+
const projectPath = dependencies.projectPath || process.cwd();
|
|
153
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
154
|
+
const env = dependencies.env || process.env;
|
|
155
|
+
const components = normalizeComponentInput(options.component);
|
|
156
|
+
const componentIds = options.all === true ? [] : components;
|
|
157
|
+
const apply = options.apply === true;
|
|
158
|
+
const stateStore = dependencies.stateStore || getSceStateStore(projectPath, {
|
|
159
|
+
fileSystem,
|
|
160
|
+
env
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const before = await runStateDoctor({}, {
|
|
164
|
+
projectPath,
|
|
165
|
+
fileSystem,
|
|
166
|
+
env,
|
|
167
|
+
stateStore
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const migration = await runStateMigration({
|
|
171
|
+
apply,
|
|
172
|
+
all: options.all === true,
|
|
173
|
+
componentIds
|
|
174
|
+
}, {
|
|
175
|
+
projectPath,
|
|
176
|
+
fileSystem,
|
|
177
|
+
env,
|
|
178
|
+
stateStore
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const after = await runStateDoctor({}, {
|
|
182
|
+
projectPath,
|
|
183
|
+
fileSystem,
|
|
184
|
+
env,
|
|
185
|
+
stateStore
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const beforePending = Number(before && before.summary && before.summary.pending_components) || 0;
|
|
189
|
+
const afterPending = Number(after && after.summary && after.summary.pending_components) || 0;
|
|
190
|
+
const beforeBlocking = Number(before && before.summary && before.summary.blocking_count) || 0;
|
|
191
|
+
const afterBlocking = Number(after && after.summary && after.summary.blocking_count) || 0;
|
|
192
|
+
const pendingReduced = Math.max(0, beforePending - afterPending);
|
|
193
|
+
const blockingReduced = Math.max(0, beforeBlocking - afterBlocking);
|
|
194
|
+
|
|
195
|
+
const payload = {
|
|
196
|
+
mode: 'state-reconcile',
|
|
197
|
+
success: Boolean(migration && migration.success) && afterBlocking === 0,
|
|
198
|
+
apply,
|
|
199
|
+
generated_at: new Date().toISOString(),
|
|
200
|
+
store_path: after && after.store_path ? after.store_path : null,
|
|
201
|
+
sqlite: after && after.sqlite ? after.sqlite : null,
|
|
202
|
+
migration,
|
|
203
|
+
before: {
|
|
204
|
+
summary: before && before.summary ? before.summary : null,
|
|
205
|
+
blocking: Array.isArray(before && before.blocking) ? before.blocking : [],
|
|
206
|
+
alerts: Array.isArray(before && before.alerts) ? before.alerts : []
|
|
207
|
+
},
|
|
208
|
+
after: {
|
|
209
|
+
summary: after && after.summary ? after.summary : null,
|
|
210
|
+
blocking: Array.isArray(after && after.blocking) ? after.blocking : [],
|
|
211
|
+
alerts: Array.isArray(after && after.alerts) ? after.alerts : []
|
|
212
|
+
},
|
|
213
|
+
summary: {
|
|
214
|
+
apply,
|
|
215
|
+
migrated_components: Number(migration && migration.summary && migration.summary.migrated_components) || 0,
|
|
216
|
+
migrated_records: Number(migration && migration.summary && migration.summary.migrated_records) || 0,
|
|
217
|
+
before_pending_components: beforePending,
|
|
218
|
+
after_pending_components: afterPending,
|
|
219
|
+
pending_components_reduced: pendingReduced,
|
|
220
|
+
before_blocking_count: beforeBlocking,
|
|
221
|
+
after_blocking_count: afterBlocking,
|
|
222
|
+
blocking_reduced: blockingReduced
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
printPayload(payload, options, 'State Reconcile');
|
|
227
|
+
return payload;
|
|
228
|
+
}
|
|
229
|
+
|
|
150
230
|
async function safeRun(handler, options = {}, dependencies = {}, title = 'state command') {
|
|
151
231
|
try {
|
|
152
232
|
await handler(options, dependencies);
|
|
@@ -199,6 +279,15 @@ function registerStateCommands(program) {
|
|
|
199
279
|
.option('--out <path>', 'Output file path', '.sce/reports/state-migration/state-export.latest.json')
|
|
200
280
|
.option('--json', 'Print machine-readable JSON output')
|
|
201
281
|
.action(async (options) => safeRun(runStateExportCommand, options, {}, 'state export'));
|
|
282
|
+
|
|
283
|
+
state
|
|
284
|
+
.command('reconcile')
|
|
285
|
+
.description('Run doctor + migrate + doctor in one flow (dry-run by default)')
|
|
286
|
+
.option('--component <id>', `Component id (repeatable): ${knownIds}`, collectOptionValue, [])
|
|
287
|
+
.option('--all', 'Reconcile all known components')
|
|
288
|
+
.option('--apply', 'Apply migration writes (default is dry-run)')
|
|
289
|
+
.option('--json', 'Print machine-readable JSON output')
|
|
290
|
+
.action(async (options) => safeRun(runStateReconcileCommand, options, {}, 'state reconcile'));
|
|
202
291
|
}
|
|
203
292
|
|
|
204
293
|
module.exports = {
|
|
@@ -206,5 +295,6 @@ module.exports = {
|
|
|
206
295
|
runStateDoctorCommand,
|
|
207
296
|
runStateMigrateCommand,
|
|
208
297
|
runStateExportCommand,
|
|
298
|
+
runStateReconcileCommand,
|
|
209
299
|
registerStateCommands
|
|
210
300
|
};
|
|
@@ -137,6 +137,8 @@ class SceStateStore {
|
|
|
137
137
|
errorbook_incident_index: {},
|
|
138
138
|
governance_spec_scene_override: {},
|
|
139
139
|
governance_scene_index: {},
|
|
140
|
+
release_evidence_run: {},
|
|
141
|
+
release_gate_history: {},
|
|
140
142
|
migration_records: {},
|
|
141
143
|
auth_leases: {},
|
|
142
144
|
auth_events: [],
|
|
@@ -407,6 +409,54 @@ class SceStateStore {
|
|
|
407
409
|
|
|
408
410
|
CREATE INDEX IF NOT EXISTS idx_governance_scene_index_registry_counts
|
|
409
411
|
ON governance_scene_index_registry(total_specs DESC, active_specs DESC);
|
|
412
|
+
|
|
413
|
+
CREATE TABLE IF NOT EXISTS release_evidence_run_registry (
|
|
414
|
+
session_id TEXT PRIMARY KEY,
|
|
415
|
+
merged_at TEXT,
|
|
416
|
+
status TEXT,
|
|
417
|
+
gate_passed INTEGER,
|
|
418
|
+
spec_success_rate_percent REAL,
|
|
419
|
+
risk_level TEXT,
|
|
420
|
+
ontology_quality_score REAL,
|
|
421
|
+
capability_coverage_percent REAL,
|
|
422
|
+
capability_coverage_passed INTEGER,
|
|
423
|
+
scene_package_batch_passed INTEGER,
|
|
424
|
+
scene_package_batch_failure_count INTEGER,
|
|
425
|
+
failed_goals INTEGER,
|
|
426
|
+
release_gate_preflight_available INTEGER,
|
|
427
|
+
release_gate_preflight_blocked INTEGER,
|
|
428
|
+
source_updated_at TEXT,
|
|
429
|
+
source TEXT,
|
|
430
|
+
indexed_at TEXT NOT NULL
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
CREATE INDEX IF NOT EXISTS idx_release_evidence_run_registry_status_merged
|
|
434
|
+
ON release_evidence_run_registry(status, merged_at DESC);
|
|
435
|
+
|
|
436
|
+
CREATE TABLE IF NOT EXISTS release_gate_history_registry (
|
|
437
|
+
tag TEXT PRIMARY KEY,
|
|
438
|
+
evaluated_at TEXT,
|
|
439
|
+
gate_passed INTEGER,
|
|
440
|
+
enforce INTEGER,
|
|
441
|
+
risk_level TEXT,
|
|
442
|
+
spec_success_rate_percent REAL,
|
|
443
|
+
scene_package_batch_passed INTEGER,
|
|
444
|
+
scene_package_batch_failure_count INTEGER,
|
|
445
|
+
capability_expected_unknown_count INTEGER,
|
|
446
|
+
capability_provided_unknown_count INTEGER,
|
|
447
|
+
release_gate_preflight_available INTEGER,
|
|
448
|
+
release_gate_preflight_blocked INTEGER,
|
|
449
|
+
require_release_gate_preflight INTEGER,
|
|
450
|
+
drift_alert_count INTEGER,
|
|
451
|
+
drift_blocked INTEGER,
|
|
452
|
+
weekly_ops_blocked INTEGER,
|
|
453
|
+
source_updated_at TEXT,
|
|
454
|
+
source TEXT,
|
|
455
|
+
indexed_at TEXT NOT NULL
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
CREATE INDEX IF NOT EXISTS idx_release_gate_history_registry_eval
|
|
459
|
+
ON release_gate_history_registry(evaluated_at DESC);
|
|
410
460
|
`);
|
|
411
461
|
}
|
|
412
462
|
|
|
@@ -678,6 +728,66 @@ class SceStateStore {
|
|
|
678
728
|
};
|
|
679
729
|
}
|
|
680
730
|
|
|
731
|
+
_mapReleaseEvidenceRunRow(row) {
|
|
732
|
+
if (!row) {
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
session_id: normalizeString(row.session_id),
|
|
737
|
+
merged_at: normalizeIsoTimestamp(row.merged_at, '') || null,
|
|
738
|
+
status: normalizeString(row.status) || null,
|
|
739
|
+
gate_passed: normalizeBooleanValue(row.gate_passed, false),
|
|
740
|
+
spec_success_rate_percent: Number.isFinite(Number(row.spec_success_rate_percent))
|
|
741
|
+
? Number(row.spec_success_rate_percent)
|
|
742
|
+
: null,
|
|
743
|
+
risk_level: normalizeString(row.risk_level) || null,
|
|
744
|
+
ontology_quality_score: Number.isFinite(Number(row.ontology_quality_score))
|
|
745
|
+
? Number(row.ontology_quality_score)
|
|
746
|
+
: null,
|
|
747
|
+
capability_coverage_percent: Number.isFinite(Number(row.capability_coverage_percent))
|
|
748
|
+
? Number(row.capability_coverage_percent)
|
|
749
|
+
: null,
|
|
750
|
+
capability_coverage_passed: normalizeBooleanValue(row.capability_coverage_passed, false),
|
|
751
|
+
scene_package_batch_passed: normalizeBooleanValue(row.scene_package_batch_passed, false),
|
|
752
|
+
scene_package_batch_failure_count: normalizeNonNegativeInteger(row.scene_package_batch_failure_count, 0),
|
|
753
|
+
failed_goals: normalizeNonNegativeInteger(row.failed_goals, 0),
|
|
754
|
+
release_gate_preflight_available: normalizeBooleanValue(row.release_gate_preflight_available, false),
|
|
755
|
+
release_gate_preflight_blocked: normalizeBooleanValue(row.release_gate_preflight_blocked, false),
|
|
756
|
+
source_updated_at: normalizeIsoTimestamp(row.source_updated_at, '') || null,
|
|
757
|
+
source: normalizeString(row.source) || null,
|
|
758
|
+
indexed_at: normalizeIsoTimestamp(row.indexed_at, '') || null
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
_mapReleaseGateHistoryRow(row) {
|
|
763
|
+
if (!row) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
return {
|
|
767
|
+
tag: normalizeString(row.tag),
|
|
768
|
+
evaluated_at: normalizeIsoTimestamp(row.evaluated_at, '') || null,
|
|
769
|
+
gate_passed: normalizeBooleanValue(row.gate_passed, false),
|
|
770
|
+
enforce: normalizeBooleanValue(row.enforce, false),
|
|
771
|
+
risk_level: normalizeString(row.risk_level) || null,
|
|
772
|
+
spec_success_rate_percent: Number.isFinite(Number(row.spec_success_rate_percent))
|
|
773
|
+
? Number(row.spec_success_rate_percent)
|
|
774
|
+
: null,
|
|
775
|
+
scene_package_batch_passed: normalizeBooleanValue(row.scene_package_batch_passed, false),
|
|
776
|
+
scene_package_batch_failure_count: normalizeNonNegativeInteger(row.scene_package_batch_failure_count, 0),
|
|
777
|
+
capability_expected_unknown_count: normalizeNonNegativeInteger(row.capability_expected_unknown_count, 0),
|
|
778
|
+
capability_provided_unknown_count: normalizeNonNegativeInteger(row.capability_provided_unknown_count, 0),
|
|
779
|
+
release_gate_preflight_available: normalizeBooleanValue(row.release_gate_preflight_available, false),
|
|
780
|
+
release_gate_preflight_blocked: normalizeBooleanValue(row.release_gate_preflight_blocked, false),
|
|
781
|
+
require_release_gate_preflight: normalizeBooleanValue(row.require_release_gate_preflight, false),
|
|
782
|
+
drift_alert_count: normalizeNonNegativeInteger(row.drift_alert_count, 0),
|
|
783
|
+
drift_blocked: normalizeBooleanValue(row.drift_blocked, false),
|
|
784
|
+
weekly_ops_blocked: normalizeBooleanValue(row.weekly_ops_blocked, false),
|
|
785
|
+
source_updated_at: normalizeIsoTimestamp(row.source_updated_at, '') || null,
|
|
786
|
+
source: normalizeString(row.source) || null,
|
|
787
|
+
indexed_at: normalizeIsoTimestamp(row.indexed_at, '') || null
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
681
791
|
async resolveOrCreateTaskRef(options = {}) {
|
|
682
792
|
const sceneId = normalizeString(options.sceneId);
|
|
683
793
|
const specId = normalizeString(options.specId);
|
|
@@ -2165,6 +2275,322 @@ class SceStateStore {
|
|
|
2165
2275
|
.filter(Boolean);
|
|
2166
2276
|
}
|
|
2167
2277
|
|
|
2278
|
+
async upsertReleaseEvidenceRunRecords(records = [], options = {}) {
|
|
2279
|
+
const source = normalizeString(options.source) || 'file.release-evidence.handoff-runs';
|
|
2280
|
+
const nowIso = this.now();
|
|
2281
|
+
const normalizedRecords = Array.isArray(records)
|
|
2282
|
+
? records.map((item) => ({
|
|
2283
|
+
session_id: normalizeString(item && item.session_id),
|
|
2284
|
+
merged_at: normalizeIsoTimestamp(item && item.merged_at, '') || null,
|
|
2285
|
+
status: normalizeString(item && item.status) || null,
|
|
2286
|
+
gate_passed: normalizeBooleanValue(item && item.gate_passed, false),
|
|
2287
|
+
spec_success_rate_percent: Number.isFinite(Number(item && item.spec_success_rate_percent))
|
|
2288
|
+
? Number(item.spec_success_rate_percent)
|
|
2289
|
+
: null,
|
|
2290
|
+
risk_level: normalizeString(item && item.risk_level) || null,
|
|
2291
|
+
ontology_quality_score: Number.isFinite(Number(item && item.ontology_quality_score))
|
|
2292
|
+
? Number(item.ontology_quality_score)
|
|
2293
|
+
: null,
|
|
2294
|
+
capability_coverage_percent: Number.isFinite(Number(item && item.capability_coverage_percent))
|
|
2295
|
+
? Number(item.capability_coverage_percent)
|
|
2296
|
+
: null,
|
|
2297
|
+
capability_coverage_passed: normalizeBooleanValue(item && item.capability_coverage_passed, false),
|
|
2298
|
+
scene_package_batch_passed: normalizeBooleanValue(item && item.scene_package_batch_passed, false),
|
|
2299
|
+
scene_package_batch_failure_count: normalizeNonNegativeInteger(item && item.scene_package_batch_failure_count, 0),
|
|
2300
|
+
failed_goals: normalizeNonNegativeInteger(item && item.failed_goals, 0),
|
|
2301
|
+
release_gate_preflight_available: normalizeBooleanValue(item && item.release_gate_preflight_available, false),
|
|
2302
|
+
release_gate_preflight_blocked: normalizeBooleanValue(item && item.release_gate_preflight_blocked, false),
|
|
2303
|
+
source_updated_at: normalizeIsoTimestamp(item && item.source_updated_at, '') || null,
|
|
2304
|
+
source: normalizeString(item && item.source) || source,
|
|
2305
|
+
indexed_at: nowIso
|
|
2306
|
+
}))
|
|
2307
|
+
.filter((item) => item.session_id)
|
|
2308
|
+
: [];
|
|
2309
|
+
|
|
2310
|
+
if (this._useMemoryBackend()) {
|
|
2311
|
+
for (const item of normalizedRecords) {
|
|
2312
|
+
this._memory.release_evidence_run[item.session_id] = { ...item };
|
|
2313
|
+
}
|
|
2314
|
+
return {
|
|
2315
|
+
success: true,
|
|
2316
|
+
written: normalizedRecords.length,
|
|
2317
|
+
total: Object.keys(this._memory.release_evidence_run || {}).length
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
if (!await this.ensureReady()) {
|
|
2322
|
+
return null;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
const statement = this._db.prepare(`
|
|
2326
|
+
INSERT OR REPLACE INTO release_evidence_run_registry(
|
|
2327
|
+
session_id, merged_at, status, gate_passed, spec_success_rate_percent, risk_level,
|
|
2328
|
+
ontology_quality_score, capability_coverage_percent, capability_coverage_passed,
|
|
2329
|
+
scene_package_batch_passed, scene_package_batch_failure_count, failed_goals,
|
|
2330
|
+
release_gate_preflight_available, release_gate_preflight_blocked,
|
|
2331
|
+
source_updated_at, source, indexed_at
|
|
2332
|
+
)
|
|
2333
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2334
|
+
`);
|
|
2335
|
+
|
|
2336
|
+
this._withTransaction(() => {
|
|
2337
|
+
for (const item of normalizedRecords) {
|
|
2338
|
+
statement.run(
|
|
2339
|
+
item.session_id,
|
|
2340
|
+
item.merged_at,
|
|
2341
|
+
item.status,
|
|
2342
|
+
item.gate_passed ? 1 : 0,
|
|
2343
|
+
item.spec_success_rate_percent,
|
|
2344
|
+
item.risk_level,
|
|
2345
|
+
item.ontology_quality_score,
|
|
2346
|
+
item.capability_coverage_percent,
|
|
2347
|
+
item.capability_coverage_passed ? 1 : 0,
|
|
2348
|
+
item.scene_package_batch_passed ? 1 : 0,
|
|
2349
|
+
item.scene_package_batch_failure_count,
|
|
2350
|
+
item.failed_goals,
|
|
2351
|
+
item.release_gate_preflight_available ? 1 : 0,
|
|
2352
|
+
item.release_gate_preflight_blocked ? 1 : 0,
|
|
2353
|
+
item.source_updated_at,
|
|
2354
|
+
item.source,
|
|
2355
|
+
item.indexed_at
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
});
|
|
2359
|
+
|
|
2360
|
+
const totalRow = this._db
|
|
2361
|
+
.prepare('SELECT COUNT(*) AS total FROM release_evidence_run_registry')
|
|
2362
|
+
.get();
|
|
2363
|
+
|
|
2364
|
+
return {
|
|
2365
|
+
success: true,
|
|
2366
|
+
written: normalizedRecords.length,
|
|
2367
|
+
total: normalizeNonNegativeInteger(totalRow && totalRow.total, 0)
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
async listReleaseEvidenceRunRecords(options = {}) {
|
|
2372
|
+
const limit = normalizeInteger(options.limit, 100);
|
|
2373
|
+
const status = normalizeString(options.status);
|
|
2374
|
+
const riskLevel = normalizeString(options.riskLevel || options.risk_level);
|
|
2375
|
+
|
|
2376
|
+
if (this._useMemoryBackend()) {
|
|
2377
|
+
let rows = Object.values(this._memory.release_evidence_run || {}).map((item) => ({ ...item }));
|
|
2378
|
+
if (status) {
|
|
2379
|
+
rows = rows.filter((item) => normalizeString(item.status) === status);
|
|
2380
|
+
}
|
|
2381
|
+
if (riskLevel) {
|
|
2382
|
+
rows = rows.filter((item) => normalizeString(item.risk_level) === riskLevel);
|
|
2383
|
+
}
|
|
2384
|
+
rows.sort((left, right) => {
|
|
2385
|
+
const leftTs = Date.parse(left.merged_at || '') || 0;
|
|
2386
|
+
const rightTs = Date.parse(right.merged_at || '') || 0;
|
|
2387
|
+
if (rightTs !== leftTs) {
|
|
2388
|
+
return rightTs - leftTs;
|
|
2389
|
+
}
|
|
2390
|
+
return `${right.session_id}`.localeCompare(`${left.session_id}`);
|
|
2391
|
+
});
|
|
2392
|
+
if (limit > 0) {
|
|
2393
|
+
rows = rows.slice(0, limit);
|
|
2394
|
+
}
|
|
2395
|
+
return rows.map((row) => this._mapReleaseEvidenceRunRow(row));
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
if (!await this.ensureReady()) {
|
|
2399
|
+
return null;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
let query = `
|
|
2403
|
+
SELECT session_id, merged_at, status, gate_passed, spec_success_rate_percent, risk_level,
|
|
2404
|
+
ontology_quality_score, capability_coverage_percent, capability_coverage_passed,
|
|
2405
|
+
scene_package_batch_passed, scene_package_batch_failure_count, failed_goals,
|
|
2406
|
+
release_gate_preflight_available, release_gate_preflight_blocked, source_updated_at,
|
|
2407
|
+
source, indexed_at
|
|
2408
|
+
FROM release_evidence_run_registry
|
|
2409
|
+
`;
|
|
2410
|
+
const clauses = [];
|
|
2411
|
+
const params = [];
|
|
2412
|
+
if (status) {
|
|
2413
|
+
clauses.push('status = ?');
|
|
2414
|
+
params.push(status);
|
|
2415
|
+
}
|
|
2416
|
+
if (riskLevel) {
|
|
2417
|
+
clauses.push('risk_level = ?');
|
|
2418
|
+
params.push(riskLevel);
|
|
2419
|
+
}
|
|
2420
|
+
if (clauses.length > 0) {
|
|
2421
|
+
query += ` WHERE ${clauses.join(' AND ')}`;
|
|
2422
|
+
}
|
|
2423
|
+
query += ' ORDER BY merged_at DESC, session_id DESC';
|
|
2424
|
+
if (limit > 0) {
|
|
2425
|
+
query += ' LIMIT ?';
|
|
2426
|
+
params.push(limit);
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
const rows = this._db.prepare(query).all(...params);
|
|
2430
|
+
return rows
|
|
2431
|
+
.map((row) => this._mapReleaseEvidenceRunRow(row))
|
|
2432
|
+
.filter(Boolean);
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
async upsertReleaseGateHistoryRecords(records = [], options = {}) {
|
|
2436
|
+
const source = normalizeString(options.source) || 'file.release-evidence.gate-history';
|
|
2437
|
+
const nowIso = this.now();
|
|
2438
|
+
const normalizedRecords = Array.isArray(records)
|
|
2439
|
+
? records.map((item) => ({
|
|
2440
|
+
tag: normalizeString(item && item.tag),
|
|
2441
|
+
evaluated_at: normalizeIsoTimestamp(item && item.evaluated_at, '') || null,
|
|
2442
|
+
gate_passed: normalizeBooleanValue(item && item.gate_passed, false),
|
|
2443
|
+
enforce: normalizeBooleanValue(item && item.enforce, false),
|
|
2444
|
+
risk_level: normalizeString(item && item.risk_level) || null,
|
|
2445
|
+
spec_success_rate_percent: Number.isFinite(Number(item && item.spec_success_rate_percent))
|
|
2446
|
+
? Number(item.spec_success_rate_percent)
|
|
2447
|
+
: null,
|
|
2448
|
+
scene_package_batch_passed: normalizeBooleanValue(item && item.scene_package_batch_passed, false),
|
|
2449
|
+
scene_package_batch_failure_count: normalizeNonNegativeInteger(item && item.scene_package_batch_failure_count, 0),
|
|
2450
|
+
capability_expected_unknown_count: normalizeNonNegativeInteger(item && item.capability_expected_unknown_count, 0),
|
|
2451
|
+
capability_provided_unknown_count: normalizeNonNegativeInteger(item && item.capability_provided_unknown_count, 0),
|
|
2452
|
+
release_gate_preflight_available: normalizeBooleanValue(item && item.release_gate_preflight_available, false),
|
|
2453
|
+
release_gate_preflight_blocked: normalizeBooleanValue(item && item.release_gate_preflight_blocked, false),
|
|
2454
|
+
require_release_gate_preflight: normalizeBooleanValue(item && item.require_release_gate_preflight, false),
|
|
2455
|
+
drift_alert_count: normalizeNonNegativeInteger(item && item.drift_alert_count, 0),
|
|
2456
|
+
drift_blocked: normalizeBooleanValue(item && item.drift_blocked, false),
|
|
2457
|
+
weekly_ops_blocked: normalizeBooleanValue(item && item.weekly_ops_blocked, false),
|
|
2458
|
+
source_updated_at: normalizeIsoTimestamp(item && item.source_updated_at, '') || null,
|
|
2459
|
+
source: normalizeString(item && item.source) || source,
|
|
2460
|
+
indexed_at: nowIso
|
|
2461
|
+
}))
|
|
2462
|
+
.filter((item) => item.tag)
|
|
2463
|
+
: [];
|
|
2464
|
+
|
|
2465
|
+
if (this._useMemoryBackend()) {
|
|
2466
|
+
for (const item of normalizedRecords) {
|
|
2467
|
+
this._memory.release_gate_history[item.tag] = { ...item };
|
|
2468
|
+
}
|
|
2469
|
+
return {
|
|
2470
|
+
success: true,
|
|
2471
|
+
written: normalizedRecords.length,
|
|
2472
|
+
total: Object.keys(this._memory.release_gate_history || {}).length
|
|
2473
|
+
};
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
if (!await this.ensureReady()) {
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
const statement = this._db.prepare(`
|
|
2481
|
+
INSERT OR REPLACE INTO release_gate_history_registry(
|
|
2482
|
+
tag, evaluated_at, gate_passed, enforce, risk_level, spec_success_rate_percent,
|
|
2483
|
+
scene_package_batch_passed, scene_package_batch_failure_count,
|
|
2484
|
+
capability_expected_unknown_count, capability_provided_unknown_count,
|
|
2485
|
+
release_gate_preflight_available, release_gate_preflight_blocked,
|
|
2486
|
+
require_release_gate_preflight, drift_alert_count, drift_blocked,
|
|
2487
|
+
weekly_ops_blocked, source_updated_at, source, indexed_at
|
|
2488
|
+
)
|
|
2489
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2490
|
+
`);
|
|
2491
|
+
|
|
2492
|
+
this._withTransaction(() => {
|
|
2493
|
+
for (const item of normalizedRecords) {
|
|
2494
|
+
statement.run(
|
|
2495
|
+
item.tag,
|
|
2496
|
+
item.evaluated_at,
|
|
2497
|
+
item.gate_passed ? 1 : 0,
|
|
2498
|
+
item.enforce ? 1 : 0,
|
|
2499
|
+
item.risk_level,
|
|
2500
|
+
item.spec_success_rate_percent,
|
|
2501
|
+
item.scene_package_batch_passed ? 1 : 0,
|
|
2502
|
+
item.scene_package_batch_failure_count,
|
|
2503
|
+
item.capability_expected_unknown_count,
|
|
2504
|
+
item.capability_provided_unknown_count,
|
|
2505
|
+
item.release_gate_preflight_available ? 1 : 0,
|
|
2506
|
+
item.release_gate_preflight_blocked ? 1 : 0,
|
|
2507
|
+
item.require_release_gate_preflight ? 1 : 0,
|
|
2508
|
+
item.drift_alert_count,
|
|
2509
|
+
item.drift_blocked ? 1 : 0,
|
|
2510
|
+
item.weekly_ops_blocked ? 1 : 0,
|
|
2511
|
+
item.source_updated_at,
|
|
2512
|
+
item.source,
|
|
2513
|
+
item.indexed_at
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
2517
|
+
|
|
2518
|
+
const totalRow = this._db
|
|
2519
|
+
.prepare('SELECT COUNT(*) AS total FROM release_gate_history_registry')
|
|
2520
|
+
.get();
|
|
2521
|
+
|
|
2522
|
+
return {
|
|
2523
|
+
success: true,
|
|
2524
|
+
written: normalizedRecords.length,
|
|
2525
|
+
total: normalizeNonNegativeInteger(totalRow && totalRow.total, 0)
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
|
|
2529
|
+
async listReleaseGateHistoryRecords(options = {}) {
|
|
2530
|
+
const limit = normalizeInteger(options.limit, 100);
|
|
2531
|
+
const tag = normalizeString(options.tag);
|
|
2532
|
+
const riskLevel = normalizeString(options.riskLevel || options.risk_level);
|
|
2533
|
+
|
|
2534
|
+
if (this._useMemoryBackend()) {
|
|
2535
|
+
let rows = Object.values(this._memory.release_gate_history || {}).map((item) => ({ ...item }));
|
|
2536
|
+
if (tag) {
|
|
2537
|
+
rows = rows.filter((item) => normalizeString(item.tag) === tag);
|
|
2538
|
+
}
|
|
2539
|
+
if (riskLevel) {
|
|
2540
|
+
rows = rows.filter((item) => normalizeString(item.risk_level) === riskLevel);
|
|
2541
|
+
}
|
|
2542
|
+
rows.sort((left, right) => {
|
|
2543
|
+
const leftTs = Date.parse(left.evaluated_at || '') || 0;
|
|
2544
|
+
const rightTs = Date.parse(right.evaluated_at || '') || 0;
|
|
2545
|
+
if (rightTs !== leftTs) {
|
|
2546
|
+
return rightTs - leftTs;
|
|
2547
|
+
}
|
|
2548
|
+
return `${right.tag}`.localeCompare(`${left.tag}`);
|
|
2549
|
+
});
|
|
2550
|
+
if (limit > 0) {
|
|
2551
|
+
rows = rows.slice(0, limit);
|
|
2552
|
+
}
|
|
2553
|
+
return rows.map((row) => this._mapReleaseGateHistoryRow(row));
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
if (!await this.ensureReady()) {
|
|
2557
|
+
return null;
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
let query = `
|
|
2561
|
+
SELECT tag, evaluated_at, gate_passed, enforce, risk_level, spec_success_rate_percent,
|
|
2562
|
+
scene_package_batch_passed, scene_package_batch_failure_count,
|
|
2563
|
+
capability_expected_unknown_count, capability_provided_unknown_count,
|
|
2564
|
+
release_gate_preflight_available, release_gate_preflight_blocked,
|
|
2565
|
+
require_release_gate_preflight, drift_alert_count, drift_blocked,
|
|
2566
|
+
weekly_ops_blocked, source_updated_at, source, indexed_at
|
|
2567
|
+
FROM release_gate_history_registry
|
|
2568
|
+
`;
|
|
2569
|
+
const clauses = [];
|
|
2570
|
+
const params = [];
|
|
2571
|
+
if (tag) {
|
|
2572
|
+
clauses.push('tag = ?');
|
|
2573
|
+
params.push(tag);
|
|
2574
|
+
}
|
|
2575
|
+
if (riskLevel) {
|
|
2576
|
+
clauses.push('risk_level = ?');
|
|
2577
|
+
params.push(riskLevel);
|
|
2578
|
+
}
|
|
2579
|
+
if (clauses.length > 0) {
|
|
2580
|
+
query += ` WHERE ${clauses.join(' AND ')}`;
|
|
2581
|
+
}
|
|
2582
|
+
query += ' ORDER BY evaluated_at DESC, tag DESC';
|
|
2583
|
+
if (limit > 0) {
|
|
2584
|
+
query += ' LIMIT ?';
|
|
2585
|
+
params.push(limit);
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
const rows = this._db.prepare(query).all(...params);
|
|
2589
|
+
return rows
|
|
2590
|
+
.map((row) => this._mapReleaseGateHistoryRow(row))
|
|
2591
|
+
.filter(Boolean);
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2168
2594
|
_resolveOrCreateTaskRefInMemory(options = {}) {
|
|
2169
2595
|
const sceneId = normalizeString(options.sceneId);
|
|
2170
2596
|
const specId = normalizeString(options.specId);
|
|
@@ -11,6 +11,8 @@ const COMPONENT_ERRORBOOK_ENTRY_INDEX = 'errorbook.entry-index';
|
|
|
11
11
|
const COMPONENT_ERRORBOOK_INCIDENT_INDEX = 'errorbook.incident-index';
|
|
12
12
|
const COMPONENT_SPEC_SCENE_OVERRIDES = 'governance.spec-scene-overrides';
|
|
13
13
|
const COMPONENT_SPEC_SCENE_INDEX = 'governance.scene-index';
|
|
14
|
+
const COMPONENT_RELEASE_EVIDENCE_RUNS = 'release.evidence-runs-index';
|
|
15
|
+
const COMPONENT_RELEASE_GATE_HISTORY = 'release.gate-history-index';
|
|
14
16
|
const DEFAULT_STATE_EXPORT_PATH = '.sce/reports/state-migration/state-export.latest.json';
|
|
15
17
|
|
|
16
18
|
const COMPONENT_DEFINITIONS = Object.freeze([
|
|
@@ -41,6 +43,14 @@ const COMPONENT_DEFINITIONS = Object.freeze([
|
|
|
41
43
|
{
|
|
42
44
|
id: COMPONENT_SPEC_SCENE_INDEX,
|
|
43
45
|
source_path: '.sce/spec-governance/scene-index.json'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: COMPONENT_RELEASE_EVIDENCE_RUNS,
|
|
49
|
+
source_path: '.sce/reports/release-evidence/handoff-runs.json'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: COMPONENT_RELEASE_GATE_HISTORY,
|
|
53
|
+
source_path: '.sce/reports/release-evidence/release-gate-history.json'
|
|
44
54
|
}
|
|
45
55
|
]);
|
|
46
56
|
|
|
@@ -280,6 +290,106 @@ function mapSpecSceneIndexPayload(payload = {}) {
|
|
|
280
290
|
.filter((item) => item.scene_id);
|
|
281
291
|
}
|
|
282
292
|
|
|
293
|
+
function mapReleaseEvidenceRunsPayload(payload = {}) {
|
|
294
|
+
if (!payload || typeof payload !== 'object' || !Array.isArray(payload.sessions)) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
const sourceUpdatedAt = normalizeString(payload.updated_at) || normalizeString(payload.generated_at);
|
|
298
|
+
return payload.sessions
|
|
299
|
+
.map((session) => {
|
|
300
|
+
const gate = session && session.gate && typeof session.gate === 'object' ? session.gate : {};
|
|
301
|
+
const gateActual = gate && gate.actual && typeof gate.actual === 'object' ? gate.actual : {};
|
|
302
|
+
const coverage = session && session.capability_coverage && typeof session.capability_coverage === 'object'
|
|
303
|
+
? session.capability_coverage
|
|
304
|
+
: {};
|
|
305
|
+
const coverageSummary = coverage && coverage.summary && typeof coverage.summary === 'object'
|
|
306
|
+
? coverage.summary
|
|
307
|
+
: {};
|
|
308
|
+
const scenePackageBatch = session && session.scene_package_batch && typeof session.scene_package_batch === 'object'
|
|
309
|
+
? session.scene_package_batch
|
|
310
|
+
: {};
|
|
311
|
+
const scenePackageBatchSummary = scenePackageBatch && scenePackageBatch.summary && typeof scenePackageBatch.summary === 'object'
|
|
312
|
+
? scenePackageBatch.summary
|
|
313
|
+
: {};
|
|
314
|
+
const batchSummary = session && session.batch_summary && typeof session.batch_summary === 'object'
|
|
315
|
+
? session.batch_summary
|
|
316
|
+
: {};
|
|
317
|
+
const preflight = session && session.release_gate_preflight && typeof session.release_gate_preflight === 'object'
|
|
318
|
+
? session.release_gate_preflight
|
|
319
|
+
: {};
|
|
320
|
+
return {
|
|
321
|
+
session_id: normalizeString(session && session.session_id),
|
|
322
|
+
merged_at: normalizeString(session && session.merged_at) || normalizeString(session && session.generated_at),
|
|
323
|
+
status: normalizeString(session && session.status),
|
|
324
|
+
gate_passed: gate && gate.passed === true,
|
|
325
|
+
spec_success_rate_percent: Number.isFinite(Number(gateActual.spec_success_rate_percent))
|
|
326
|
+
? Number(gateActual.spec_success_rate_percent)
|
|
327
|
+
: null,
|
|
328
|
+
risk_level: normalizeString(gateActual.risk_level),
|
|
329
|
+
ontology_quality_score: Number.isFinite(Number(gateActual.ontology_quality_score))
|
|
330
|
+
? Number(gateActual.ontology_quality_score)
|
|
331
|
+
: null,
|
|
332
|
+
capability_coverage_percent: Number.isFinite(Number(coverageSummary.coverage_percent))
|
|
333
|
+
? Number(coverageSummary.coverage_percent)
|
|
334
|
+
: null,
|
|
335
|
+
capability_coverage_passed: coverageSummary.passed === true,
|
|
336
|
+
scene_package_batch_passed: scenePackageBatchSummary.batch_gate_passed === true,
|
|
337
|
+
scene_package_batch_failure_count: Number.isFinite(Number(scenePackageBatchSummary.batch_gate_failure_count))
|
|
338
|
+
? Number.parseInt(`${scenePackageBatchSummary.batch_gate_failure_count}`, 10)
|
|
339
|
+
: (
|
|
340
|
+
Number.isFinite(Number(scenePackageBatchSummary.failed))
|
|
341
|
+
? Number.parseInt(`${scenePackageBatchSummary.failed}`, 10)
|
|
342
|
+
: 0
|
|
343
|
+
),
|
|
344
|
+
failed_goals: Number.isFinite(Number(batchSummary.failed_goals))
|
|
345
|
+
? Number.parseInt(`${batchSummary.failed_goals}`, 10)
|
|
346
|
+
: 0,
|
|
347
|
+
release_gate_preflight_available: preflight && preflight.available === true,
|
|
348
|
+
release_gate_preflight_blocked: preflight && preflight.blocked === true,
|
|
349
|
+
source_updated_at: sourceUpdatedAt
|
|
350
|
+
};
|
|
351
|
+
})
|
|
352
|
+
.filter((item) => item.session_id);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function mapReleaseGateHistoryPayload(payload = {}) {
|
|
356
|
+
if (!payload || typeof payload !== 'object' || !Array.isArray(payload.entries)) {
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
const sourceUpdatedAt = normalizeString(payload.updated_at) || normalizeString(payload.generated_at);
|
|
360
|
+
return payload.entries
|
|
361
|
+
.map((entry) => ({
|
|
362
|
+
tag: normalizeString(entry && entry.tag),
|
|
363
|
+
evaluated_at: normalizeString(entry && entry.evaluated_at),
|
|
364
|
+
gate_passed: entry && entry.gate_passed === true,
|
|
365
|
+
enforce: entry && entry.enforce === true,
|
|
366
|
+
risk_level: normalizeString(entry && entry.risk_level),
|
|
367
|
+
spec_success_rate_percent: Number.isFinite(Number(entry && entry.spec_success_rate_percent))
|
|
368
|
+
? Number(entry.spec_success_rate_percent)
|
|
369
|
+
: null,
|
|
370
|
+
scene_package_batch_passed: entry && entry.scene_package_batch_passed === true,
|
|
371
|
+
scene_package_batch_failure_count: Number.isFinite(Number(entry && entry.scene_package_batch_failure_count))
|
|
372
|
+
? Number.parseInt(`${entry.scene_package_batch_failure_count}`, 10)
|
|
373
|
+
: 0,
|
|
374
|
+
capability_expected_unknown_count: Number.isFinite(Number(entry && entry.capability_expected_unknown_count))
|
|
375
|
+
? Number.parseInt(`${entry.capability_expected_unknown_count}`, 10)
|
|
376
|
+
: 0,
|
|
377
|
+
capability_provided_unknown_count: Number.isFinite(Number(entry && entry.capability_provided_unknown_count))
|
|
378
|
+
? Number.parseInt(`${entry.capability_provided_unknown_count}`, 10)
|
|
379
|
+
: 0,
|
|
380
|
+
release_gate_preflight_available: entry && entry.release_gate_preflight_available === true,
|
|
381
|
+
release_gate_preflight_blocked: entry && entry.release_gate_preflight_blocked === true,
|
|
382
|
+
require_release_gate_preflight: entry && entry.require_release_gate_preflight === true,
|
|
383
|
+
drift_alert_count: Number.isFinite(Number(entry && entry.drift_alert_count))
|
|
384
|
+
? Number.parseInt(`${entry.drift_alert_count}`, 10)
|
|
385
|
+
: 0,
|
|
386
|
+
drift_blocked: entry && entry.drift_blocked === true,
|
|
387
|
+
weekly_ops_blocked: entry && entry.weekly_ops_blocked === true,
|
|
388
|
+
source_updated_at: sourceUpdatedAt
|
|
389
|
+
}))
|
|
390
|
+
.filter((item) => item.tag);
|
|
391
|
+
}
|
|
392
|
+
|
|
283
393
|
async function readJsonSource(absolutePath, fileSystem = fs) {
|
|
284
394
|
if (!await fileSystem.pathExists(absolutePath)) {
|
|
285
395
|
return {
|
|
@@ -325,6 +435,10 @@ async function readComponentSnapshot(component = {}, projectPath = process.cwd()
|
|
|
325
435
|
records = mapSpecSceneOverridesPayload(source.payload);
|
|
326
436
|
} else if (component.id === COMPONENT_SPEC_SCENE_INDEX) {
|
|
327
437
|
records = mapSpecSceneIndexPayload(source.payload);
|
|
438
|
+
} else if (component.id === COMPONENT_RELEASE_EVIDENCE_RUNS) {
|
|
439
|
+
records = mapReleaseEvidenceRunsPayload(source.payload);
|
|
440
|
+
} else if (component.id === COMPONENT_RELEASE_GATE_HISTORY) {
|
|
441
|
+
records = mapReleaseGateHistoryPayload(source.payload);
|
|
328
442
|
}
|
|
329
443
|
}
|
|
330
444
|
|
|
@@ -434,6 +548,16 @@ async function writeComponentToStateStore(componentSnapshot = {}, stateStore, co
|
|
|
434
548
|
source: componentId
|
|
435
549
|
});
|
|
436
550
|
}
|
|
551
|
+
if (componentId === COMPONENT_RELEASE_EVIDENCE_RUNS) {
|
|
552
|
+
return stateStore.upsertReleaseEvidenceRunRecords(componentSnapshot.records, {
|
|
553
|
+
source: componentId
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
if (componentId === COMPONENT_RELEASE_GATE_HISTORY) {
|
|
557
|
+
return stateStore.upsertReleaseGateHistoryRecords(componentSnapshot.records, {
|
|
558
|
+
source: componentId
|
|
559
|
+
});
|
|
560
|
+
}
|
|
437
561
|
return null;
|
|
438
562
|
}
|
|
439
563
|
|
|
@@ -561,6 +685,8 @@ async function runStateDoctor(options = {}, dependencies = {}) {
|
|
|
561
685
|
errorbookIncidentRows,
|
|
562
686
|
governanceOverrideRows,
|
|
563
687
|
governanceSceneRows,
|
|
688
|
+
releaseEvidenceRows,
|
|
689
|
+
releaseGateHistoryRows,
|
|
564
690
|
migrations
|
|
565
691
|
] = await Promise.all([
|
|
566
692
|
stateStore.listAgentRuntimeRecords({ limit: 0 }),
|
|
@@ -570,6 +696,8 @@ async function runStateDoctor(options = {}, dependencies = {}) {
|
|
|
570
696
|
stateStore.listErrorbookIncidentIndexRecords({ limit: 0 }),
|
|
571
697
|
stateStore.listGovernanceSpecSceneOverrideRecords({ limit: 0 }),
|
|
572
698
|
stateStore.listGovernanceSceneIndexRecords({ limit: 0 }),
|
|
699
|
+
stateStore.listReleaseEvidenceRunRecords({ limit: 0 }),
|
|
700
|
+
stateStore.listReleaseGateHistoryRecords({ limit: 0 }),
|
|
573
701
|
stateStore.listStateMigrations({ limit: 20 })
|
|
574
702
|
]);
|
|
575
703
|
|
|
@@ -580,7 +708,9 @@ async function runStateDoctor(options = {}, dependencies = {}) {
|
|
|
580
708
|
[COMPONENT_ERRORBOOK_ENTRY_INDEX, Array.isArray(errorbookEntryRows) ? errorbookEntryRows.length : 0],
|
|
581
709
|
[COMPONENT_ERRORBOOK_INCIDENT_INDEX, Array.isArray(errorbookIncidentRows) ? errorbookIncidentRows.length : 0],
|
|
582
710
|
[COMPONENT_SPEC_SCENE_OVERRIDES, Array.isArray(governanceOverrideRows) ? governanceOverrideRows.length : 0],
|
|
583
|
-
[COMPONENT_SPEC_SCENE_INDEX, Array.isArray(governanceSceneRows) ? governanceSceneRows.length : 0]
|
|
711
|
+
[COMPONENT_SPEC_SCENE_INDEX, Array.isArray(governanceSceneRows) ? governanceSceneRows.length : 0],
|
|
712
|
+
[COMPONENT_RELEASE_EVIDENCE_RUNS, Array.isArray(releaseEvidenceRows) ? releaseEvidenceRows.length : 0],
|
|
713
|
+
[COMPONENT_RELEASE_GATE_HISTORY, Array.isArray(releaseGateHistoryRows) ? releaseGateHistoryRows.length : 0]
|
|
584
714
|
]);
|
|
585
715
|
|
|
586
716
|
const checks = plan.components.map((component) => {
|
|
@@ -788,6 +918,8 @@ async function runStateExport(options = {}, dependencies = {}) {
|
|
|
788
918
|
errorbookIncidentRows,
|
|
789
919
|
governanceOverrideRows,
|
|
790
920
|
governanceSceneRows,
|
|
921
|
+
releaseEvidenceRows,
|
|
922
|
+
releaseGateHistoryRows,
|
|
791
923
|
migrations
|
|
792
924
|
] = await Promise.all([
|
|
793
925
|
stateStore.listAgentRuntimeRecords({ limit: 0 }),
|
|
@@ -797,6 +929,8 @@ async function runStateExport(options = {}, dependencies = {}) {
|
|
|
797
929
|
stateStore.listErrorbookIncidentIndexRecords({ limit: 0 }),
|
|
798
930
|
stateStore.listGovernanceSpecSceneOverrideRecords({ limit: 0 }),
|
|
799
931
|
stateStore.listGovernanceSceneIndexRecords({ limit: 0 }),
|
|
932
|
+
stateStore.listReleaseEvidenceRunRecords({ limit: 0 }),
|
|
933
|
+
stateStore.listReleaseGateHistoryRecords({ limit: 0 }),
|
|
800
934
|
stateStore.listStateMigrations({ limit: 0 })
|
|
801
935
|
]);
|
|
802
936
|
|
|
@@ -813,6 +947,8 @@ async function runStateExport(options = {}, dependencies = {}) {
|
|
|
813
947
|
errorbook_incident_index_registry: Array.isArray(errorbookIncidentRows) ? errorbookIncidentRows : [],
|
|
814
948
|
governance_spec_scene_override_registry: Array.isArray(governanceOverrideRows) ? governanceOverrideRows : [],
|
|
815
949
|
governance_scene_index_registry: Array.isArray(governanceSceneRows) ? governanceSceneRows : [],
|
|
950
|
+
release_evidence_run_registry: Array.isArray(releaseEvidenceRows) ? releaseEvidenceRows : [],
|
|
951
|
+
release_gate_history_registry: Array.isArray(releaseGateHistoryRows) ? releaseGateHistoryRows : [],
|
|
816
952
|
state_migration_registry: Array.isArray(migrations) ? migrations : []
|
|
817
953
|
},
|
|
818
954
|
summary: {
|
|
@@ -823,6 +959,8 @@ async function runStateExport(options = {}, dependencies = {}) {
|
|
|
823
959
|
errorbook_incident_index_registry: Array.isArray(errorbookIncidentRows) ? errorbookIncidentRows.length : 0,
|
|
824
960
|
governance_spec_scene_override_registry: Array.isArray(governanceOverrideRows) ? governanceOverrideRows.length : 0,
|
|
825
961
|
governance_scene_index_registry: Array.isArray(governanceSceneRows) ? governanceSceneRows.length : 0,
|
|
962
|
+
release_evidence_run_registry: Array.isArray(releaseEvidenceRows) ? releaseEvidenceRows.length : 0,
|
|
963
|
+
release_gate_history_registry: Array.isArray(releaseGateHistoryRows) ? releaseGateHistoryRows.length : 0,
|
|
826
964
|
state_migration_registry: Array.isArray(migrations) ? migrations.length : 0
|
|
827
965
|
},
|
|
828
966
|
out_file: path.relative(projectPath, outPath).replace(/\\/g, '/')
|
|
@@ -841,6 +979,8 @@ module.exports = {
|
|
|
841
979
|
COMPONENT_ERRORBOOK_INCIDENT_INDEX,
|
|
842
980
|
COMPONENT_SPEC_SCENE_OVERRIDES,
|
|
843
981
|
COMPONENT_SPEC_SCENE_INDEX,
|
|
982
|
+
COMPONENT_RELEASE_EVIDENCE_RUNS,
|
|
983
|
+
COMPONENT_RELEASE_GATE_HISTORY,
|
|
844
984
|
COMPONENT_DEFINITIONS,
|
|
845
985
|
DEFAULT_STATE_EXPORT_PATH,
|
|
846
986
|
buildStateMigrationPlan,
|
package/package.json
CHANGED