scene-capability-engine 3.4.6 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.5.0] - 2026-03-02
11
+
12
+ ### Added
13
+ - Studio automatic intake + spec governance baseline:
14
+ - new policy: `.sce/config/studio-intake-policy.json` (template/adopt/takeover managed)
15
+ - `studio plan` now auto-detects goal intent and resolves spec by bind/create strategy
16
+ - new commands:
17
+ - `sce studio intake`
18
+ - `sce studio portfolio`
19
+ - plan stage now auto-writes scene portfolio governance artifacts:
20
+ - `.sce/spec-governance/scene-portfolio.latest.json`
21
+ - `.sce/spec-governance/scene-index.json`
22
+ - New intake/governance module:
23
+ - `lib/studio/spec-intake-governor.js`
24
+ - scene-level duplicate/overflow/stale spec governance summary for portfolio management
25
+
26
+ ### Changed
27
+ - Takeover/adoption managed config baseline now includes `config/studio-intake-policy.json` by default.
28
+ - README/README.zh/command reference updated for auto intake and scene-organized spec governance workflow.
29
+
10
30
  ## [3.4.6] - 2026-03-02
11
31
 
12
32
  ### Added
package/README.md CHANGED
@@ -26,6 +26,7 @@ SCE is designed for teams that want AI agents to deliver software end-to-end wit
26
26
  | Capability | What SCE Provides | Outcome |
27
27
  | --- | --- | --- |
28
28
  | Scene + Spec model | Scene-governed sessions and Spec lifecycle (`requirements/design/tasks`) | Stable context across long AI runs |
29
+ | Auto intake + Spec governance | Goal intent detection, auto spec bind/create, scene portfolio governance | Automatic scene-to-spec tracking with bounded spec growth |
29
30
  | Studio workflow | `studio plan -> generate -> apply -> verify -> release` | Structured chat-to-release execution |
30
31
  | Autonomous delivery | `auto close-loop`, `close-loop-program`, `close-loop-controller` | Unattended bounded convergence |
31
32
  | Multi-agent orchestration | DAG scheduling, retries, 429 adaptive parallel control | Reliable parallel execution at scale |
@@ -106,6 +107,7 @@ SCE now enforces a domain-closed diagnosis and repair route by default:
106
107
  Hard rule defaults:
107
108
  - After two failed rounds on the same problem fingerprint, debug evidence is required in subsequent attempts.
108
109
  - `studio verify/release` run `problem-closure-gate` by default when a spec is bound.
110
+ - `studio plan` auto-runs goal intake (`bind existing spec` or `create spec`) and writes scene portfolio governance snapshots by default.
109
111
 
110
112
  ---
111
113
 
@@ -128,6 +130,7 @@ SCE is tool-agnostic and works with Codex, Claude Code, Cursor, Windsurf, VS Cod
128
130
 
129
131
  ## Important Version Changes
130
132
 
