scene-capability-engine 3.6.7 → 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 CHANGED
@@ -7,6 +7,19 @@ 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
+
10
23
  ## [3.6.7] - 2026-03-05
11
24
 
12
25
  ### Fixed
package/README.md CHANGED
@@ -149,8 +149,10 @@ 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
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`)
package/README.zh.md CHANGED
@@ -149,8 +149,10 @@ 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
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`)
@@ -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
 
@@ -756,6 +759,7 @@ SQLite index tables introduced for gradual migration:
756
759
  Runtime read preference:
757
760
  - timeline/session runtime views now prefer SQLite indexes when indexed rows exist.
758
761
  - file artifacts remain source-of-truth for content payload and recovery operations.
762
+ - release workflow runs `state reconcile --all --apply` before reconciliation gate.
759
763
  - `sce state doctor --json` now includes:
760
764
  - `summary` aggregate (`pending_components`, `total_record_drift`, `blocking_count`, `alert_count`)
761
765
  - runtime read diagnostics (`runtime.timeline`, `runtime.scene_session`) with read-source/read-preference and consistency status
@@ -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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.7",
3
+ "version": "3.6.8",
4
4
  "description": "SCE (Scene Capability Engine) - A CLI tool and npm package for spec-driven development with AI coding assistants.",
5
5
  "main": "index.js",
6
6
  "bin": {