scene-capability-engine 3.5.0 → 3.5.1

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,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.5.1] - 2026-03-03
11
+
12
+ ### Added
13
+ - Studio historical scene backfill command:
14
+ - `sce studio backfill-spec-scenes`
15
+ - supports active-only/default batch backfill and writes `.sce/spec-governance/spec-scene-overrides.json`
16
+ - optionally refreshes governance artifacts after apply
17
+
18
+ ### Changed
19
+ - Studio intake/governance policy is now stricter by default:
20
+ - `allow_manual_spec_override=false` blocks `--manual-spec` bypass unless explicitly enabled
21
+ - `governance.require_auto_on_plan=true` blocks `--no-spec-governance` bypass unless explicitly enabled
22
+ - Spec governance and related-spec lookup now read scene override mappings from `.sce/spec-governance/spec-scene-overrides.json`.
23
+ - Takeover/adoption baseline now provisions the stricter intake policy and backfill defaults.
24
+
10
25
  ## [3.5.0] - 2026-03-02
11
26
 
12
27
  ### Added
package/README.md CHANGED
@@ -108,6 +108,8 @@ Hard rule defaults:
108
108
  - After two failed rounds on the same problem fingerprint, debug evidence is required in subsequent attempts.
109
109
  - `studio verify/release` run `problem-closure-gate` by default when a spec is bound.
110
110
  - `studio plan` auto-runs goal intake (`bind existing spec` or `create spec`) and writes scene portfolio governance snapshots by default.
111
+ - `studio plan --manual-spec` and `--no-spec-governance` are blocked by default policy; use policy overrides only when absolutely necessary.
112
+ - Historical specs can be scene-governed incrementally via `sce studio backfill-spec-scenes --apply` (writes `.sce/spec-governance/spec-scene-overrides.json`).
111
113
 
112
114
  ---
113
115
 
@@ -130,6 +132,7 @@ SCE is tool-agnostic and works with Codex, Claude Code, Cursor, Windsurf, VS Cod
130
132
 
131
133
  ## Important Version Changes
132
134
 
135
+ - `3.5.1`: Enforced stricter Studio intake defaults (`--manual-spec` and `--no-spec-governance` blocked unless policy override), added historical spec scene backfill command (`sce studio backfill-spec-scenes`) and persisted override mapping (`.sce/spec-governance/spec-scene-overrides.json`) for portfolio/related-spec alignment.
133
136
  - `3.5.0`: Added Studio automatic goal intake + scene spec portfolio governance (`sce studio intake`, `sce studio portfolio`), including default intake policy baseline and governance artifacts for bounded scene spec growth.
134
137
  - `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.
135
138
  - `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.
@@ -177,5 +180,5 @@ MIT. See [LICENSE](LICENSE).
177
180
 
178
181
  ---
179
182
 
180
- **Version**: 3.5.0
181
- **Last Updated**: 2026-03-02
183
+ **Version**: 3.5.1
184
+ **Last Updated**: 2026-03-03
package/README.zh.md CHANGED
@@ -108,6 +108,8 @@ SCE 默认按“问题域闭环”推进诊断与修复:
108
108
  - 同一问题指纹失败两轮后,后续尝试必须补充 debug 证据。
109
109
  - 当 spec 绑定时,`studio verify/release` 默认执行 `problem-closure-gate`。
110
110
  - `studio plan` 默认执行目标 intake(自动绑定已有 spec 或新建 spec),并自动写入 scene 维度的 spec 治理快照。
111
+ - 默认策略会阻断 `studio plan --manual-spec` 与 `--no-spec-governance`(仅在确有必要且策略显式放开时可绕过)。
112
+ - 历史 spec 可通过 `sce studio backfill-spec-scenes --apply` 分批回填到 scene 治理映射(写入 `.sce/spec-governance/spec-scene-overrides.json`)。
111
113
 
112
114
  ---
113
115
 
@@ -130,6 +132,7 @@ SCE 对工具无锁定,可接入 Codex、Claude Code、Cursor、Windsurf、VS
130
132
 
131
133
  ## 重要版本变更
132
134
 
135
+ - `3.5.1`:默认强化 Studio intake 治理(`--manual-spec`、`--no-spec-governance` 在未显式放开策略时会被阻断),新增历史 spec 场景回填命令 `sce studio backfill-spec-scenes`,并写入 `.sce/spec-governance/spec-scene-overrides.json` 以统一 portfolio 与 related-spec 的场景映射。
133
136
  - `3.5.0`:新增 Studio 目标自动 intake + 场景 spec 组合治理(`sce studio intake`、`sce studio portfolio`),并默认启用 intake 策略基线与治理快照产物,控制场景内 spec 无序增长。
134
137
  - `3.4.6`:新增默认 `problem-closure-gate` + `problem-contract` 基线,并强化问题评估强制维度(`problem_contract`/`ontology_alignment`/`convergence`),提升 verify/release 收敛控制。
135
138
  - `3.4.5`:`git-managed-gate` 在默认 CI 放宽模式下(`CI/GITHUB_ACTIONS` 且非 strict)对工作区变更改为告警,不再误阻断发布。
@@ -177,5 +180,5 @@ MIT,见 [LICENSE](LICENSE)。
177
180
 
178
181
  ---
179
182
 
180
- **版本**:3.5.0
181
- **最后更新**:2026-03-02
183
+ **版本**:3.5.1
184
+ **最后更新**:2026-03-03
@@ -523,8 +523,6 @@ Curated quality policy (`宁缺毋滥,优胜略汰`) defaults:
523
523
  sce studio plan --scene scene.customer-order-inventory --from-chat session-20260226 --goal "customer+order+inventory demo" --json
524
524
  # Recommended: bind spec explicitly so Studio can ingest problem-domain-chain deterministically
525
525
  sce studio plan --scene scene.customer-order-inventory --spec 01-00-customer-order-inventory --from-chat session-20260226 --goal "customer+order+inventory demo" --json
526
- # Disable auto intake for emergency/manual mode
527
- sce studio plan --scene scene.customer-order-inventory --from-chat session-20260226 --goal "customer+order+inventory demo" --manual-spec --json
528
526
 
529
527
  # Analyze intake decision only (no write by default)
530
528
  sce studio intake --scene scene.customer-order-inventory --from-chat session-20260226 --goal "optimize checkout retry flow" --json
@@ -560,6 +558,11 @@ sce studio rollback --job <job-id> --reason "manual-check-failed" --json
560
558
  sce studio portfolio --json
561
559
  sce studio portfolio --scene scene.customer-order-inventory --strict --json
562
560
 