133
+ - `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.
131
134
  - `3.4.6`: Added default `problem-closure-gate` + `problem-contract` baseline and strengthened mandatory problem evaluation dimensions (`problem_contract`/`ontology_alignment`/`convergence`) for verify/release convergence control.
132
135
  - `3.4.5`: `git-managed-gate` now treats worktree checks as advisory in default relaxed CI mode (`CI/GITHUB_ACTIONS`, non-strict), preventing false release blocking.
133
136
  - `3.4.4`: Added `SCE_GIT_MANAGEMENT_ALLOW_UNTRACKED=1` / `--allow-untracked`; release workflow uses it for npm publish after generating release evidence artifacts.
@@ -174,5 +177,5 @@ MIT. See [LICENSE](LICENSE).
174
177
 
175
178
  ---
176
179
 
177
- **Version**: 3.4.6
180
+ **Version**: 3.5.0
178
181
  **Last Updated**: 2026-03-02
package/README.zh.md CHANGED
@@ -26,6 +26,7 @@ SCE 面向希望让 AI Agent 端到端推进交付、同时保持治理可控的
26
26
  | 能力 | SCE 提供什么 | 结果 |
27
27
  | --- | --- | --- |
28
28
  | Scene + Spec 模型 | 场景主会话治理 + Spec 生命周期(需求/设计/任务) | 长周期 AI 上下文稳定 |
29
+ | 自动 intake + Spec 治理 | 目标意图识别、自动绑定/创建 spec、按 scene 组合治理 | 场景需求自动纳管,spec 增长可控 |
29
30
  | Studio 工作流 | `studio plan -> generate -> apply -> verify -> release` | 对话到发布路径结构化 |
30
31
  | 自动闭环交付 | `auto close-loop`、`close-loop-program`、`close-loop-controller` | 无人值守有界收敛 |
31
32
  | 多 Agent 编排 | DAG 调度、重试、429 自适应并行 | 并行执行稳定可控 |
@@ -106,6 +107,7 @@ SCE 默认按“问题域闭环”推进诊断与修复:
106
107
  默认硬规则:
107
108
  - 同一问题指纹失败两轮后,后续尝试必须补充 debug 证据。
108
109
  - 当 spec 绑定时,`studio verify/release` 默认执行 `problem-closure-gate`。
110
+ - `studio plan` 默认执行目标 intake(自动绑定已有 spec 或新建 spec),并自动写入 scene 维度的 spec 治理快照。
109
111
 
110
112
  ---
111
113
 
@@ -128,6 +130,7 @@ SCE 对工具无锁定,可接入 Codex、Claude Code、Cursor、Windsurf、VS
128
130
 
129
131
  ## 重要版本变更
130
132
 
133
+ - `3.5.0`:新增 Studio 目标自动 intake + 场景 spec 组合治理(`sce studio intake`、`sce studio portfolio`),并默认启用 intake 策略基线与治理快照产物,控制场景内 spec 无序增长。
131
134
  - `3.4.6`:新增默认 `problem-closure-gate` + `problem-contract` 基线,并强化问题评估强制维度(`problem_contract`/`ontology_alignment`/`convergence`),提升 verify/release 收敛控制。
132
135
  - `3.4.5`:`git-managed-gate` 在默认 CI 放宽模式下(`CI/GITHUB_ACTIONS` 且非 strict)对工作区变更改为告警,不再误阻断发布。
133
136
  - `3.4.4`:新增 `SCE_GIT_MANAGEMENT_ALLOW_UNTRACKED=1` / `--allow-untracked`;发布工作流在 npm publish 前生成证据资产时可放行未跟踪文件。
@@ -174,5 +177,5 @@ MIT,见 [LICENSE](LICENSE)。
174
177
 
175
178
  ---
176
179
 
177
- **版本**:3.4.6
180
+ **版本**:3.5.0
178
181
  **最后更新**:2026-03-02
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Quick reference for all `sce` commands
4
4
 
5
- **Version**: 3.4.6
5
+ **Version**: 3.5.0
6
6
  **Last Updated**: 2026-03-02
7
7
 
8
8
  ---
@@ -523,6 +523,13 @@ 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
+
529
+ # Analyze intake decision only (no write by default)
530
+ sce studio intake --scene scene.customer-order-inventory --from-chat session-20260226 --goal "optimize checkout retry flow" --json
531
+ # Apply intake and create spec when decision is create_spec
532
+ sce studio intake --scene scene.customer-order-inventory --from-chat session-20260226 --goal "optimize checkout retry flow" --apply --json
526
533
 
527
534
  # Generate patch bundle metadata (scene is inherited from plan)
528
535
  sce studio generate --target 331 --json
@@ -549,15 +556,26 @@ sce studio events --job <job-id> --limit 50 --json
549
556
  # Rollback a job after apply/release
550
557
  sce studio rollback --job <job-id> --reason "manual-check-failed" --json
551
558
 
559
+ # Build scene-organized spec governance portfolio
560
+ sce studio portfolio --json
561
+ sce studio portfolio --scene scene.customer-order-inventory --strict --json
562
+
552
563
  # Enforce authorization for a protected action
553
564
  SCE_STUDIO_REQUIRE_AUTH=1 SCE_STUDIO_AUTH_PASSWORD=top-secret sce studio apply --job <job-id> --auth-password top-secret --json
554
565
  ```
555
566
 
556
567
  Stage guardrails are enforced by default:
557
568
  - `plan` requires `--scene`; SCE binds one active primary session per scene
569
+ - `plan` runs auto intake by default (`.sce/config/studio-intake-policy.json`):
570
+ - detect goal intent (`change_request` vs `analysis_only`)
571
+ - resolve spec via explicit binding / scene latest / related specs / auto-create
572
+ - auto-create spec artifacts when no suitable spec is found and policy requires tracking
558
573
  - `plan --spec <id>` (recommended) ingests `.sce/specs/<spec>/custom/problem-domain-chain.json` into studio job context