561
+ # Backfill historical spec scene bindings into override map (active-only by default)
562
+ sce studio backfill-spec-scenes --apply --json
563
+ # Backfill a bounded batch from unassigned scene and refresh governance snapshot
564
+ sce studio backfill-spec-scenes --scene scene.unassigned --limit 20 --apply --json
565
+
563
566
  # Enforce authorization for a protected action
564
567
  SCE_STUDIO_REQUIRE_AUTH=1 SCE_STUDIO_AUTH_PASSWORD=top-secret sce studio apply --job <job-id> --auth-password top-secret --json
565
568
  ```
@@ -570,12 +573,17 @@ Stage guardrails are enforced by default:
570
573
  - detect goal intent (`change_request` vs `analysis_only`)
571
574
  - resolve spec via explicit binding / scene latest / related specs / auto-create
572
575
  - auto-create spec artifacts when no suitable spec is found and policy requires tracking
576
+ - `plan --manual-spec` is blocked by default (`allow_manual_spec_override=false`)
577
+ - `plan --no-spec-governance` is blocked by default (`governance.require_auto_on_plan=true`)
573
578
  - `plan --spec <id>` (recommended) ingests `.sce/specs/<spec>/custom/problem-domain-chain.json` into studio job context
574
579
  - when `--spec` is omitted, `plan` auto-resolves the latest matching spec chain by `scene_id` when available
575
580
  - `plan` auto-searches related historical specs by `scene + goal` and writes top candidates into job metadata (`source.related_specs`)
576
581
  - `plan` auto-runs scene spec governance snapshot and writes:
577
582
  - `.sce/spec-governance/scene-portfolio.latest.json`
578
583
  - `.sce/spec-governance/scene-index.json`
584
+ - historical spec scene backfill can be managed via:
585
+ - `sce studio backfill-spec-scenes --apply`
586
+ - writes `.sce/spec-governance/spec-scene-overrides.json`
579
587
  - successful `release` auto-archives current scene session and auto-opens the next scene cycle session
580
588
  - `generate` requires `plan`
581
589
  - `generate` consumes the plan-stage domain-chain context and writes chain-aware metadata/report (`.sce/reports/studio/generate-<job-id>.json`)
@@ -657,13 +665,21 @@ Studio intake policy file (default, recommended to commit): `.sce/config/studio-
657
665
  "enabled": true,
658
666
  "auto_create_spec": true,
659
667
  "force_spec_for_studio_plan": true,
668
+ "allow_manual_spec_override": false,
660
669
  "prefer_existing_scene_spec": true,
661
670
  "related_spec_min_score": 45,
662
671
  "governance": {
663
672
  "auto_run_on_plan": true,
673
+ "require_auto_on_plan": true,
664
674
  "max_active_specs_per_scene": 3,
665
675
  "stale_days": 14,
666
676
  "duplicate_similarity_threshold": 0.66
677
+ },
678
+ "backfill": {
679
+ "enabled": true,
680
+ "active_only_default": true,
681
+ "default_scene_id": "scene.sce-core",
682
+ "override_file": ".sce/spec-governance/spec-scene-overrides.json"
667
683
  }
668
684
  }
669
685
  ```
@@ -12,8 +12,10 @@ const { findRelatedSpecs } = require('../spec/related-specs');
12
12
  const { captureTimelineCheckpoint } = require('../runtime/project-timeline');
13
13
  const { runProblemEvaluation } = require('../problem/problem-evaluator');
14
14
  const {
15
+ loadStudioIntakePolicy,
15
16
  runStudioAutoIntake,
16
- runStudioSpecGovernance
17
+ runStudioSpecGovernance,
18
+ runStudioSceneBackfill
17
19
  } = require('../studio/spec-intake-governor');
18
20
 
19
21
  const STUDIO_JOB_API_VERSION = 'sce.studio.job/v0.1';
@@ -1358,6 +1360,20 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1358
1360
  throw new Error('--scene is required');
1359
1361
  }
1360
1362
 
1363
+ const intakePolicyBundle = await loadStudioIntakePolicy(projectPath, fileSystem);
1364
+ const intakePolicy = intakePolicyBundle.policy || {};
1365
+ const governancePolicy = intakePolicy.governance || {};
1366
+ if (manualSpecMode && intakePolicy.allow_manual_spec_override !== true) {
1367
+ throw new Error(
1368
+ '--manual-spec is disabled by studio intake policy (allow_manual_spec_override=false)'
1369
+ );
1370
+ }
1371
+ if (skipSpecGovernance && governancePolicy.require_auto_on_plan !== false) {
1372
+ throw new Error(
1373
+ '--no-spec-governance is disabled by studio intake policy (governance.require_auto_on_plan=true)'
1374
+ );
1375
+ }
1376
+
1361
1377
  let domainChainBinding = await resolveDomainChainBinding({
1362
1378
  sceneId,
1363
1379
  specId,
@@ -2383,6 +2399,19 @@ function printStudioPortfolioPayload(payload, options = {}) {
2383
2399
  console.log(` Overflow scenes: ${summary.overflow_scenes || 0}`);
2384
2400
  }
2385
2401
 
2402
+ function printStudioBackfillPayload(payload, options = {}) {
2403
+ if (options.json) {
2404
+ console.log(JSON.stringify(payload, null, 2));
2405
+ return;
2406
+ }
2407
+ console.log(chalk.blue('Studio scene backfill'));
2408
+ console.log(` Source scene: ${payload.source_scene || 'scene.unassigned'}`);
2409
+ console.log(` Candidates: ${payload.summary ? payload.summary.candidate_count : 0}`);
2410
+ console.log(` Changed: ${payload.summary ? payload.summary.changed_count : 0}`);
2411
+ console.log(` Apply: ${payload.apply ? 'yes' : 'no'}`);
2412
+ console.log(` Override file: ${payload.override_file || 'n/a'}`);
2413
+ }
2414
+
2386
2415
  async function runStudioPortfolioCommand(options = {}, dependencies = {}) {
2387
2416
  const projectPath = dependencies.projectPath || process.cwd();
2388
2417
  const fileSystem = dependencies.fileSystem || fs;
@@ -2404,6 +2433,29 @@ async function runStudioPortfolioCommand(options = {}, dependencies = {}) {
2404
2433
  return payload;
2405
2434
  }
2406
2435
 
2436
+ async function runStudioBackfillSpecScenesCommand(options = {}, dependencies = {}) {
2437
+ const projectPath = dependencies.projectPath || process.cwd();
2438
+ const fileSystem = dependencies.fileSystem || fs;
2439
+ const backfillOptions = {
2440
+ scene: normalizeString(options.scene),
2441
+ all: options.all === true,
2442
+ limit: options.limit,
2443
+ apply: options.apply === true,
2444
+ refresh_governance: options.refreshGovernance !== false
2445
+ };
2446
+ if (options.activeOnly === true) {
2447
+ backfillOptions.active_only = true;
2448
+ }
2449
+
2450
+ const payload = await runStudioSceneBackfill(backfillOptions, {
2451
+ projectPath,
2452
+ fileSystem
2453
+ });
2454
+
2455
+ printStudioBackfillPayload(payload, options);
2456
+ return payload;
2457
+ }
2458
+
2407
2459
  async function runStudioCommand(handler, options, stageName = '') {
2408
2460
  try {
2409
2461
  const stage = normalizeString(stageName) || 'unknown';
@@ -2447,9 +2499,9 @@ function registerStudioCommands(program) {
2447
2499
  .requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
2448
2500
  .option('--spec <spec-id>', 'Optional spec binding for domain-chain context ingestion')
2449
2501
  .option('--goal <goal>', 'Optional goal summary')
2450
- .option('--manual-spec', 'Disable auto intake and only use explicit --spec or existing scene binding')
2502
+ .option('--manual-spec', 'Legacy bypass flag (disabled by default policy)')
2451
2503
  .option('--target <target>', 'Target integration profile', 'default')
2452
- .option('--no-spec-governance', 'Skip auto portfolio governance snapshot on plan stage')
2504
+ .option('--no-spec-governance', 'Legacy bypass flag (disabled by default policy)')
2453
2505
  .option('--job <job-id>', 'Reuse an explicit studio job id')
2454
2506
  .option('--json', 'Print machine-readable JSON output')
2455
2507
  .action(async (options) => runStudioCommand(runStudioPlanCommand, options, 'plan'));
@@ -2462,7 +2514,7 @@ function registerStudioCommands(program) {
2462
2514
  .option('--spec <spec-id>', 'Optional explicit spec id')
2463
2515
  .option('--goal <goal>', 'Goal text used for intent classification')
2464
2516
  .option('--apply', 'Create spec when decision is create_spec')
2465
- .option('--manual-spec', 'Disable auto intake and keep explicit/manual binding only')
2517
+ .option('--manual-spec', 'Legacy bypass flag (disabled by default policy)')
2466
2518
  .option('--json', 'Print machine-readable JSON output')
2467
2519
  .action(async (options) => runStudioCommand(runStudioIntakeCommand, options, 'intake'));
2468
2520
 
@@ -2475,6 +2527,18 @@ function registerStudioCommands(program) {
2475
2527
  .option('--json', 'Print machine-readable JSON output')
2476
2528
  .action(async (options) => runStudioCommand(runStudioPortfolioCommand, options, 'portfolio'));
2477
2529
 
2530
+ studio
2531
+ .command('backfill-spec-scenes')
2532
+ .description('Backfill scene bindings for historical specs (writes override mapping when --apply)')
2533
+ .option('--scene <scene-id>', 'Source scene filter (default: scene.unassigned)')
2534
+ .option('--all', 'Include completed/stale specs (default uses active-only policy)')
2535
+ .option('--active-only', 'Force active-only filtering')
2536
+ .option('--limit <n>', 'Maximum number of specs to process')
2537
+ .option('--apply', 'Write mapping to .sce/spec-governance/spec-scene-overrides.json')
2538
+ .option('--no-refresh-governance', 'Skip portfolio refresh after apply')
2539
+ .option('--json', 'Print machine-readable JSON output')
2540
+ .action(async (options) => runStudioCommand(runStudioBackfillSpecScenesCommand, options, 'backfill-spec-scenes'));
2541
+
2478
2542
  studio
2479
2543
  .command('generate')
2480
2544
  .description('Generate patch bundle metadata for a planned studio job (scene inherited from plan)')
@@ -2567,6 +2631,7 @@ module.exports = {
2567
2631
  runStudioRollbackCommand,
2568
2632
  runStudioEventsCommand,
2569
2633
  runStudioPortfolioCommand,
2634
+ runStudioBackfillSpecScenesCommand,
2570
2635
  runStudioResumeCommand,
2571
2636
  registerStudioCommands
2572
2637
  };
@@ -1,6 +1,10 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs-extra');
3
3
  const { DOMAIN_CHAIN_RELATIVE_PATH } = require('./domain-modeling');
4
+ const {
5
+ loadSceneBindingOverrides,
6
+ resolveSceneIdFromOverrides
7
+ } = require('./scene-binding-overrides');
4
8
 
5
9
  function normalizeText(value) {
6
10
  if (typeof value !== 'string') {
@@ -70,6 +74,8 @@ async function resolveSpecSearchEntries(projectPath, fileSystem = fs) {
70
74
  return [];
71
75
  }
72
76
 
77
+ const overrideContext = await loadSceneBindingOverrides(projectPath, {}, fileSystem);
78
+ const overrides = overrideContext.overrides;
73
79
  const names = await fileSystem.readdir(specsRoot);
74
80
  const entries = [];
75
81
 
@@ -106,7 +112,10 @@ async function resolveSpecSearchEntries(projectPath, fileSystem = fs) {
106
112
  ]);
107
113
 
108
114
  const sceneId = normalizeText(
109
- (domainChain && domainChain.scene_id) || extractSceneIdFromSceneSpec(sceneSpecContent) || ''
115
+ (domainChain && domainChain.scene_id)
116
+ || extractSceneIdFromSceneSpec(sceneSpecContent)
117
+ || resolveSceneIdFromOverrides(specId, overrides)
118
+ || ''
110
119
  ) || null;
111
120
  const problemStatement = normalizeText(
112
121
  (domainChain && domainChain.problem && domainChain.problem.statement) || ''
@@ -257,4 +266,3 @@ module.exports = {
257
266
  calculateSpecRelevance,
258
267
  findRelatedSpecs
259
268
  };
260
-
@@ -0,0 +1,115 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+
4
+ const DEFAULT_SPEC_SCENE_OVERRIDE_PATH = '.sce/spec-governance/spec-scene-overrides.json';
5
+
6
+ function normalizeText(value) {
7
+ if (typeof value !== 'string') {
8
+ return '';
9
+ }
10
+ return value.trim();
11
+ }
12
+
13
+ function normalizeOverrideEntry(specId, payload = {}) {
14
+ const normalizedSpecId = normalizeText(specId);
15
+ if (!normalizedSpecId) {
16
+ return null;
17
+ }
18
+
19
+ if (typeof payload === 'string') {
20
+ const sceneId = normalizeText(payload);
21
+ if (!sceneId) {
22
+ return null;
23
+ }
24
+ return {
25
+ spec_id: normalizedSpecId,
26
+ scene_id: sceneId,
27
+ source: 'override',
28
+ rule_id: null,
29
+ updated_at: null
30
+ };
31
+ }
32
+
33
+ const sceneId = normalizeText(payload && payload.scene_id);
34
+ if (!sceneId) {
35
+ return null;
36
+ }
37
+ return {
38
+ spec_id: normalizedSpecId,
39
+ scene_id: sceneId,
40
+ source: normalizeText(payload.source) || 'override',
41
+ rule_id: normalizeText(payload.rule_id) || null,
42
+ updated_at: normalizeText(payload.updated_at) || null
43
+ };
44
+ }
45
+
46
+ function normalizeSceneBindingOverrides(raw = {}) {
47
+ const payload = raw && typeof raw === 'object' ? raw : {};
48
+ const mappingsRaw = payload.mappings && typeof payload.mappings === 'object'
49
+ ? payload.mappings
50
+ : {};
51
+ const mappings = {};
52
+ for (const [specId, entry] of Object.entries(mappingsRaw)) {
53
+ const normalized = normalizeOverrideEntry(specId, entry);
54
+ if (!normalized) {
55
+ continue;
56
+ }
57
+ mappings[normalized.spec_id] = {
58
+ scene_id: normalized.scene_id,
59
+ source: normalized.source,
60
+ rule_id: normalized.rule_id,
61
+ updated_at: normalized.updated_at
62
+ };
63
+ }
64
+ return {
65
+ schema_version: normalizeText(payload.schema_version) || '1.0',
66
+ generated_at: normalizeText(payload.generated_at) || null,
67
+ updated_at: normalizeText(payload.updated_at) || null,
68
+ mappings
69
+ };
70
+ }
71
+
72
+ async function loadSceneBindingOverrides(projectPath = process.cwd(), options = {}, fileSystem = fs) {
73
+ const overridePath = normalizeText(options.override_path || options.overridePath)
74
+ || DEFAULT_SPEC_SCENE_OVERRIDE_PATH;
75
+ const absolutePath = path.join(projectPath, overridePath);
76
+ let payload = {};
77
+ let loadedFrom = 'default';
78
+ if (await fileSystem.pathExists(absolutePath)) {
79
+ try {
80
+ payload = await fileSystem.readJson(absolutePath);
81
+ loadedFrom = 'file';
82
+ } catch (_error) {
83
+ payload = {};
84
+ loadedFrom = 'default';
85
+ }
86
+ }
87
+ return {
88
+ override_path: overridePath,
89
+ absolute_path: absolutePath,
90
+ loaded_from: loadedFrom,
91
+ overrides: normalizeSceneBindingOverrides(payload)
92
+ };
93
+ }
94
+
95
+ function resolveSceneIdFromOverrides(specId, overrides = {}) {
96
+ const normalizedSpecId = normalizeText(specId);
97
+ if (!normalizedSpecId) {
98
+ return null;
99
+ }
100
+ const mappings = overrides && typeof overrides === 'object' && overrides.mappings
101
+ ? overrides.mappings
102
+ : {};
103
+ const entry = mappings[normalizedSpecId];
104
+ if (!entry || typeof entry !== 'object') {
105
+ return null;
106
+ }
107
+ return normalizeText(entry.scene_id) || null;
108
+ }
109
+
110
+ module.exports = {
111
+ DEFAULT_SPEC_SCENE_OVERRIDE_PATH,
112
+ normalizeSceneBindingOverrides,
113
+ loadSceneBindingOverrides,
114
+ resolveSceneIdFromOverrides
115
+ };
@@ -2,17 +2,63 @@ const path = require('path');
2
2
  const fs = require('fs-extra');
3
3
  const { DraftGenerator } = require('../spec/bootstrap/draft-generator');
4
4
  const { ensureSpecDomainArtifacts } = require('../spec/domain-modeling');
5
+ const {
6
+ DEFAULT_SPEC_SCENE_OVERRIDE_PATH,
7
+ loadSceneBindingOverrides,
8
+ normalizeSceneBindingOverrides,
9
+ resolveSceneIdFromOverrides
10
+ } = require('../spec/scene-binding-overrides');
5
11
 
6
12
  const DEFAULT_STUDIO_INTAKE_POLICY_PATH = '.sce/config/studio-intake-policy.json';
7
13
  const DEFAULT_STUDIO_GOVERNANCE_DIR = '.sce/spec-governance';
8
14
  const DEFAULT_STUDIO_PORTFOLIO_REPORT = `${DEFAULT_STUDIO_GOVERNANCE_DIR}/scene-portfolio.latest.json`;
9
15
  const DEFAULT_STUDIO_SCENE_INDEX = `${DEFAULT_STUDIO_GOVERNANCE_DIR}/scene-index.json`;
16
+ const DEFAULT_STUDIO_SCENE_OVERRIDE_PATH = DEFAULT_SPEC_SCENE_OVERRIDE_PATH;
17
+
18
+ const DEFAULT_STUDIO_SCENE_BACKFILL_RULES = Object.freeze([
19
+ {
20
+ id: 'moqui-core',
21
+ scene_id: 'scene.moqui-core',
22
+ keywords: ['moqui']
23
+ },
24
+ {
25
+ id: 'orchestration',
26
+ scene_id: 'scene.sce-orchestration',
27
+ keywords: ['orchestrate', 'runtime', 'controller', 'batch', 'parallel']
28
+ },
29
+ {
30
+ id: 'template-registry',
31
+ scene_id: 'scene.sce-template-registry',
32
+ keywords: ['template', 'scene-package', 'registry', 'catalog', 'scene-template']
33
+ },
34
+ {
35
+ id: 'spec-governance',
36
+ scene_id: 'scene.sce-spec-governance',
37
+ keywords: ['spec', 'gate', 'ontology', 'governance', 'policy']
38
+ },
39
+ {
40
+ id: 'quality',
41
+ scene_id: 'scene.sce-quality',
42
+ keywords: ['test', 'quality', 'stability', 'jest', 'coverage']
43
+ },
44
+ {
45
+ id: 'docs',
46
+ scene_id: 'scene.sce-docs',
47
+ keywords: ['document', 'documentation', 'onboarding', 'guide']
48
+ },
49
+ {
50
+ id: 'platform',
51
+ scene_id: 'scene.sce-platform',
52
+ keywords: ['adopt', 'upgrade', 'workspace', 'repo', 'environment', 'devops', 'release', 'github', 'npm']
53
+ }
54
+ ]);
10
55
 
11
56
  const DEFAULT_STUDIO_INTAKE_POLICY = Object.freeze({
12
57
  schema_version: '1.0',
13
58
  enabled: true,
14
59
  auto_create_spec: true,
15
60
  force_spec_for_studio_plan: true,
61
+ allow_manual_spec_override: false,
16
62
  prefer_existing_scene_spec: true,
17
63
  related_spec_min_score: 45,
18
64
  allow_new_spec_when_goal_diverges: true,
@@ -33,9 +79,17 @@ const DEFAULT_STUDIO_INTAKE_POLICY = Object.freeze({
33
79
  },
34
80
  governance: {
35
81
  auto_run_on_plan: true,
82
+ require_auto_on_plan: true,
36
83
  max_active_specs_per_scene: 3,
37
84
  stale_days: 14,
38
85
  duplicate_similarity_threshold: 0.66
86
+ },
87
+ backfill: {
88
+ enabled: true,
89
+ active_only_default: true,
90
+ default_scene_id: 'scene.sce-core',
91
+ override_file: DEFAULT_STUDIO_SCENE_OVERRIDE_PATH,
92
+ rules: DEFAULT_STUDIO_SCENE_BACKFILL_RULES
39
93
  }
40
94
  });
41
95
 
@@ -87,6 +141,30 @@ function normalizeTextList(value = []) {
87
141
  .filter(Boolean);
88
142
  }
89
143
 
144
+ function normalizeBackfillRules(value = []) {
145
+ if (!Array.isArray(value)) {
146
+ return [];
147
+ }
148
+ const rules = [];
149
+ for (const item of value) {
150
+ if (!item || typeof item !== 'object') {
151
+ continue;
152
+ }
153
+ const id = normalizeText(item.id);
154
+ const sceneId = normalizeText(item.scene_id || item.sceneId);
155
+ const keywords = normalizeTextList(item.keywords || item.match_any_keywords || item.matchAnyKeywords);
156
+ if (!id || !sceneId || keywords.length === 0) {
157
+ continue;
158
+ }
159
+ rules.push({
160
+ id,
161
+ scene_id: sceneId,
162
+ keywords
163
+ });
164
+ }
165
+ return rules;
166
+ }
167
+
90
168
  function toRelativePosix(projectPath, absolutePath) {
91
169
  return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
92
170
  }
@@ -169,6 +247,8 @@ function normalizeStudioIntakePolicy(raw = {}) {
169
247
  const payload = raw && typeof raw === 'object' ? raw : {};
170
248
  const specId = payload.spec_id && typeof payload.spec_id === 'object' ? payload.spec_id : {};
171
249
  const governance = payload.governance && typeof payload.governance === 'object' ? payload.governance : {};
250
+ const backfill = payload.backfill && typeof payload.backfill === 'object' ? payload.backfill : {};
251
+ const normalizedBackfillRules = normalizeBackfillRules(backfill.rules);
172
252
 
173
253
  return {
174
254
  schema_version: normalizeText(payload.schema_version) || DEFAULT_STUDIO_INTAKE_POLICY.schema_version,
@@ -178,6 +258,10 @@ function normalizeStudioIntakePolicy(raw = {}) {
178
258
  payload.force_spec_for_studio_plan,
179
259
  DEFAULT_STUDIO_INTAKE_POLICY.force_spec_for_studio_plan
180
260
  ),
261
+ allow_manual_spec_override: normalizeBoolean(
262
+ payload.allow_manual_spec_override,
263
+ DEFAULT_STUDIO_INTAKE_POLICY.allow_manual_spec_override
264
+ ),
181
265
  prefer_existing_scene_spec: normalizeBoolean(
182
266
  payload.prefer_existing_scene_spec,
183
267
  DEFAULT_STUDIO_INTAKE_POLICY.prefer_existing_scene_spec
@@ -224,6 +308,10 @@ function normalizeStudioIntakePolicy(raw = {}) {
224
308
  governance.auto_run_on_plan,
225
309
  DEFAULT_STUDIO_INTAKE_POLICY.governance.auto_run_on_plan
226
310
  ),
311
+ require_auto_on_plan: normalizeBoolean(
312
+ governance.require_auto_on_plan,
313
+ DEFAULT_STUDIO_INTAKE_POLICY.governance.require_auto_on_plan
314
+ ),
227
315
  max_active_specs_per_scene: normalizeInteger(
228
316
  governance.max_active_specs_per_scene,
229
317
  DEFAULT_STUDIO_INTAKE_POLICY.governance.max_active_specs_per_scene,
@@ -243,6 +331,23 @@ function normalizeStudioIntakePolicy(raw = {}) {
243
331
  DEFAULT_STUDIO_INTAKE_POLICY.governance.duplicate_similarity_threshold
244
332
  ))
245
333
  )
334
+ },
335
+ backfill: {
336
+ enabled: normalizeBoolean(
337
+ backfill.enabled,
338
+ DEFAULT_STUDIO_INTAKE_POLICY.backfill.enabled
339
+ ),
340
+ active_only_default: normalizeBoolean(
341
+ backfill.active_only_default,
342
+ DEFAULT_STUDIO_INTAKE_POLICY.backfill.active_only_default
343
+ ),
344
+ default_scene_id: normalizeText(backfill.default_scene_id)
345
+ || DEFAULT_STUDIO_INTAKE_POLICY.backfill.default_scene_id,
346
+ override_file: normalizeText(backfill.override_file)
347
+ || DEFAULT_STUDIO_INTAKE_POLICY.backfill.override_file,
348
+ rules: normalizedBackfillRules.length > 0
349
+ ? normalizedBackfillRules
350
+ : normalizeBackfillRules(DEFAULT_STUDIO_INTAKE_POLICY.backfill.rules)
246
351
  }
247
352
  };
248
353
  }
@@ -611,6 +716,12 @@ async function runStudioAutoIntake(options = {}, dependencies = {}) {
611
716
  created_spec: null
612
717
  };
613
718
 
719
+ if (skip && policy.allow_manual_spec_override !== true) {
720
+ throw new Error(
721
+ 'manual spec override is disabled by studio intake policy (allow_manual_spec_override=false)'
722
+ );
723
+ }
724
+
614
725
  if (skip) {
615
726
  payload.enabled = false;
616
727
  payload.decision = {
@@ -705,6 +816,10 @@ async function scanSpecPortfolio(projectPath = process.cwd(), options = {}, depe
705
816
  return [];
706
817
  }
707
818
  const staleDays = normalizeInteger(options.stale_days, 14, 1, 3650);
819
+ const overrideContext = await loadSceneBindingOverrides(projectPath, {
820
+ overridePath: options.override_file || DEFAULT_STUDIO_SCENE_OVERRIDE_PATH
821
+ }, fileSystem);
822
+ const sceneOverrides = normalizeSceneBindingOverrides(overrideContext.overrides || {});
708
823
  const entries = await fileSystem.readdir(specsRoot);
709
824
  const records = [];
710
825
 
@@ -733,9 +848,12 @@ async function scanSpecPortfolio(projectPath = process.cwd(), options = {}, depe
733
848
  readFileSafe(tasksPath, fileSystem)
734
849
  ]);
735
850
 
736
- const sceneId = normalizeText(
737
- chain && chain.scene_id ? chain.scene_id : ''
738
- ) || 'scene.unassigned';
851
+ const sceneFromChain = normalizeText(chain && chain.scene_id ? chain.scene_id : '');
852
+ const sceneFromOverride = resolveSceneIdFromOverrides(entry, sceneOverrides);
853
+ const sceneId = sceneFromChain || sceneFromOverride || 'scene.unassigned';
854
+ const sceneSource = sceneFromChain
855
+ ? 'domain-chain'
856
+ : (sceneFromOverride ? 'override' : 'unassigned');
739
857
  const problemStatement = normalizeText(
740
858
  (chain && chain.problem && chain.problem.statement)
741
859
  || (contract && contract.issue_statement)
@@ -767,6 +885,7 @@ async function scanSpecPortfolio(projectPath = process.cwd(), options = {}, depe
767
885
  tasks_progress: taskProgress.ratio,
768
886
  lifecycle_state: lifecycle.state,
769
887
  age_days: lifecycle.age_days,
888
+ scene_source: sceneSource,
770
889
  tokens
771
890
  });
772
891
  }
@@ -889,6 +1008,202 @@ function buildSceneGovernanceReport(records = [], policy = DEFAULT_STUDIO_INTAKE
889
1008
  };
890
1009
  }
891
1010
 
1011
+ function classifyBackfillRule(record = {}, backfillPolicy = {}) {
1012
+ const rules = Array.isArray(backfillPolicy.rules) ? backfillPolicy.rules : [];
1013
+ const defaultSceneId = normalizeText(backfillPolicy.default_scene_id) || 'scene.sce-core';
1014
+ const searchText = [
1015
+ normalizeText(record.spec_id).toLowerCase(),
1016
+ normalizeText(record.problem_statement).toLowerCase()
1017
+ ].join(' ');
1018
+ const searchTokens = new Set(tokenizeText(searchText));
1019
+ let bestMatch = null;
1020
+
1021
+ for (const rule of rules) {
1022
+ const ruleId = normalizeText(rule.id);
1023
+ const sceneId = normalizeText(rule.scene_id);
1024
+ const keywords = normalizeTextList(rule.keywords).map((item) => item.toLowerCase());
1025
+ if (!ruleId || !sceneId || keywords.length === 0) {
1026
+ continue;
1027
+ }
1028
+
1029
+ const matchedKeywords = [];
1030
+ for (const keyword of keywords) {
1031
+ if (!keyword) {
1032
+ continue;
1033
+ }
1034
+ if (searchText.includes(keyword) || searchTokens.has(keyword)) {
1035
+ matchedKeywords.push(keyword);
1036
+ }
1037
+ }
1038
+ if (matchedKeywords.length === 0) {
1039
+ continue;
1040
+ }
1041
+
1042
+ const score = matchedKeywords.length;
1043
+ if (!bestMatch || score > bestMatch.score) {
1044
+ bestMatch = {
1045
+ rule_id: ruleId,
1046
+ scene_id: sceneId,
1047
+ matched_keywords: matchedKeywords,
1048
+ score
1049
+ };
1050
+ }
1051
+ }
1052
+
1053
+ if (!bestMatch) {
1054
+ return {
1055
+ scene_id: defaultSceneId,
1056
+ rule_id: 'default',
1057
+ matched_keywords: [],
1058
+ confidence: 'low',
1059
+ source: 'default'
1060
+ };
1061
+ }
1062
+
1063
+ return {
1064
+ scene_id: bestMatch.scene_id,
1065
+ rule_id: bestMatch.rule_id,
1066
+ matched_keywords: bestMatch.matched_keywords,
1067
+ confidence: bestMatch.score >= 2 ? 'high' : 'medium',
1068
+ source: 'rule'
1069
+ };
1070
+ }
1071
+
1072
+ function clampBackfillLimit(value, fallback = 0, max = 1000) {
1073
+ if (value === undefined || value === null || value === '') {
1074
+ return fallback;
1075
+ }
1076
+ const parsed = Number.parseInt(String(value), 10);
1077
+ if (!Number.isFinite(parsed) || parsed < 0) {
1078
+ return fallback;
1079
+ }
1080
+ return Math.min(parsed, max);
1081
+ }
1082
+
1083
+ async function runStudioSceneBackfill(options = {}, dependencies = {}) {
1084
+ const projectPath = dependencies.projectPath || process.cwd();
1085
+ const fileSystem = dependencies.fileSystem || fs;
1086
+ const loaded = options.policy && typeof options.policy === 'object'
1087
+ ? { policy: normalizeStudioIntakePolicy(options.policy), policy_path: '(inline)', loaded_from: 'inline' }
1088
+ : await loadStudioIntakePolicy(projectPath, fileSystem);
1089
+ const policy = loaded.policy;
1090
+ const backfillPolicy = policy.backfill || DEFAULT_STUDIO_INTAKE_POLICY.backfill;
1091
+ const apply = options.apply === true;
1092
+ const refreshGovernance = options.refresh_governance !== false && options.refreshGovernance !== false;
1093
+ const sourceScene = normalizeText(options.source_scene || options.sourceScene || options.scene) || 'scene.unassigned';
1094
+ const includeAll = options.all === true || options.active_only === false || options.activeOnly === false;
1095
+ const activeOnly = includeAll ? false : (options.active_only === true || options.activeOnly === true || backfillPolicy.active_only_default !== false);
1096
+ const limit = clampBackfillLimit(options.limit, 0, 2000);
1097
+ const overrideFile = normalizeText(backfillPolicy.override_file) || DEFAULT_STUDIO_SCENE_OVERRIDE_PATH;
1098
+
1099
+ const records = await scanSpecPortfolio(projectPath, {
1100
+ stale_days: policy.governance && policy.governance.stale_days,
1101
+ override_file: overrideFile
1102
+ }, {
1103
+ fileSystem
1104
+ });
1105
+
1106
+ let candidates = records.filter((item) => normalizeText(item.scene_id) === sourceScene);
1107
+ if (activeOnly) {
1108
+ candidates = candidates.filter((item) => item.lifecycle_state === 'active');
1109
+ }
1110
+ if (limit > 0) {
1111
+ candidates = candidates.slice(0, limit);
1112
+ }
1113
+
1114
+ const assignmentPlan = candidates.map((record) => {
1115
+ const decision = classifyBackfillRule(record, backfillPolicy);
1116
+ return {
1117
+ spec_id: record.spec_id,
1118
+ from_scene_id: sourceScene,
1119
+ to_scene_id: decision.scene_id,
1120
+ lifecycle_state: record.lifecycle_state,
1121
+ rule_id: decision.rule_id,
1122
+ source: decision.source,
1123
+ confidence: decision.confidence,
1124
+ matched_keywords: decision.matched_keywords
1125
+ };
1126
+ });
1127
+
1128
+ const overrideContext = await loadSceneBindingOverrides(projectPath, {
1129
+ overridePath: overrideFile
1130
+ }, fileSystem);
1131
+ const existingOverrides = normalizeSceneBindingOverrides(overrideContext.overrides || {});
1132
+ const nextOverrides = normalizeSceneBindingOverrides(existingOverrides);
1133
+ const now = new Date().toISOString();
1134
+ let changedCount = 0;
1135
+
1136
+ for (const item of assignmentPlan) {
1137
+ const existing = existingOverrides.mappings[item.spec_id];
1138
+ const currentScene = normalizeText(existing && existing.scene_id);
1139
+ if (currentScene === item.to_scene_id) {
1140
+ continue;
1141
+ }
1142
+ nextOverrides.mappings[item.spec_id] = {
1143
+ scene_id: item.to_scene_id,
1144
+ source: 'scene-backfill',
1145
+ rule_id: item.rule_id,
1146
+ updated_at: now
1147
+ };
1148
+ changedCount += 1;
1149
+ }
1150
+
1151
+ const totalsByTargetScene = {};
1152
+ for (const item of assignmentPlan) {
1153
+ totalsByTargetScene[item.to_scene_id] = (totalsByTargetScene[item.to_scene_id] || 0) + 1;
1154
+ }
1155
+
1156
+ const payload = {
1157
+ mode: 'studio-scene-backfill',
1158
+ success: true,
1159
+ generated_at: now,
1160
+ policy_path: loaded.policy_path,
1161
+ policy_loaded_from: loaded.loaded_from,
1162
+ source_scene: sourceScene,
1163
+ active_only: activeOnly,
1164
+ apply,
1165
+ refresh_governance: refreshGovernance,
1166
+ override_file: overrideFile,
1167
+ summary: {
1168
+ candidate_count: assignmentPlan.length,
1169
+ changed_count: changedCount,
1170
+ target_scene_count: Object.keys(totalsByTargetScene).length
1171
+ },
1172
+ targets: totalsByTargetScene,
1173
+ assignments: assignmentPlan
1174
+ };
1175
+
1176
+ if (apply) {
1177
+ const overrideAbsolutePath = path.join(projectPath, overrideFile);
1178
+ await fileSystem.ensureDir(path.dirname(overrideAbsolutePath));
1179
+ const serialized = {
1180
+ schema_version: '1.0',
1181
+ generated_at: nextOverrides.generated_at || now,
1182
+ updated_at: now,
1183
+ source: 'studio-scene-backfill',
1184
+ mappings: nextOverrides.mappings
1185
+ };
1186
+ await fileSystem.writeJson(overrideAbsolutePath, serialized, { spaces: 2 });
1187
+ payload.override_written = overrideFile;
1188
+ if (refreshGovernance) {
1189
+ const refreshed = await runStudioSpecGovernance({
1190
+ apply: true
1191
+ }, {
1192
+ projectPath,
1193
+ fileSystem
1194
+ });
1195
+ payload.governance = {
1196
+ status: refreshed.summary ? refreshed.summary.status : null,
1197
+ alert_count: refreshed.summary ? Number(refreshed.summary.alert_count || 0) : 0,
1198
+ report_file: refreshed.report_file || null,
1199
+ scene_index_file: refreshed.scene_index_file || null
1200
+ };
1201
+ }
1202
+ }
1203
+
1204
+ return payload;
1205
+ }
1206
+
892
1207
  async function runStudioSpecGovernance(options = {}, dependencies = {}) {
893
1208
  const projectPath = dependencies.projectPath || process.cwd();
894
1209
  const fileSystem = dependencies.fileSystem || fs;
@@ -897,11 +1212,14 @@ async function runStudioSpecGovernance(options = {}, dependencies = {}) {
897
1212
  : await loadStudioIntakePolicy(projectPath, fileSystem);
898
1213
  const policy = loaded.policy;
899
1214
  const governance = policy.governance || DEFAULT_STUDIO_INTAKE_POLICY.governance;
1215
+ const backfill = policy.backfill || DEFAULT_STUDIO_INTAKE_POLICY.backfill;
900
1216
  const apply = options.apply !== false;
901
1217
  const sceneFilter = normalizeText(options.scene_id || options.sceneId || options.scene);
1218
+ const overrideFile = normalizeText(backfill.override_file) || DEFAULT_STUDIO_SCENE_OVERRIDE_PATH;
902
1219
 
903
1220
  const records = await scanSpecPortfolio(projectPath, {
904
- stale_days: governance.stale_days
1221
+ stale_days: governance.stale_days,
1222
+ override_file: overrideFile
905
1223
  }, {
906
1224
  fileSystem
907
1225
  });
@@ -920,7 +1238,10 @@ async function runStudioSpecGovernance(options = {}, dependencies = {}) {
920
1238
  policy_path: loaded.policy_path,
921
1239
  policy_loaded_from: loaded.loaded_from,
922
1240
  policy: {
923
- governance
1241
+ governance,
1242
+ backfill: {
1243
+ override_file: overrideFile
1244
+ }
924
1245
  },
925
1246
  summary: {
926
1247
  scene_count: summary.scene_count,
@@ -974,6 +1295,7 @@ async function runStudioSpecGovernance(options = {}, dependencies = {}) {
974
1295
  module.exports = {
975
1296
  DEFAULT_STUDIO_INTAKE_POLICY_PATH,
976
1297
  DEFAULT_STUDIO_INTAKE_POLICY,
1298
+ DEFAULT_STUDIO_SCENE_OVERRIDE_PATH,
977
1299
  DEFAULT_STUDIO_PORTFOLIO_REPORT,
978
1300
  DEFAULT_STUDIO_SCENE_INDEX,
979
1301
  normalizeStudioIntakePolicy,
@@ -986,6 +1308,7 @@ module.exports = {
986
1308
  parseTasksProgress,
987
1309
  scanSpecPortfolio,
988
1310
  buildSceneGovernanceReport,
1311
+ runStudioSceneBackfill,
989
1312
  runStudioSpecGovernance,
990
1313
  tokenizeText,
991
1314
  computeJaccard
@@ -92,6 +92,7 @@ const STUDIO_INTAKE_POLICY_DEFAULTS = Object.freeze({
92
92
  enabled: true,
93
93
  auto_create_spec: true,
94
94
  force_spec_for_studio_plan: true,
95
+ allow_manual_spec_override: false,
95
96
  prefer_existing_scene_spec: true,
96
97
  related_spec_min_score: 45,
97
98
  allow_new_spec_when_goal_diverges: true,
@@ -112,9 +113,25 @@ const STUDIO_INTAKE_POLICY_DEFAULTS = Object.freeze({
112
113
  },
113
114
  governance: {
114
115
  auto_run_on_plan: true,
116
+ require_auto_on_plan: true,
115
117
  max_active_specs_per_scene: 3,
116
118
  stale_days: 14,
117
119
  duplicate_similarity_threshold: 0.66
120
+ },
121
+ backfill: {
122
+ enabled: true,
123
+ active_only_default: true,
124
+ default_scene_id: 'scene.sce-core',
125
+ override_file: '.sce/spec-governance/spec-scene-overrides.json',
126
+ rules: [
127
+ { id: 'moqui-core', scene_id: 'scene.moqui-core', keywords: ['moqui'] },
128
+ { id: 'orchestration', scene_id: 'scene.sce-orchestration', keywords: ['orchestrate', 'runtime', 'controller', 'batch', 'parallel'] },
129
+ { id: 'template-registry', scene_id: 'scene.sce-template-registry', keywords: ['template', 'scene-package', 'registry', 'catalog', 'scene-template'] },
130
+ { id: 'spec-governance', scene_id: 'scene.sce-spec-governance', keywords: ['spec', 'gate', 'ontology', 'governance', 'policy'] },
131
+ { id: 'quality', scene_id: 'scene.sce-quality', keywords: ['test', 'quality', 'stability', 'jest', 'coverage'] },
132
+ { id: 'docs', scene_id: 'scene.sce-docs', keywords: ['document', 'documentation', 'onboarding', 'guide'] },
133
+ { id: 'platform', scene_id: 'scene.sce-platform', keywords: ['adopt', 'upgrade', 'workspace', 'repo', 'environment', 'devops', 'release', 'github', 'npm'] }
134
+ ]
118
135
  }
119
136
  });
120
137
 
@@ -172,6 +189,7 @@ const TAKEOVER_DEFAULTS = Object.freeze({
172
189
  enabled: true,
173
190
  auto_create_spec: true,
174
191
  force_spec_for_studio_plan: true,
192
+ allow_manual_spec_override: false,
175
193
  prefer_existing_scene_spec: true,
176
194
  related_spec_min_score: 45,
177
195
  allow_new_spec_when_goal_diverges: true,
@@ -179,9 +197,16 @@ const TAKEOVER_DEFAULTS = Object.freeze({
179
197
  goal_missing_strategy: 'create_for_tracking',
180
198
  governance: {
181
199
  auto_run_on_plan: true,
200
+ require_auto_on_plan: true,
182
201
  max_active_specs_per_scene: 3,
183
202
  stale_days: 14,
184
203
  duplicate_similarity_threshold: 0.66
204
+ },
205
+ backfill: {
206
+ enabled: true,
207
+ active_only_default: true,
208
+ default_scene_id: 'scene.sce-core',
209
+ override_file: '.sce/spec-governance/spec-scene-overrides.json'
185
210
  }
186
211
  },
187
212
  debug_policy: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.5.0",
3
+ "version": "3.5.1",
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": {
@@ -3,6 +3,7 @@
3
3
  "enabled": true,
4
4
  "auto_create_spec": true,
5
5
  "force_spec_for_studio_plan": true,
6
+ "allow_manual_spec_override": false,
6
7
  "prefer_existing_scene_spec": true,
7
8
  "related_spec_min_score": 45,
8
9
  "allow_new_spec_when_goal_diverges": true,
@@ -61,8 +62,93 @@
61
62
  },
62
63
  "governance": {
63
64
  "auto_run_on_plan": true,
65
+ "require_auto_on_plan": true,
64
66
  "max_active_specs_per_scene": 3,
65
67
  "stale_days": 14,
66
68
  "duplicate_similarity_threshold": 0.66
69
+ },
70
+ "backfill": {
71
+ "enabled": true,
72
+ "active_only_default": true,
73
+ "default_scene_id": "scene.sce-core",
74
+ "override_file": ".sce/spec-governance/spec-scene-overrides.json",
75
+ "rules": [
76
+ {
77
+ "id": "moqui-core",
78
+ "scene_id": "scene.moqui-core",
79
+ "keywords": [
80
+ "moqui"
81
+ ]
82
+ },
83
+ {
84
+ "id": "orchestration",
85
+ "scene_id": "scene.sce-orchestration",
86
+ "keywords": [
87
+ "orchestrate",
88
+ "runtime",
89
+ "controller",
90
+ "batch",
91
+ "parallel"
92
+ ]
93
+ },
94
+ {
95
+ "id": "template-registry",
96
+ "scene_id": "scene.sce-template-registry",
97
+ "keywords": [
98
+ "template",
99
+ "scene-package",
100
+ "registry",
101
+ "catalog",
102
+ "scene-template"
103
+ ]
104
+ },
105
+ {
106
+ "id": "spec-governance",
107
+ "scene_id": "scene.sce-spec-governance",
108
+ "keywords": [
109
+ "spec",
110
+ "gate",
111
+ "ontology",
112
+ "governance",
113
+ "policy"
114
+ ]
115
+ },
116
+ {
117
+ "id": "quality",
118
+ "scene_id": "scene.sce-quality",
119
+ "keywords": [
120
+ "test",
121
+ "quality",
122
+ "stability",
123
+ "jest",
124
+ "coverage"
125
+ ]
126
+ },
127
+ {
128
+ "id": "docs",
129
+ "scene_id": "scene.sce-docs",
130
+ "keywords": [
131
+ "document",
132
+ "documentation",
133
+ "onboarding",
134
+ "guide"
135
+ ]
136
+ },
137
+ {
138
+ "id": "platform",
139
+ "scene_id": "scene.sce-platform",
140
+ "keywords": [
141
+ "adopt",
142
+ "upgrade",
143
+ "workspace",
144
+ "repo",
145
+ "environment",
146
+ "devops",
147
+ "release",
148
+ "github",
149
+ "npm"
150
+ ]
151
+ }
152
+ ]
67
153
  }
68
154
  }