559
574
  - when `--spec` is omitted, `plan` auto-resolves the latest matching spec chain by `scene_id` when available
560
575
  - `plan` auto-searches related historical specs by `scene + goal` and writes top candidates into job metadata (`source.related_specs`)
576
+ - `plan` auto-runs scene spec governance snapshot and writes:
577
+ - `.sce/spec-governance/scene-portfolio.latest.json`
578
+ - `.sce/spec-governance/scene-index.json`
561
579
  - successful `release` auto-archives current scene session and auto-opens the next scene cycle session
562
580
  - `generate` requires `plan`
563
581
  - `generate` consumes the plan-stage domain-chain context and writes chain-aware metadata/report (`.sce/reports/studio/generate-<job-id>.json`)
@@ -632,6 +650,24 @@ Default policy file (recommended to commit): `.sce/config/studio-security.json`
632
650
  }
633
651
  ```
634
652
 
653
+ Studio intake policy file (default, recommended to commit): `.sce/config/studio-intake-policy.json`
654
+
655
+ ```json
656
+ {
657
+ "enabled": true,
658
+ "auto_create_spec": true,
659
+ "force_spec_for_studio_plan": true,
660
+ "prefer_existing_scene_spec": true,
661
+ "related_spec_min_score": 45,
662
+ "governance": {
663
+ "auto_run_on_plan": true,
664
+ "max_active_specs_per_scene": 3,
665
+ "stale_days": 14,
666
+ "duplicate_similarity_threshold": 0.66
667
+ }
668
+ }
669
+ ```
670
+
635
671
  ### Capability Matrix Utilities
636
672
 
637
673
  ```bash
@@ -120,6 +120,7 @@ class AdoptionStrategy {
120
120
  'config/spec-domain-policy.json',
121
121
  'config/problem-eval-policy.json',
122
122
  'config/problem-closure-policy.json',
123
+ 'config/studio-intake-policy.json',
123
124
  'specs/SPEC_WORKFLOW_GUIDE.md',
124
125
  'hooks/sync-tasks-on-edit.sce.hook',
125
126
  'hooks/check-spec-on-create.sce.hook',
@@ -169,6 +169,7 @@ class DetectionEngine {
169
169
  'config/spec-domain-policy.json',
170
170
  'config/problem-eval-policy.json',
171
171
  'config/problem-closure-policy.json',
172
+ 'config/studio-intake-policy.json',
172
173
  'README.md',
173
174
  'ultrawork-application-guide.md',
174
175
  'ultrawork-integration-summary.md',
@@ -70,7 +70,8 @@ class FileClassifier {
70
70
  'config/session-governance.json',
71
71
  'config/spec-domain-policy.json',
72
72
  'config/problem-eval-policy.json',
73
- 'config/problem-closure-policy.json'
73
+ 'config/problem-closure-policy.json',
74
+ 'config/studio-intake-policy.json'
74
75
  ];
75
76
 
76
77
  // Generated directory patterns
@@ -290,6 +290,7 @@ class SmartOrchestrator {
290
290
  'config/spec-domain-policy.json',
291
291
  'config/problem-eval-policy.json',
292
292
  'config/problem-closure-policy.json',
293
+ 'config/studio-intake-policy.json',
293
294
  'README.md'
294
295
  ];
295
296
 
@@ -11,6 +11,10 @@ const {
11
11
  const { findRelatedSpecs } = require('../spec/related-specs');
12
12
  const { captureTimelineCheckpoint } = require('../runtime/project-timeline');
13
13
  const { runProblemEvaluation } = require('../problem/problem-evaluator');
14
+ const {
15
+ runStudioAutoIntake,
16
+ runStudioSpecGovernance
17
+ } = require('../studio/spec-intake-governor');
14
18
 
15
19
  const STUDIO_JOB_API_VERSION = 'sce.studio.job/v0.1';
16
20
  const STAGE_ORDER = ['plan', 'generate', 'apply', 'verify', 'release'];
@@ -1343,6 +1347,9 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1343
1347
  const fromChat = normalizeString(options.fromChat);
1344
1348
  const sceneId = normalizeString(options.scene);
1345
1349
  const specId = normalizeString(options.spec);
1350
+ const goal = normalizeString(options.goal);
1351
+ const manualSpecMode = options.manualSpec === true;
1352
+ const skipSpecGovernance = options.specGovernance === false;
1346
1353
 
1347
1354
  if (!fromChat) {
1348
1355
  throw new Error('--from-chat is required');
@@ -1350,16 +1357,18 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1350
1357
  if (!sceneId) {
1351
1358
  throw new Error('--scene is required');
1352
1359
  }
1353
- const domainChainBinding = await resolveDomainChainBinding({
1360
+
1361
+ let domainChainBinding = await resolveDomainChainBinding({
1354
1362
  sceneId,
1355
1363
  specId,
1356
- goal: normalizeString(options.goal)
1364
+ goal
1357
1365
  }, {
1358
1366
  projectPath,
1359
1367
  fileSystem
1360
1368
  });
1361
- const relatedSpecLookup = await findRelatedSpecs({
1362
- query: normalizeString(options.goal),
1369
+
1370
+ let relatedSpecLookup = await findRelatedSpecs({
1371
+ query: goal,
1363
1372
  sceneId,
1364
1373
  limit: 8,
1365
1374
  excludeSpecId: domainChainBinding.spec_id || specId || null
@@ -1367,6 +1376,45 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1367
1376
  projectPath,
1368
1377
  fileSystem
1369
1378
  });
1379
+
1380
+ const intake = await runStudioAutoIntake({
1381
+ scene_id: sceneId,
1382
+ from_chat: fromChat,
1383
+ goal,
1384
+ explicit_spec_id: specId,
1385
+ domain_chain_binding: domainChainBinding,
1386
+ related_specs: relatedSpecLookup,
1387
+ apply: !manualSpecMode,
1388
+ skip: manualSpecMode
1389
+ }, {
1390
+ projectPath,
1391
+ fileSystem
1392
+ });
1393
+
1394
+ const intakeSpecId = normalizeString(intake && intake.selected_spec_id);
1395
+ const effectiveSpecId = intakeSpecId || normalizeString(domainChainBinding.spec_id) || specId || null;
1396
+
1397
+ if (effectiveSpecId && effectiveSpecId !== normalizeString(domainChainBinding.spec_id)) {
1398
+ domainChainBinding = await resolveDomainChainBinding({
1399
+ sceneId,
1400
+ specId: effectiveSpecId,
1401
+ goal
1402
+ }, {
1403
+ projectPath,
1404
+ fileSystem
1405
+ });
1406
+ }
1407
+
1408
+ relatedSpecLookup = await findRelatedSpecs({
1409
+ query: goal,
1410
+ sceneId,
1411
+ limit: 8,
1412
+ excludeSpecId: effectiveSpecId || null
1413
+ }, {
1414
+ projectPath,
1415
+ fileSystem
1416
+ });
1417
+
1370
1418
  const relatedSpecItems = Array.isArray(relatedSpecLookup.related_specs)
1371
1419
  ? relatedSpecLookup.related_specs.map((item) => ({
1372
1420
  spec_id: item.spec_id,
@@ -1387,7 +1435,7 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1387
1435
  domainChainBinding.problem_contract || {},
1388
1436
  {
1389
1437
  scene_id: sceneId,
1390
- goal: normalizeString(options.goal),
1438
+ goal,
1391
1439
  problem_statement: normalizeString(domainChainBinding?.summary?.problem_statement),
1392
1440
  verification_plan: normalizeString(domainChainBinding?.summary?.verification_plan)
1393
1441
  }
@@ -1396,11 +1444,11 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1396
1444
  job_id: jobId,
1397
1445
  scene: {
1398
1446
  id: sceneId,
1399
- spec_id: domainChainBinding.spec_id || specId || null
1447
+ spec_id: effectiveSpecId
1400
1448
  },
1401
1449
  source: {
1402
- goal: normalizeString(options.goal) || null,
1403
- spec_id: domainChainBinding.spec_id || specId || null,
1450
+ goal: goal || null,
1451
+ spec_id: effectiveSpecId,
1404
1452
  problem_contract: problemContract,
1405
1453
  problem_contract_path: domainChainBinding.problem_contract_path || null,
1406
1454
  domain_chain: {
@@ -1415,8 +1463,8 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1415
1463
  };
1416
1464
  const planProblemEvaluation = await enforceProblemEvaluationForStage(planShadowJob, 'plan', {
1417
1465
  scene_id: sceneId,
1418
- spec_id: domainChainBinding.spec_id || specId || null,
1419
- goal: normalizeString(options.goal) || null,
1466
+ spec_id: effectiveSpecId,
1467
+ goal: goal || null,
1420
1468
  problem_contract: problemContract,
1421
1469
  domain_chain: {
1422
1470
  resolved: domainChainBinding.resolved === true,
@@ -1444,16 +1492,35 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1444
1492
  const sessionStore = dependencies.sessionStore || new SessionStore(projectPath);
1445
1493
  const sceneSessionBinding = await sessionStore.beginSceneSession({
1446
1494
  sceneId,
1447
- objective: normalizeString(options.goal) || `Studio scene cycle for ${sceneId}`,
1495
+ objective: goal || `Studio scene cycle for ${sceneId}`,
1448
1496
  tool: normalizeString(options.tool) || 'generic'
1449
1497
  });
1498
+
1499
+ let governanceSnapshot = null;
1500
+ let governanceWarning = '';
1501
+ const autoRunGovernance = !(skipSpecGovernance)
1502
+ && (!intake || !intake.policy || !intake.policy.governance || intake.policy.governance.auto_run_on_plan !== false);
1503
+ if (autoRunGovernance) {
1504
+ try {
1505
+ governanceSnapshot = await runStudioSpecGovernance({
1506
+ apply: true,
1507
+ scene: sceneId
1508
+ }, {
1509
+ projectPath,
1510
+ fileSystem
1511
+ });
1512
+ } catch (error) {
1513
+ governanceWarning = normalizeString(error && error.message);
1514
+ }
1515
+ }
1516
+
1450
1517
  stages.plan = {
1451
1518
  status: 'completed',
1452
1519
  completed_at: now,
1453
1520
  metadata: {
1454
1521
  from_chat: fromChat,
1455
1522
  scene_id: sceneId,
1456
- spec_id: domainChainBinding.spec_id || specId || null,
1523
+ spec_id: effectiveSpecId,
1457
1524
  scene_session_id: sceneSessionBinding.session.session_id,
1458
1525
  scene_cycle: sceneSessionBinding.scene_cycle,
1459
1526
  domain_chain_resolved: domainChainBinding.resolved === true,
@@ -1463,7 +1530,18 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1463
1530
  domain_chain_summary: domainChainBinding.summary || null,
1464
1531
  domain_chain_reason: domainChainBinding.reason || null,
1465
1532
  problem_contract: problemContract,
1533
+ intake: intake ? {
1534
+ enabled: intake.enabled === true,
1535
+ intent_type: intake.intent ? intake.intent.intent_type : null,
1536
+ decision_action: intake.decision ? intake.decision.action : null,
1537
+ decision_reason: intake.decision ? intake.decision.reason : null,
1538
+ selected_spec_id: intake.selected_spec_id || effectiveSpecId || null,
1539
+ created_spec_id: intake.created_spec && intake.created_spec.created ? intake.created_spec.spec_id : null,
1540
+ policy_path: intake.policy_path || null
1541
+ } : null,
1466
1542
  problem_evaluation: summarizeProblemEvaluation(planProblemEvaluation),
1543
+ spec_governance: governanceSnapshot ? governanceSnapshot.summary : null,
1544
+ spec_governance_warning: governanceWarning || null,
1467
1545
  related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
1468
1546
  related_specs_top: relatedSpecItems
1469
1547
  }
@@ -1477,15 +1555,24 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1477
1555
  status: 'planned',
1478
1556
  source: {
1479
1557
  from_chat: fromChat,
1480
- goal: normalizeString(options.goal) || null,
1481
- spec_id: domainChainBinding.spec_id || specId || null,
1558
+ goal: goal || null,
1559
+ spec_id: effectiveSpecId,
1482
1560
  problem_contract: problemContract,
1483
1561
  problem_contract_path: domainChainBinding.problem_contract_path || null,
1562
+ intake: intake ? {
1563
+ enabled: intake.enabled === true,
1564
+ policy_path: intake.policy_path || null,
1565
+ policy_loaded_from: intake.policy_loaded_from || null,
1566
+ intent: intake.intent || null,
1567
+ decision: intake.decision || null,
1568
+ selected_spec_id: intake.selected_spec_id || effectiveSpecId || null,
1569
+ created_spec: intake.created_spec || null
1570
+ } : null,
1484
1571
  domain_chain: {
1485
1572
  resolved: domainChainBinding.resolved === true,
1486
1573
  source: domainChainBinding.source || 'none',
1487
1574
  reason: domainChainBinding.reason || null,
1488
- spec_id: domainChainBinding.spec_id || null,
1575
+ spec_id: effectiveSpecId || domainChainBinding.spec_id || null,
1489
1576
  chain_path: domainChainBinding.chain_path || null,
1490
1577
  candidate_count: Number.isFinite(Number(domainChainBinding.candidate_count))
1491
1578
  ? Number(domainChainBinding.candidate_count)
@@ -1500,11 +1587,20 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1500
1587
  scene_id: relatedSpecLookup.scene_id || null,
1501
1588
  total_candidates: Number(relatedSpecLookup.total_candidates || 0),
1502
1589
  items: relatedSpecItems
1503
- }
1590
+ },
1591
+ spec_governance: governanceSnapshot
1592
+ ? {
1593
+ status: governanceSnapshot.summary ? governanceSnapshot.summary.status : null,
1594
+ alert_count: governanceSnapshot.summary ? Number(governanceSnapshot.summary.alert_count || 0) : 0,
1595
+ report_file: governanceSnapshot.report_file || null,
1596
+ scene_index_file: governanceSnapshot.scene_index_file || null
1597
+ }
1598
+ : null,
1599
+ spec_governance_warning: governanceWarning || null
1504
1600
  },
1505
1601
  scene: {
1506
1602
  id: sceneId,
1507
- spec_id: domainChainBinding.spec_id || specId || null,
1603
+ spec_id: effectiveSpecId,
1508
1604
  related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
1509
1605
  },
1510
1606
  session: {
@@ -1520,6 +1616,12 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1520
1616
  patch_bundle_id: null,
1521
1617
  verify_report: null,
1522
1618
  release_ref: null,
1619
+ spec_portfolio_report: governanceSnapshot && governanceSnapshot.report_file
1620
+ ? governanceSnapshot.report_file
1621
+ : null,
1622
+ spec_scene_index: governanceSnapshot && governanceSnapshot.scene_index_file
1623
+ ? governanceSnapshot.scene_index_file
1624
+ : null,
1523
1625
  problem_eval_reports: {
1524
1626
  plan: normalizeString(planProblemEvaluation.report_file) || null
1525
1627
  }
@@ -1530,7 +1632,7 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1530
1632
  await appendStudioEvent(paths, job, 'stage.plan.completed', {
1531
1633
  from_chat: fromChat,
1532
1634
  scene_id: sceneId,
1533
- spec_id: domainChainBinding.spec_id || specId || null,
1635
+ spec_id: effectiveSpecId,
1534
1636
  scene_session_id: sceneSessionBinding.session.session_id,
1535
1637
  scene_cycle: sceneSessionBinding.scene_cycle,
1536
1638
  target: job.target,
@@ -1539,13 +1641,27 @@ async function runStudioPlanCommand(options = {}, dependencies = {}) {
1539
1641
  domain_chain_spec_id: domainChainBinding.spec_id || null,
1540
1642
  domain_chain_path: domainChainBinding.chain_path || null,
1541
1643
  problem_contract: problemContract,
1644
+ intake_action: intake && intake.decision ? intake.decision.action : null,
1645
+ intake_reason: intake && intake.decision ? intake.decision.reason : null,
1646
+ intake_selected_spec_id: intake ? intake.selected_spec_id || effectiveSpecId || null : effectiveSpecId,
1647
+ intake_created_spec_id: intake && intake.created_spec && intake.created_spec.created
1648
+ ? intake.created_spec.spec_id
1649
+ : null,
1542
1650
  problem_evaluation: summarizeProblemEvaluation(planProblemEvaluation),
1651
+ spec_governance: governanceSnapshot ? governanceSnapshot.summary : null,
1652
+ spec_governance_warning: governanceWarning || null,
1543
1653
  related_specs_total: Number(relatedSpecLookup.total_candidates || 0),
1544
1654
  related_spec_ids: relatedSpecItems.map((item) => item.spec_id)
1545
1655
  }, fileSystem);
1546
1656
  await writeLatestJob(paths, jobId, fileSystem);
1547
1657
 
1548
1658
  const payload = buildCommandPayload('studio-plan', job);
1659
+ payload.scene = {
1660
+ id: sceneId,
1661
+ spec_id: effectiveSpecId
1662
+ };
1663
+ payload.intake = job.source && job.source.intake ? job.source.intake : null;
1664
+ payload.spec_governance = governanceSnapshot ? governanceSnapshot.summary : null;
1549
1665
  printStudioPayload(payload, options);
1550
1666
  return payload;
1551
1667
  }
@@ -2178,6 +2294,116 @@ async function runStudioEventsCommand(options = {}, dependencies = {}) {
2178
2294
  return payload;
2179
2295
  }
2180
2296
 
2297
+ function printStudioIntakePayload(payload, options = {}) {
2298
+ if (options.json) {
2299
+ console.log(JSON.stringify(payload, null, 2));
2300
+ return;
2301
+ }
2302
+
2303
+ console.log(chalk.blue('Studio intake'));
2304
+ console.log(` Scene: ${payload.scene_id || 'n/a'}`);
2305
+ console.log(` Goal: ${payload.goal || '(empty)'}`);
2306
+ console.log(` Intent: ${payload.intent && payload.intent.intent_type ? payload.intent.intent_type : 'unknown'}`);
2307
+ console.log(` Decision: ${payload.decision && payload.decision.action ? payload.decision.action : 'none'}`);
2308
+ console.log(` Spec: ${payload.selected_spec_id || 'n/a'}`);
2309
+ }
2310
+
2311
+ async function runStudioIntakeCommand(options = {}, dependencies = {}) {
2312
+ const projectPath = dependencies.projectPath || process.cwd();
2313
+ const fileSystem = dependencies.fileSystem || fs;
2314
+ const sceneId = normalizeString(options.scene);
2315
+ const fromChat = normalizeString(options.fromChat);
2316
+ const goal = normalizeString(options.goal);
2317
+ const specId = normalizeString(options.spec);
2318
+
2319
+ if (!sceneId) {
2320
+ throw new Error('--scene is required');
2321
+ }
2322
+ if (!fromChat) {
2323
+ throw new Error('--from-chat is required');
2324
+ }
2325
+
2326
+ const domainChainBinding = await resolveDomainChainBinding({
2327
+ sceneId,
2328
+ specId,
2329
+ goal
2330
+ }, {
2331
+ projectPath,
2332
+ fileSystem
2333
+ });
2334
+
2335
+ const relatedSpecLookup = await findRelatedSpecs({
2336
+ query: goal,
2337
+ sceneId,
2338
+ limit: 8,
2339
+ excludeSpecId: domainChainBinding.spec_id || specId || null
2340
+ }, {
2341
+ projectPath,
2342
+ fileSystem
2343
+ });
2344
+
2345
+ const intake = await runStudioAutoIntake({
2346
+ scene_id: sceneId,
2347
+ from_chat: fromChat,
2348
+ goal,
2349
+ explicit_spec_id: specId,
2350
+ domain_chain_binding: domainChainBinding,
2351
+ related_specs: relatedSpecLookup,
2352
+ apply: options.apply === true,
2353
+ skip: options.manualSpec === true
2354
+ }, {
2355
+ projectPath,
2356
+ fileSystem
2357
+ });
2358
+
2359
+ const payload = {
2360
+ ...intake,
2361
+ domain_chain_source: domainChainBinding.source || 'none',
2362
+ domain_chain_spec_id: domainChainBinding.spec_id || null,
2363
+ related_specs_total: Number(relatedSpecLookup.total_candidates || 0)
2364
+ };
2365
+ printStudioIntakePayload(payload, options);
2366
+ return payload;
2367
+ }
2368
+
2369
+ function printStudioPortfolioPayload(payload, options = {}) {
2370
+ if (options.json) {
2371
+ console.log(JSON.stringify(payload, null, 2));
2372
+ return;
2373
+ }
2374
+ const summary = payload.summary || {};
2375
+ console.log(chalk.blue('Studio portfolio governance'));
2376
+ console.log(` Status: ${summary.status || 'unknown'}`);
2377
+ console.log(` Scenes: ${summary.scene_count || 0}`);
2378
+ console.log(` Specs: ${summary.total_specs || 0}`);
2379
+ console.log(` Active: ${summary.active_specs || 0}`);
2380
+ console.log(` Completed: ${summary.completed_specs || 0}`);
2381
+ console.log(` Stale: ${summary.stale_specs || 0}`);
2382
+ console.log(` Duplicate pairs: ${summary.duplicate_pairs || 0}`);
2383
+ console.log(` Overflow scenes: ${summary.overflow_scenes || 0}`);
2384
+ }
2385
+
2386
+ async function runStudioPortfolioCommand(options = {}, dependencies = {}) {
2387
+ const projectPath = dependencies.projectPath || process.cwd();
2388
+ const fileSystem = dependencies.fileSystem || fs;
2389
+ const payload = await runStudioSpecGovernance({
2390
+ scene: normalizeString(options.scene),
2391
+ apply: options.apply !== false
2392
+ }, {
2393
+ projectPath,
2394
+ fileSystem
2395
+ });
2396
+
2397
+ if (options.strict && payload.summary && Number(payload.summary.alert_count || 0) > 0) {
2398
+ throw new Error(
2399
+ `studio portfolio governance has alerts: ${payload.summary.alert_count} (duplicate/stale/overflow)`
2400
+ );
2401
+ }
2402
+
2403
+ printStudioPortfolioPayload(payload, options);
2404
+ return payload;
2405
+ }
2406
+
2181
2407
  async function runStudioCommand(handler, options, stageName = '') {
2182
2408
  try {
2183
2409
  const stage = normalizeString(stageName) || 'unknown';
@@ -2221,11 +2447,34 @@ function registerStudioCommands(program) {
2221
2447
  .requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
2222
2448
  .option('--spec <spec-id>', 'Optional spec binding for domain-chain context ingestion')
2223
2449
  .option('--goal <goal>', 'Optional goal summary')
2450
+ .option('--manual-spec', 'Disable auto intake and only use explicit --spec or existing scene binding')
2224
2451
  .option('--target <target>', 'Target integration profile', 'default')
2452
+ .option('--no-spec-governance', 'Skip auto portfolio governance snapshot on plan stage')
2225
2453
  .option('--job <job-id>', 'Reuse an explicit studio job id')
2226
2454
  .option('--json', 'Print machine-readable JSON output')
2227
2455
  .action(async (options) => runStudioCommand(runStudioPlanCommand, options, 'plan'));
2228
2456
 
2457
+ studio
2458
+ .command('intake')
2459
+ .description('Analyze chat goal and auto-resolve spec binding/create decision')
2460
+ .requiredOption('--scene <scene-id>', 'Scene identifier')
2461
+ .requiredOption('--from-chat <session>', 'Chat session identifier or transcript reference')
2462
+ .option('--spec <spec-id>', 'Optional explicit spec id')
2463
+ .option('--goal <goal>', 'Goal text used for intent classification')
2464
+ .option('--apply', 'Create spec when decision is create_spec')
2465
+ .option('--manual-spec', 'Disable auto intake and keep explicit/manual binding only')
2466
+ .option('--json', 'Print machine-readable JSON output')
2467
+ .action(async (options) => runStudioCommand(runStudioIntakeCommand, options, 'intake'));
2468
+
2469
+ studio
2470
+ .command('portfolio')
2471
+ .description('Build scene-organized spec governance portfolio')
2472
+ .option('--scene <scene-id>', 'Optional scene filter')
2473
+ .option('--no-apply', 'Do not write portfolio/index artifacts to .sce/spec-governance/')
2474
+ .option('--strict', 'Fail when governance alerts are detected')
2475
+ .option('--json', 'Print machine-readable JSON output')
2476
+ .action(async (options) => runStudioCommand(runStudioPortfolioCommand, options, 'portfolio'));
2477
+
2229
2478
  studio
2230
2479
  .command('generate')
2231
2480
  .description('Generate patch bundle metadata for a planned studio job (scene inherited from plan)')
@@ -2310,12 +2559,14 @@ module.exports = {
2310
2559
  resolveNextAction,
2311
2560
  buildProgress,
2312
2561
  runStudioPlanCommand,
2562
+ runStudioIntakeCommand,
2313
2563
  runStudioGenerateCommand,
2314
2564
  runStudioApplyCommand,
2315
2565
  runStudioVerifyCommand,
2316
2566
  runStudioReleaseCommand,
2317
2567
  runStudioRollbackCommand,
2318
2568
  runStudioEventsCommand,
2569
+ runStudioPortfolioCommand,
2319
2570
  runStudioResumeCommand,
2320
2571
  registerStudioCommands
2321
2572
  };