scene-capability-engine 3.6.60 → 3.6.61

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,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.6.61] - 2026-03-20
11
+
12
+ ### Added
13
+ - Added `sce project candidate inspect --root <path> --json` as the canonical single-root inspection contract for workspace-backed roots, local `.sce` candidates, ordinary directory candidates, and invalid roots.
14
+ - Added `sce project onboarding import --root <path> --json` as the canonical root-based onboarding entry, reusing the ordered `register/attach/hydrate/activate/scaffold` step envelope instead of inventing a second import receipt.
15
+
16
+ ### Changed
17
+ - MagicBall-facing multi-project docs, command reference, CLI examples, frontend mapping, and release-time project-contract audit now include `project candidate inspect` and `project onboarding import` alongside the existing portfolio/target/supervision contracts.
18
+ - Root-based onboarding now suppresses internal adoption progress logs in `--json` mode so adapters receive clean machine-readable envelopes.
19
+
10
20
  ## [3.6.60] - 2026-03-19
11
21
 
12
22
  ### Changed
package/README.md CHANGED
@@ -174,7 +174,7 @@ MagicBall-specific integration surfaces now also include:
174
174
  - `sce app registry status|configure|sync*`
175
175
  - `sce app runtime show|releases|install|activate|uninstall`
176
176
  - `sce app engineering preview|ownership|open|import|show|attach|hydrate|scaffold|activate`
177
- - `sce project portfolio show|target resolve|supervision show`
177
+ - `sce project portfolio show|candidate inspect|onboarding import|target resolve|supervision show`
178
178
  - `sce mode application|ontology|engineering home`
179
179
  - `sce pm requirement|tracking|planning|change|issue ... --json`
180
180
  - `sce ontology er|br|dl ... --json`
@@ -230,5 +230,5 @@ MIT. See [LICENSE](LICENSE).
230
230
 
231
231
  ---
232
232
 
233
- **Version**: 3.6.60
234
- **Last Updated**: 2026-03-19
233
+ **Version**: 3.6.61
234
+ **Last Updated**: 2026-03-20
package/README.zh.md CHANGED
@@ -179,7 +179,7 @@ SCE 默认是强治理的。
179
179
  - `sce app registry status|configure|sync*`
180
180
  - `sce app runtime show|releases|install|activate|uninstall`
181
181
  - `sce app engineering preview|ownership|open|import|show|attach|hydrate|scaffold|activate`
182
- - `sce project portfolio show|target resolve|supervision show`
182
+ - `sce project portfolio show|candidate inspect|onboarding import|target resolve|supervision show`
183
183
  - `sce mode application|ontology|engineering home`
184
184
  - `sce pm requirement|tracking|planning|change|issue ... --json`
185
185
  - `sce ontology er|br|dl ... --json`
@@ -235,5 +235,5 @@ MIT,见 [LICENSE](LICENSE)。
235
235
 
236
236
  ---
237
237
 
238
- **版本**:3.6.60
239
- **最后更新**:2026-03-19
238
+ **版本**:3.6.61
239
+ **最后更新**:2026-03-20
@@ -2660,7 +2660,7 @@ Overall Health: 2 healthy, 1 unhealthy
2660
2660
 
2661
2661
  ---
2662
2662
 
2663
- ## Project Portfolio And Supervision
2663
+ ## Project Portfolio, Candidate Inspection, And Onboarding
2664
2664
 
2665
2665
  #### `sce project portfolio show`
2666
2666
 
@@ -2680,6 +2680,42 @@ sce project portfolio show [options]
2680
2680
  - Marks inaccessible or partial projects explicitly instead of dropping them
2681
2681
  - Reuses project-local session governance signals to summarize active sessions and last activity
2682
2682
 
2683
+ #### `sce project candidate inspect`
2684
+
2685
+ Inspect one local root through the canonical engine-owned project-candidate contract.
2686
+
2687
+ **Usage:**
2688
+ ```bash
2689
+ sce project candidate inspect --root <path> [options]
2690
+ ```
2691
+
2692
+ **Options:**
2693
+ - `--root <path>` - Local root directory to inspect
2694
+ - `--json` - Output the canonical `LocalProjectCandidateInspection`
2695
+
2696
+ **Behavior:**
2697
+ - Distinguishes `workspace-backed`, `local-sce-candidate`, `directory-candidate`, and `invalid`
2698
+ - Reuses deterministic project/workspace identity when the root already maps to a registered workspace
2699
+ - Returns canonical readiness, availability, and reason codes without adapter-side path heuristics
2700
+
2701
+ #### `sce project onboarding import`
2702
+
2703
+ Import one local root into the canonical project portfolio using root-based onboarding.
2704
+
2705
+ **Usage:**
2706
+ ```bash
2707
+ sce project onboarding import --root <path> [options]
2708
+ ```
2709
+
2710
+ **Options:**
2711
+ - `--root <path>` - Local root directory to import
2712
+ - `--json` - Output the canonical `ProjectOnboardingImportResult`
2713
+
2714
+ **Behavior:**
2715
+ - Accepts a local root as the primary onboarding target without app-bundle-first indirection
2716
+ - Reuses the canonical ordered step envelope (`register`, `attach`, `hydrate`, `activate`, `scaffold`)
2717
+ - Registers onboarded roots into the workspace-backed portfolio without inventing a second registry
2718
+
2683
2719
  #### `sce project target resolve`
2684
2720
 
2685
2721
  Resolve one target project for cross-project flows without mutating active workspace selection.
@@ -68,6 +68,20 @@ Done when:
68
68
  - drillback panels use `items[]` directly
69
69
  - frontend does not present `cursor` as a raw event-stream offset
70
70
 
71
+ ### Task 0.5
72
+ Preflight user-picked local roots with the canonical candidate contract, then onboard by root when needed.
73
+
74
+ Commands:
75
+ ```bash
76
+ sce project candidate inspect --root "<path>" --json
77
+ sce project onboarding import --root "<path>" --json
78
+ ```
79
+
80
+ Done when:
81
+ - frontend does not infer project validity from ad hoc `.sce` path heuristics alone
82
+ - existing local `.sce` roots are imported without rebuilding them blindly
83
+ - ordinary directories can be onboarded without pretending to be app-library items first
84
+
71
85
  ## Phase 1: App Entry And Mode Switching
72
86
 
73
87
  ### Task 1.1
@@ -24,6 +24,8 @@ Assume:
24
24
  ### 1.0 Read multi-project portfolio baseline
25
25
  ```bash
26
26
  sce project portfolio show --json
27
+ sce project candidate inspect --root "C:/workspace/customer-order-demo" --json
28
+ sce project onboarding import --root "C:/workspace/customer-order-demo" --json
27
29
  sce project target resolve --request "continue customer-order-demo" --json
28
30
  sce project supervision show --project workspace:customer-order-demo --json
29
31
  ```
@@ -65,6 +65,8 @@ Recommended shape:
65
65
  ```ts
66
66
  interface ProjectWorkspaceShellState {
67
67
  projectPortfolio: Record<string, unknown> | null
68
+ projectCandidate: Record<string, unknown> | null
69
+ projectOnboarding: Record<string, unknown> | null
68
70
  currentProjectId: string | null
69
71
  projectTarget: Record<string, unknown> | null
70
72
  projectSupervision: Record<string, unknown> | null
@@ -73,11 +75,14 @@ interface ProjectWorkspaceShellState {
73
75
 
74
76
  Owned commands:
75
77
  - `sce project portfolio show --json`
78
+ - `sce project candidate inspect --root <path> --json`
79
+ - `sce project onboarding import --root <path> --json`
76
80
  - `sce project target resolve --request <text> --current-project <project-id> --json`
77
81
  - `sce project supervision show --project <project-id> --json`
78
82
 
79
83
  Rule:
80
84
  - multi-project shell must consume engine-owned project truth directly
85
+ - manual local-root import must preflight with `candidate inspect` before any onboarding mutation
81
86
  - target resolution must be treated as preflight and must not mutate active workspace selection implicitly
82
87
 
83
88
  ### 2.2 Ontology Page State
@@ -29,7 +29,7 @@ SCE changes completed and now available for MagicBall:
29
29
  - `app registry status/configure/sync*`
30
30
  - `app runtime show/releases/install/activate/uninstall`
31
31
  - `app engineering preview/ownership/open/import/show/attach/hydrate/scaffold/activate`
32
- - `project portfolio show/target resolve/supervision show`
32
+ - `project portfolio show/candidate inspect/onboarding import/target resolve/supervision show`
33
33
  - `pm requirement/tracking/planning/change/issue` data plane
34
34
  - `ontology er/br/dl` + `ontology triad summary`
35
35
  - `ontology seed list/show/apply`
@@ -41,7 +41,7 @@ SCE changes completed and now available for MagicBall:
41
41
  2. consume `device current`, `app collection list/show/apply`, `scene workspace list/show/apply`, and `app install-state list` as the local device/install baseline
42
42
  3. consume `pm`, `ontology`, and `assurance` table payloads
43
43
  4. wire runtime install/activate/uninstall and engineering attach/hydrate/scaffold/activate actions
44
- 5. treat `project portfolio / target resolve / supervision` as the default multi-project shell truth
44
+ 5. treat `project portfolio / candidate inspect / onboarding import / target resolve / supervision` as the default multi-project shell truth
45
45
  6. use demo app: `customer-order-demo`
46
46
 
47
47
  ### Related SCE docs
@@ -6,6 +6,8 @@ Define the stable phase-1 SCE contract for MagicBall multi-project surfaces.
6
6
 
7
7
  This document covers:
8
8
  - project roster projection
9
+ - root candidate inspection
10
+ - root-based onboarding import
9
11
  - target-project preflight resolution
10
12
  - project-scoped supervision snapshot
11
13
 
@@ -22,6 +24,7 @@ Use together with:
22
24
 
23
25
  Phase-1 is read-first and engine-owned:
24
26
  - project visibility comes from registered workspaces plus the current unregistered `.sce` project when applicable
27
+ - local root inspection and onboarding reuse engine-owned identity and reason codes
25
28
  - project identity is deterministic and stable for registered workspaces
26
29
  - target resolution is preflight-only and does not mutate active workspace selection
27
30
  - supervision is a project-scoped snapshot, not a raw cross-project event stream
@@ -36,6 +39,8 @@ Phase-1 does not introduce:
36
39
 
37
40
  ```bash
38
41
  sce project portfolio show --json
42
+ sce project candidate inspect --root <path> --json
43
+ sce project onboarding import --root <path> --json
39
44
  sce project target resolve --json
40
45
  sce project supervision show --project <project-id> --json
41
46
  ```
@@ -105,6 +110,55 @@ interface ProjectPortfolioRecord {
105
110
 
106
111
  ## 2. Target Resolution
107
112
 
113
+ ## 1.5 Root Candidate Inspection And Onboarding
114
+
115
+ ### Commands
116
+
117
+ ```bash
118
+ sce project candidate inspect --root <path> --json
119
+ sce project onboarding import --root <path> --json
120
+ ```
121
+
122
+ ### Contract shape
123
+
124
+ ```ts
125
+ interface LocalProjectCandidateInspection {
126
+ inspectedAt: string
127
+ rootDir: string
128
+ kind: 'workspace-backed' | 'local-sce-candidate' | 'directory-candidate' | 'invalid'
129
+ projectId?: string
130
+ workspaceId?: string
131
+ projectName?: string
132
+ readiness: 'ready' | 'partial' | 'pending' | 'blocked' | 'unknown'
133
+ availability: 'accessible' | 'inaccessible' | 'degraded'
134
+ localCandidate: boolean
135
+ reasonCodes: string[]
136
+ }
137
+
138
+ interface ProjectOnboardingImportResult {
139
+ mode: 'import'
140
+ generated_at: string
141
+ success: boolean
142
+ preview: LocalProjectCandidateInspection
143
+ steps: Array<{
144
+ key: 'register' | 'attach' | 'hydrate' | 'activate' | 'scaffold'
145
+ status: 'done' | 'skipped' | 'pending' | 'failed'
146
+ reasonCode?: string
147
+ detail?: string
148
+ }>
149
+ }
150
+ ```
151
+
152
+ ### MagicBall rules
153
+
154
+ - Adapter-owned filesystem scanning is allowed, but every chosen root must be normalized through `candidate inspect`.
155
+ - Use `project onboarding import` when the user picks a local root directly; do not fake an app-library item just to enter onboarding.
156
+ - If `kind=workspace-backed`, reuse returned `projectId/workspaceId` directly and avoid synthesizing a second registry identity.
157
+ - If `kind=local-sce-candidate`, present it as a partial local project until onboarding import registers it.
158
+ - Render `reasonCodes` directly in CLI/IDE receipts; do not replace them with frontend-only heuristics.
159
+
160
+ ## 2. Target Resolution
161
+
108
162
  ### Command
109
163
 
110
164
  ```bash
@@ -201,10 +255,12 @@ interface ProjectSupervisionItem {
201
255
  ## 4. Recommended Multi-Project Frontend Flow
202
256
 
203
257
  1. Load `sce project portfolio show --json` when entering the multi-project shell.
204
- 2. Store `activeProjectId` and render a project switcher from `projects[]`.
205
- 3. When the user enters a cross-project free-text request, preflight with `sce project target resolve --json`.
206
- 4. After project selection or successful resolution, load `sce project supervision show --project <project-id> --json`.
207
- 5. Keep per-project tabs and page layout frontend-owned, but keep project truth engine-owned.
258
+ 2. When the user selects a local root manually, preflight it with `sce project candidate inspect --root <path> --json`.
259
+ 3. If the root is not yet portfolio-backed, import it through `sce project onboarding import --root <path> --json`.
260
+ 4. Store `activeProjectId` and render a project switcher from `projects[]`.
261
+ 5. When the user enters a cross-project free-text request, preflight with `sce project target resolve --json`.
262
+ 6. After project selection or successful resolution, load `sce project supervision show --project <project-id> --json`.
263
+ 7. Keep per-project tabs and page layout frontend-owned, but keep project truth engine-owned.
208
264
 
209
265
  ## 5. Minimal Acceptance Criteria For MagicBall
210
266
 
@@ -142,6 +142,8 @@ Use `docs/magicball-cli-invocation-examples.md` for copy-ready commands.
142
142
 
143
143
  ### Multi-project control
144
144
  - `sce project portfolio show`
145
+ - `sce project candidate inspect`
146
+ - `sce project onboarding import`
145
147
  - `sce project target resolve`
146
148
  - `sce project supervision show`
147
149
 
@@ -136,11 +136,13 @@ MagicBall can treat the current SCE integration as baseline-ready when:
136
136
 
137
137
  ### Must render
138
138
  - project switcher from `project portfolio show`
139
+ - local-root candidate receipt from `project candidate inspect`
139
140
  - current project marker
140
141
  - project health summary from `project supervision show`
141
142
  - explicit degraded / inaccessible project states
142
143
 
143
144
  ### Must behave correctly
145
+ - manual root import preflights through `project candidate inspect` before `project onboarding import`
144
146
  - cross-project free-text routing preflights through `project target resolve`
145
147
  - ambiguous resolution does not auto-select silently
146
148
  - target resolution does not implicitly switch active workspace selection
@@ -94,7 +94,7 @@ rg -n "github.com/scene-capability-engine/sce" README.md README.zh.md docs START
94
94
  Additional expectation:
95
95
 
96
96
  - `audit:magicball-engineering-contract` passes so active MagicBall entry docs stay aligned with `scene delivery`, `engineering preview/ownership`, and `open/import/scaffold` contracts.
97
- - `audit:magicball-project-contract` passes so active MagicBall entry docs stay aligned with `project portfolio`, `project target resolve`, and `project supervision show` contracts.
97
+ - `audit:magicball-project-contract` passes so active MagicBall entry docs stay aligned with `project portfolio`, `project candidate inspect`, `project onboarding import`, `project target resolve`, and `project supervision show` contracts.
98
98
 
99
99
  ---
100
100
 
@@ -9,6 +9,7 @@ This directory stores release-facing documents:
9
9
  ## Archived Versions
10
10
 
11
11
  - [Release checklist](../release-checklist.md)
12
+ - [v3.6.61 release notes](./v3.6.61.md)
12
13
  - [v3.6.60 release notes](./v3.6.60.md)
13
14
  - [v3.6.59 release notes](./v3.6.59.md)
14
15
  - [v3.6.58 release notes](./v3.6.58.md)
@@ -0,0 +1,21 @@
1
+ # v3.6.61 Release Notes
2
+
3
+ Release date: 2026-03-20
4
+
5
+ ## Highlights
6
+
7
+ - Added canonical local-root inspection through `sce project candidate inspect --root <path> --json`, so adapters can classify one chosen directory without inventing their own `.sce` heuristics.
8
+ - Added canonical root-based onboarding through `sce project onboarding import --root <path> --json`, reusing the shared onboarding step envelope and registering imported roots into the project portfolio without a second registry.
9
+ - Extended MagicBall-facing contract docs and release-time doc audit to keep `candidate inspect` and `onboarding import` aligned with `project portfolio / target resolve / supervision`.
10
+
11
+ ## Validation
12
+
13
+ - `npx jest tests/unit/commands/project.test.js --runInBand`
14
+ - `npx jest tests/unit/scripts/magicball-project-contract-audit.test.js --runInBand`
15
+ - `npx jest tests/integration/project-cli.integration.test.js --runInBand`
16
+ - `node scripts/magicball-project-contract-audit.js --fail-on-violation --json`
17
+
18
+ ## Release Notes
19
+
20
+ - Use `v3.6.61` when you need the upstream `136-00` local-project candidate inspection and root-based onboarding contract in the published SCE package.
21
+ - This release stays within the existing engine/adapter boundary: adapters still own scan policy, while SCE now owns the canonical semantics for inspecting one root and importing it by root.
@@ -79,7 +79,7 @@ rg -n "github.com/scene-capability-engine/sce" README.md README.zh.md docs START
79
79
  额外预期:
80
80
 
81
81
  - `audit:magicball-engineering-contract` 通过,确保活跃的 MagicBall 入口文档与 `scene delivery`、`engineering preview/ownership`、`open/import/scaffold` 契约保持一致。
82
- - `audit:magicball-project-contract` 通过,确保活跃的 MagicBall 入口文档与 `project portfolio`、`project target resolve`、`project supervision show` 契约保持一致。
82
+ - `audit:magicball-project-contract` 通过,确保活跃的 MagicBall 入口文档与 `project portfolio`、`project candidate inspect`、`project onboarding import`、`project target resolve`、`project supervision show` 契约保持一致。
83
83
 
84
84
  ---
85
85
 
@@ -9,6 +9,7 @@
9
9
  ## 历史版本归档
10
10
 
11
11
  - [发布检查清单](../release-checklist.md)
12
+ - [v3.6.61 发布说明](./v3.6.61.md)
12
13
  - [v3.6.60 发布说明](./v3.6.60.md)
13
14
  - [v3.6.59 发布说明](./v3.6.59.md)
14
15
  - [v3.6.58 发布说明](./v3.6.58.md)
@@ -0,0 +1,21 @@
1
+ # v3.6.61 发布说明
2
+
3
+ 发布日期:2026-03-20
4
+
5
+ ## 重点变化
6
+
7
+ - 新增 `sce project candidate inspect --root <path> --json`,把“单个本地目录候选项目检查”正式上收到 engine 契约层,适配器不再需要自造 `.sce` 目录启发式。
8
+ - 新增 `sce project onboarding import --root <path> --json`,把“按根目录导入/onboarding”正式收敛到 canonical step envelope,并把导入结果纳入项目 portfolio,而不是另造第二套 registry。
9
+ - 同步扩展了 MagicBall 多项目合同文档与发布时项目合同审计,确保 `candidate inspect / onboarding import` 与既有 `project portfolio / target resolve / supervision` 保持一致。
10
+
11
+ ## 验证
12
+
13
+ - `npx jest tests/unit/commands/project.test.js --runInBand`
14
+ - `npx jest tests/unit/scripts/magicball-project-contract-audit.test.js --runInBand`
15
+ - `npx jest tests/integration/project-cli.integration.test.js --runInBand`
16
+ - `node scripts/magicball-project-contract-audit.js --fail-on-violation --json`
17
+
18
+ ## 发布说明
19
+
20
+ - 需要上游 `136-00` 本地项目候选检查与按根目录 onboarding 契约时,请使用 `v3.6.61`。
21
+ - 该版本继续保持 engine / adapter 边界:扫描策略仍归适配器,SCE 负责“检查一个 root”和“按 root 导入”的正式语义。
@@ -1,5 +1,7 @@
1
1
  const chalk = require('chalk');
2
+ const { inspectProjectCandidate } = require('../project/candidate-inspection-service');
2
3
  const { buildProjectPortfolioProjection } = require('../project/portfolio-projection-service');
4
+ const { runProjectRootOnboardingImport } = require('../project/root-onboarding-service');
3
5
  const { buildProjectSupervisionProjection } = require('../project/supervision-projection-service');
4
6
  const { resolveProjectTarget } = require('../project/target-resolution-service');
5
7
 
@@ -41,6 +43,32 @@ async function runProjectSupervisionShowCommand(options = {}, dependencies = {})
41
43
  return payload;
42
44
  }
43
45
 
46
+ async function runProjectCandidateInspectCommand(options = {}, dependencies = {}) {
47
+ const payload = await inspectProjectCandidate(options, dependencies);
48
+ if (options.json) {
49
+ console.log(JSON.stringify(payload, null, 2));
50
+ } else {
51
+ console.log(chalk.blue('Project Candidate Inspection'));
52
+ console.log(` Root: ${payload.rootDir}`);
53
+ console.log(` Kind: ${payload.kind}`);
54
+ console.log(` Readiness: ${payload.readiness}`);
55
+ }
56
+ return payload;
57
+ }
58
+
59
+ async function runProjectOnboardingImportCommand(options = {}, dependencies = {}) {
60
+ const payload = await runProjectRootOnboardingImport(options, dependencies);
61
+ if (options.json) {
62
+ console.log(JSON.stringify(payload, null, 2));
63
+ } else {
64
+ console.log(chalk.blue('Project Onboarding Import'));
65
+ console.log(` Root: ${payload.preview ? payload.preview.rootDir : options.root}`);
66
+ console.log(` Success: ${payload.success ? 'yes' : 'no'}`);
67
+ console.log(` Workspace: ${payload.result && payload.result.workspaceId ? payload.result.workspaceId : 'none'}`);
68
+ }
69
+ return payload;
70
+ }
71
+
44
72
  function safeRun(handler, options = {}, context = 'project command') {
45
73
  Promise.resolve(handler(options))
46
74
  .catch((error) => {
@@ -84,6 +112,28 @@ function registerProjectCommands(program) {
84
112
  .option('--json', 'Print machine-readable JSON output')
85
113
  .action((options) => safeRun(runProjectTargetResolveCommand, options, 'project target resolve'));
86
114
 
115
+ const candidate = project
116
+ .command('candidate')
117
+ .description('Inspect one local directory as a canonical project candidate');
118
+
119
+ candidate
120
+ .command('inspect')
121
+ .description('Inspect one local root without inventing adapter-side heuristics')
122
+ .requiredOption('--root <path>', 'Local root directory to inspect')
123
+ .option('--json', 'Print machine-readable JSON output')
124
+ .action((options) => safeRun(runProjectCandidateInspectCommand, options, 'project candidate inspect'));
125
+
126
+ const onboarding = project
127
+ .command('onboarding')
128
+ .description('Import one local directory into the canonical project portfolio');
129
+
130
+ onboarding
131
+ .command('import')
132
+ .description('Run root-based onboarding without app-bundle-first indirection')
133
+ .requiredOption('--root <path>', 'Local root directory to import')
134
+ .option('--json', 'Print machine-readable JSON output')
135
+ .action((options) => safeRun(runProjectOnboardingImportCommand, options, 'project onboarding import'));
136
+
87
137
  const supervision = project
88
138
  .command('supervision')
89
139
  .description('Inspect project-scoped supervision projection');
@@ -98,6 +148,8 @@ function registerProjectCommands(program) {
98
148
  }
99
149
 
100
150
  module.exports = {
151
+ runProjectCandidateInspectCommand,
152
+ runProjectOnboardingImportCommand,
101
153
  runProjectPortfolioShowCommand,
102
154
  runProjectTargetResolveCommand,
103
155
  runProjectSupervisionShowCommand,
@@ -0,0 +1,216 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const WorkspaceStateManager = require('../workspace/multi/workspace-state-manager');
4
+ const {
5
+ buildLocalProjectId,
6
+ buildWorkspaceProjectId
7
+ } = require('./portfolio-projection-service');
8
+
9
+ const PROJECT_CANDIDATE_REASON_CODES = {
10
+ ROOT_ACCESSIBLE: 'project.root.accessible',
11
+ ROOT_INACCESSIBLE: 'project.root.inaccessible',
12
+ ROOT_INVALID_TYPE: 'project.root.invalid_type',
13
+ WORKSPACE_REGISTERED: 'project.workspace.registered',
14
+ SCE_PRESENT: 'project.sce.present',
15
+ ROOT_NOT_INITIALIZED: 'project.root.not_initialized',
16
+ INVALID_PROJECT_METADATA: 'project.metadata.invalid',
17
+ UNREGISTERED_PROJECT: 'project.sce.unregistered'
18
+ };
19
+
20
+ function normalizeString(value) {
21
+ if (typeof value !== 'string') {
22
+ return '';
23
+ }
24
+ return value.trim();
25
+ }
26
+
27
+ function normalizePath(value) {
28
+ return normalizeString(value).replace(/\\/g, '/');
29
+ }
30
+
31
+ function buildProjectName(rootDir) {
32
+ return path.basename(rootDir) || 'project';
33
+ }
34
+
35
+ async function resolveExactWorkspaceByRoot(rootDir, stateManager) {
36
+ const workspaces = await stateManager.listWorkspaces();
37
+ const normalizedRoot = normalizePath(rootDir);
38
+ return workspaces.find((workspace) => normalizePath(workspace && workspace.path) === normalizedRoot) || null;
39
+ }
40
+
41
+ async function inspectSceMetadata(rootDir, fileSystem = fs) {
42
+ const sceRoot = path.join(rootDir, '.sce');
43
+ if (!await fileSystem.pathExists(sceRoot)) {
44
+ return {
45
+ scePresent: false,
46
+ metadataValid: true,
47
+ metadataReasonCodes: []
48
+ };
49
+ }
50
+
51
+ try {
52
+ const sceStats = await fileSystem.stat(sceRoot);
53
+ if (!sceStats.isDirectory()) {
54
+ return {
55
+ scePresent: false,
56
+ metadataValid: false,
57
+ metadataReasonCodes: [PROJECT_CANDIDATE_REASON_CODES.ROOT_INVALID_TYPE]
58
+ };
59
+ }
60
+ } catch (_error) {
61
+ return {
62
+ scePresent: false,
63
+ metadataValid: false,
64
+ metadataReasonCodes: [PROJECT_CANDIDATE_REASON_CODES.ROOT_INACCESSIBLE]
65
+ };
66
+ }
67
+
68
+ const versionPath = path.join(sceRoot, 'version.json');
69
+ if (!await fileSystem.pathExists(versionPath)) {
70
+ return {
71
+ scePresent: true,
72
+ metadataValid: true,
73
+ metadataReasonCodes: [PROJECT_CANDIDATE_REASON_CODES.SCE_PRESENT]
74
+ };
75
+ }
76
+
77
+ try {
78
+ await fileSystem.readJson(versionPath);
79
+ return {
80
+ scePresent: true,
81
+ metadataValid: true,
82
+ metadataReasonCodes: [PROJECT_CANDIDATE_REASON_CODES.SCE_PRESENT]
83
+ };
84
+ } catch (_error) {
85
+ return {
86
+ scePresent: true,
87
+ metadataValid: false,
88
+ metadataReasonCodes: [
89
+ PROJECT_CANDIDATE_REASON_CODES.SCE_PRESENT,
90
+ PROJECT_CANDIDATE_REASON_CODES.INVALID_PROJECT_METADATA
91
+ ]
92
+ };
93
+ }
94
+ }
95
+
96
+ function dedupeReasonCodes(reasonCodes = []) {
97
+ return Array.from(new Set(reasonCodes.filter(Boolean)));
98
+ }
99
+
100
+ async function inspectProjectCandidate(options = {}, dependencies = {}) {
101
+ const fileSystem = dependencies.fileSystem || fs;
102
+ const stateManager = dependencies.stateManager || new WorkspaceStateManager(dependencies.workspaceStatePath);
103
+ const requestedRoot = normalizeString(options.root || options.rootDir);
104
+ if (!requestedRoot) {
105
+ throw new Error('--root is required');
106
+ }
107
+
108
+ const absoluteRoot = path.resolve(requestedRoot);
109
+ const rootDir = normalizePath(absoluteRoot);
110
+ const inspectedAt = new Date().toISOString();
111
+ const projectName = buildProjectName(rootDir);
112
+
113
+ if (!await fileSystem.pathExists(absoluteRoot)) {
114
+ return {
115
+ inspectedAt,
116
+ rootDir,
117
+ kind: 'invalid',
118
+ projectName,
119
+ readiness: 'blocked',
120
+ availability: 'inaccessible',
121
+ localCandidate: false,
122
+ reasonCodes: [PROJECT_CANDIDATE_REASON_CODES.ROOT_INACCESSIBLE]
123
+ };
124
+ }
125
+
126
+ let rootStats = null;
127
+ try {
128
+ rootStats = await fileSystem.stat(absoluteRoot);
129
+ } catch (_error) {
130
+ return {
131
+ inspectedAt,
132
+ rootDir,
133
+ kind: 'invalid',
134
+ projectName,
135
+ readiness: 'blocked',
136
+ availability: 'inaccessible',
137
+ localCandidate: false,
138
+ reasonCodes: [PROJECT_CANDIDATE_REASON_CODES.ROOT_INACCESSIBLE]
139
+ };
140
+ }
141
+
142
+ if (!rootStats.isDirectory()) {
143
+ return {
144
+ inspectedAt,
145
+ rootDir,
146
+ kind: 'invalid',
147
+ projectName,
148
+ readiness: 'blocked',
149
+ availability: 'degraded',
150
+ localCandidate: false,
151
+ reasonCodes: [PROJECT_CANDIDATE_REASON_CODES.ROOT_INVALID_TYPE]
152
+ };
153
+ }
154
+
155
+ const workspace = await resolveExactWorkspaceByRoot(absoluteRoot, stateManager);
156
+ const sceInspection = await inspectSceMetadata(absoluteRoot, fileSystem);
157
+ const metadataBlocked = sceInspection.scePresent && !sceInspection.metadataValid;
158
+
159
+ if (workspace) {
160
+ const workspaceBlocked = !sceInspection.scePresent || metadataBlocked;
161
+ return {
162
+ inspectedAt,
163
+ rootDir,
164
+ kind: 'workspace-backed',
165
+ projectId: buildWorkspaceProjectId(workspace.name),
166
+ workspaceId: workspace.name,
167
+ projectName,
168
+ readiness: workspaceBlocked ? 'blocked' : 'ready',
169
+ availability: workspaceBlocked ? 'degraded' : 'accessible',
170
+ localCandidate: false,
171
+ reasonCodes: dedupeReasonCodes([
172
+ PROJECT_CANDIDATE_REASON_CODES.WORKSPACE_REGISTERED,
173
+ PROJECT_CANDIDATE_REASON_CODES.ROOT_ACCESSIBLE,
174
+ ...(!sceInspection.scePresent ? [PROJECT_CANDIDATE_REASON_CODES.ROOT_NOT_INITIALIZED] : []),
175
+ ...sceInspection.metadataReasonCodes
176
+ ])
177
+ };
178
+ }
179
+
180
+ if (sceInspection.scePresent) {
181
+ return {
182
+ inspectedAt,
183
+ rootDir,
184
+ kind: 'local-sce-candidate',
185
+ projectId: buildLocalProjectId(rootDir),
186
+ projectName,
187
+ readiness: metadataBlocked ? 'blocked' : 'partial',
188
+ availability: metadataBlocked ? 'degraded' : 'degraded',
189
+ localCandidate: true,
190
+ reasonCodes: dedupeReasonCodes([
191
+ PROJECT_CANDIDATE_REASON_CODES.ROOT_ACCESSIBLE,
192
+ PROJECT_CANDIDATE_REASON_CODES.UNREGISTERED_PROJECT,
193
+ ...sceInspection.metadataReasonCodes
194
+ ])
195
+ };
196
+ }
197
+
198
+ return {
199
+ inspectedAt,
200
+ rootDir,
201
+ kind: 'directory-candidate',
202
+ projectName,
203
+ readiness: 'pending',
204
+ availability: 'accessible',
205
+ localCandidate: true,
206
+ reasonCodes: [
207
+ PROJECT_CANDIDATE_REASON_CODES.ROOT_ACCESSIBLE,
208
+ PROJECT_CANDIDATE_REASON_CODES.ROOT_NOT_INITIALIZED
209
+ ]
210
+ };
211
+ }
212
+
213
+ module.exports = {
214
+ PROJECT_CANDIDATE_REASON_CODES,
215
+ inspectProjectCandidate
216
+ };
@@ -0,0 +1,350 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const WorkspaceStateManager = require('../workspace/multi/workspace-state-manager');
4
+ const SmartOrchestrator = require('../adoption/smart-orchestrator');
5
+ const { applyTakeoverBaseline } = require('../workspace/takeover-baseline');
6
+ const {
7
+ PROJECT_CANDIDATE_REASON_CODES,
8
+ inspectProjectCandidate
9
+ } = require('./candidate-inspection-service');
10
+ const {
11
+ buildWorkspaceProjectId
12
+ } = require('./portfolio-projection-service');
13
+
14
+ const PROJECT_ONBOARDING_REASON_CODES = {
15
+ MISSING_ROOT: 'project.onboarding.blocked.missing_root',
16
+ BLOCKED_BY_CANDIDATE: 'project.onboarding.blocked.candidate_state',
17
+ ROOT_ACCEPTED: 'project.onboarding.root_accepted',
18
+ IMPORT_NO_ACTIVATE: 'project.onboarding.import_no_activate',
19
+ REGISTERED: 'project.onboarding.registered',
20
+ ADOPTED: 'project.onboarding.adopted',
21
+ SCAFFOLD_REUSED: 'project.onboarding.scaffold_reused',
22
+ ADOPTION_FAILED: 'project.onboarding.adoption_failed'
23
+ };
24
+
25
+ function normalizeString(value) {
26
+ if (typeof value !== 'string') {
27
+ return '';
28
+ }
29
+ return value.trim();
30
+ }
31
+
32
+ function buildStep(key, status, detail, reasonCode) {
33
+ return {
34
+ key,
35
+ status,
36
+ ...(reasonCode ? { reasonCode } : {}),
37
+ ...(detail ? { detail } : {})
38
+ };
39
+ }
40
+
41
+ function buildWorkspaceNameCandidate(rootDir) {
42
+ const base = path.basename(rootDir).trim().toLowerCase();
43
+ const normalized = base.replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
44
+ return normalized || 'project';
45
+ }
46
+
47
+ async function allocateWorkspaceId(rootDir, stateManager) {
48
+ const base = buildWorkspaceNameCandidate(rootDir);
49
+ if (!await stateManager.hasWorkspace(base)) {
50
+ return base;
51
+ }
52
+
53
+ for (let index = 2; index < 1000; index += 1) {
54
+ const candidate = `${base}-${index}`;
55
+ if (!await stateManager.hasWorkspace(candidate)) {
56
+ return candidate;
57
+ }
58
+ }
59
+
60
+ throw new Error(`unable to allocate workspace id for root: ${rootDir}`);
61
+ }
62
+
63
+ function buildFailureEnvelope(rootInspection, steps, detail, reasonCode) {
64
+ return {
65
+ mode: 'import',
66
+ generated_at: new Date().toISOString(),
67
+ success: false,
68
+ preview: rootInspection,
69
+ summary: rootInspection,
70
+ steps,
71
+ error: {
72
+ reasonCode,
73
+ detail
74
+ }
75
+ };
76
+ }
77
+
78
+ async function runWithSuppressedConsole(callback, enabled) {
79
+ if (!enabled) {
80
+ return callback();
81
+ }
82
+
83
+ const originalLog = console.log;
84
+ const originalInfo = console.info;
85
+ const originalWarn = console.warn;
86
+ console.log = () => {};
87
+ console.info = () => {};
88
+ console.warn = () => {};
89
+ try {
90
+ return await callback();
91
+ } finally {
92
+ console.log = originalLog;
93
+ console.info = originalInfo;
94
+ console.warn = originalWarn;
95
+ }
96
+ }
97
+
98
+ async function runProjectRootOnboardingImport(options = {}, dependencies = {}) {
99
+ const fileSystem = dependencies.fileSystem || fs;
100
+ const stateManager = dependencies.stateManager || new WorkspaceStateManager(dependencies.workspaceStatePath);
101
+ const root = normalizeString(options.root || options.rootDir);
102
+ if (!root) {
103
+ throw new Error('--root is required');
104
+ }
105
+
106
+ const rootInspection = await inspectProjectCandidate({ root }, {
107
+ ...dependencies,
108
+ fileSystem,
109
+ stateManager
110
+ });
111
+ const steps = [];
112
+
113
+ if (rootInspection.kind === 'invalid') {
114
+ steps.push(buildStep(
115
+ 'register',
116
+ 'failed',
117
+ 'Root directory cannot be onboarded.',
118
+ PROJECT_ONBOARDING_REASON_CODES.BLOCKED_BY_CANDIDATE
119
+ ));
120
+ steps.push(buildStep(
121
+ 'attach',
122
+ 'failed',
123
+ 'Local root is not accessible as a project directory.',
124
+ PROJECT_CANDIDATE_REASON_CODES.ROOT_INACCESSIBLE
125
+ ));
126
+ steps.push(buildStep(
127
+ 'hydrate',
128
+ 'failed',
129
+ 'Onboarding cannot continue until the root is valid.',
130
+ PROJECT_ONBOARDING_REASON_CODES.BLOCKED_BY_CANDIDATE
131
+ ));
132
+ steps.push(buildStep(
133
+ 'activate',
134
+ 'skipped',
135
+ 'Import does not activate invalid roots.',
136
+ PROJECT_ONBOARDING_REASON_CODES.IMPORT_NO_ACTIVATE
137
+ ));
138
+ steps.push(buildStep(
139
+ 'scaffold',
140
+ 'skipped',
141
+ 'Scaffold is blocked until the root becomes valid.',
142
+ PROJECT_ONBOARDING_REASON_CODES.BLOCKED_BY_CANDIDATE
143
+ ));
144
+ return buildFailureEnvelope(
145
+ rootInspection,
146
+ steps,
147
+ 'Root inspection reported an invalid candidate state.',
148
+ PROJECT_ONBOARDING_REASON_CODES.BLOCKED_BY_CANDIDATE
149
+ );
150
+ }
151
+
152
+ if (rootInspection.reasonCodes.includes(PROJECT_CANDIDATE_REASON_CODES.INVALID_PROJECT_METADATA)) {
153
+ steps.push(buildStep(
154
+ 'register',
155
+ 'failed',
156
+ 'Root contains invalid SCE metadata and cannot be imported safely.',
157
+ PROJECT_CANDIDATE_REASON_CODES.INVALID_PROJECT_METADATA
158
+ ));
159
+ steps.push(buildStep(
160
+ 'attach',
161
+ 'done',
162
+ 'Local root is reachable.',
163
+ PROJECT_ONBOARDING_REASON_CODES.ROOT_ACCEPTED
164
+ ));
165
+ steps.push(buildStep(
166
+ 'hydrate',
167
+ 'failed',
168
+ 'Existing project metadata must be repaired before import.',
169
+ PROJECT_CANDIDATE_REASON_CODES.INVALID_PROJECT_METADATA
170
+ ));
171
+ steps.push(buildStep(
172
+ 'activate',
173
+ 'skipped',
174
+ 'Import does not activate blocked projects.',
175
+ PROJECT_ONBOARDING_REASON_CODES.IMPORT_NO_ACTIVATE
176
+ ));
177
+ steps.push(buildStep(
178
+ 'scaffold',
179
+ 'skipped',
180
+ 'Scaffold is blocked by invalid project metadata.',
181
+ PROJECT_CANDIDATE_REASON_CODES.INVALID_PROJECT_METADATA
182
+ ));
183
+ return buildFailureEnvelope(
184
+ rootInspection,
185
+ steps,
186
+ 'Existing project metadata is invalid.',
187
+ PROJECT_CANDIDATE_REASON_CODES.INVALID_PROJECT_METADATA
188
+ );
189
+ }
190
+
191
+ let onboardingPreview = { ...rootInspection };
192
+ let importResult = null;
193
+ let takeoverBaseline = null;
194
+
195
+ if (rootInspection.kind === 'directory-candidate') {
196
+ const orchestrator = dependencies.smartOrchestrator || new SmartOrchestrator();
197
+ importResult = await runWithSuppressedConsole(() => orchestrator.orchestrate(rootInspection.rootDir, {
198
+ dryRun: false,
199
+ verbose: false,
200
+ skipBackup: false,
201
+ skipUpdate: false
202
+ }), options.json === true);
203
+
204
+ if (!importResult.success) {
205
+ steps.push(buildStep(
206
+ 'register',
207
+ 'failed',
208
+ 'Workspace registration was not attempted because adoption failed.',
209
+ PROJECT_ONBOARDING_REASON_CODES.ADOPTION_FAILED
210
+ ));
211
+ steps.push(buildStep(
212
+ 'attach',
213
+ 'done',
214
+ 'Local root is reachable.',
215
+ PROJECT_ONBOARDING_REASON_CODES.ROOT_ACCEPTED
216
+ ));
217
+ steps.push(buildStep(
218
+ 'hydrate',
219
+ 'failed',
220
+ (importResult.errors || []).join('; ') || 'Adoption failed.',
221
+ PROJECT_ONBOARDING_REASON_CODES.ADOPTION_FAILED
222
+ ));
223
+ steps.push(buildStep(
224
+ 'activate',
225
+ 'skipped',
226
+ 'Import does not activate failed onboarding results.',
227
+ PROJECT_ONBOARDING_REASON_CODES.IMPORT_NO_ACTIVATE
228
+ ));
229
+ steps.push(buildStep(
230
+ 'scaffold',
231
+ 'failed',
232
+ 'SCE baseline could not be applied to the root directory.',
233
+ PROJECT_ONBOARDING_REASON_CODES.ADOPTION_FAILED
234
+ ));
235
+ return buildFailureEnvelope(
236
+ rootInspection,
237
+ steps,
238
+ (importResult.errors || []).join('; ') || 'Adoption failed.',
239
+ PROJECT_ONBOARDING_REASON_CODES.ADOPTION_FAILED
240
+ );
241
+ }
242
+
243
+ const packageJson = require('../../package.json');
244
+ takeoverBaseline = await applyTakeoverBaseline(rootInspection.rootDir, {
245
+ apply: true,
246
+ writeReport: true,
247
+ sceVersion: packageJson.version
248
+ });
249
+ onboardingPreview = await inspectProjectCandidate({ root: rootInspection.rootDir }, {
250
+ ...dependencies,
251
+ fileSystem,
252
+ stateManager
253
+ });
254
+ }
255
+
256
+ let workspaceId = rootInspection.workspaceId || null;
257
+ if (!workspaceId) {
258
+ workspaceId = await allocateWorkspaceId(rootInspection.rootDir, stateManager);
259
+ await stateManager.createWorkspace(workspaceId, rootInspection.rootDir);
260
+ onboardingPreview = {
261
+ ...onboardingPreview,
262
+ kind: 'workspace-backed',
263
+ projectId: buildWorkspaceProjectId(workspaceId),
264
+ workspaceId,
265
+ readiness: onboardingPreview.kind === 'directory-candidate' ? 'ready' : onboardingPreview.readiness,
266
+ availability: 'accessible',
267
+ localCandidate: false,
268
+ reasonCodes: Array.from(new Set([
269
+ PROJECT_CANDIDATE_REASON_CODES.WORKSPACE_REGISTERED,
270
+ ...onboardingPreview.reasonCodes
271
+ ]))
272
+ };
273
+ }
274
+
275
+ steps.push(buildStep(
276
+ 'register',
277
+ 'done',
278
+ rootInspection.workspaceId
279
+ ? 'Workspace was already registered.'
280
+ : `Workspace registered as ${workspaceId}.`,
281
+ rootInspection.workspaceId
282
+ ? PROJECT_CANDIDATE_REASON_CODES.WORKSPACE_REGISTERED
283
+ : PROJECT_ONBOARDING_REASON_CODES.REGISTERED
284
+ ));
285
+ steps.push(buildStep(
286
+ 'attach',
287
+ 'done',
288
+ 'Local root is accepted as the canonical onboarding source.',
289
+ PROJECT_ONBOARDING_REASON_CODES.ROOT_ACCEPTED
290
+ ));
291
+ steps.push(buildStep(
292
+ 'hydrate',
293
+ 'done',
294
+ rootInspection.kind === 'directory-candidate'
295
+ ? 'SCE baseline and project hydration were applied to the root directory.'
296
+ : 'Existing SCE project root is ready for portfolio import.',
297
+ rootInspection.kind === 'directory-candidate'
298
+ ? PROJECT_ONBOARDING_REASON_CODES.ADOPTED
299
+ : PROJECT_CANDIDATE_REASON_CODES.SCE_PRESENT
300
+ ));
301
+ steps.push(buildStep(
302
+ 'activate',
303
+ 'skipped',
304
+ 'Import keeps active workspace selection unchanged in phase-1.',
305
+ PROJECT_ONBOARDING_REASON_CODES.IMPORT_NO_ACTIVATE
306
+ ));
307
+ steps.push(buildStep(
308
+ 'scaffold',
309
+ rootInspection.kind === 'directory-candidate' ? 'done' : 'skipped',
310
+ rootInspection.kind === 'directory-candidate'
311
+ ? 'Applied SCE baseline files to the project root.'
312
+ : 'Existing SCE baseline is reused; no scaffold rewrite was required.',
313
+ rootInspection.kind === 'directory-candidate'
314
+ ? PROJECT_ONBOARDING_REASON_CODES.ADOPTED
315
+ : PROJECT_ONBOARDING_REASON_CODES.SCAFFOLD_REUSED
316
+ ));
317
+
318
+ return {
319
+ mode: 'import',
320
+ generated_at: new Date().toISOString(),
321
+ success: true,
322
+ preview: onboardingPreview,
323
+ summary: onboardingPreview,
324
+ steps,
325
+ result: {
326
+ rootDir: onboardingPreview.rootDir,
327
+ projectId: onboardingPreview.projectId || null,
328
+ workspaceId: onboardingPreview.workspaceId || null,
329
+ ...(importResult ? {
330
+ adoption: {
331
+ mode: importResult.mode || null,
332
+ backupId: importResult.backup ? importResult.backup.id : null,
333
+ changes: importResult.changes || { created: [], updated: [], deleted: [], preserved: [] },
334
+ warnings: importResult.warnings || []
335
+ }
336
+ } : {}),
337
+ ...(takeoverBaseline ? {
338
+ takeoverBaseline: {
339
+ reportFile: takeoverBaseline.report_file || null,
340
+ summary: takeoverBaseline.summary || {}
341
+ }
342
+ } : {})
343
+ }
344
+ };
345
+ }
346
+
347
+ module.exports = {
348
+ PROJECT_ONBOARDING_REASON_CODES,
349
+ runProjectRootOnboardingImport
350
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.60",
3
+ "version": "3.6.61",
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": {
@@ -8,19 +8,21 @@ const REQUIRED_CHECKS = [
8
8
  {
9
9
  path: 'README.md',
10
10
  requiredSnippets: [
11
- '`sce project portfolio show|target resolve|supervision show`'
11
+ '`sce project portfolio show|candidate inspect|onboarding import|target resolve|supervision show`'
12
12
  ]
13
13
  },
14
14
  {
15
15
  path: 'README.zh.md',
16
16
  requiredSnippets: [
17
- '`sce project portfolio show|target resolve|supervision show`'
17
+ '`sce project portfolio show|candidate inspect|onboarding import|target resolve|supervision show`'
18
18
  ]
19
19
  },
20
20
  {
21
21
  path: 'docs/command-reference.md',
22
22
  requiredSnippets: [
23
23
  'sce project portfolio show [options]',
24
+ 'sce project candidate inspect --root <path> [options]',
25
+ 'sce project onboarding import --root <path> [options]',
24
26
  'sce project target resolve [options]',
25
27
  'sce project supervision show --project <id> [options]'
26
28
  ]
@@ -45,6 +47,8 @@ const REQUIRED_CHECKS = [
45
47
  path: 'docs/magicball-project-portfolio-contract.md',
46
48
  requiredSnippets: [
47
49
  'sce project portfolio show --json',
50
+ 'sce project candidate inspect --root <path> --json',
51
+ 'sce project onboarding import --root <path> --json',
48
52
  'sce project target resolve --json',
49
53
  'sce project supervision show --project <project-id> --json'
50
54
  ]
@@ -53,7 +57,11 @@ const REQUIRED_CHECKS = [
53
57
  path: 'docs/magicball-frontend-state-and-command-mapping.md',
54
58
  requiredSnippets: [
55
59
  'projectPortfolio: Record<string, unknown> | null',
60
+ 'projectCandidate: Record<string, unknown> | null',
61
+ 'projectOnboarding: Record<string, unknown> | null',
56
62
  '`sce project portfolio show --json`',
63
+ '`sce project candidate inspect --root <path> --json`',
64
+ '`sce project onboarding import --root <path> --json`',
57
65
  '`sce project target resolve --request <text> --current-project <project-id> --json`',
58
66
  '`sce project supervision show --project <project-id> --json`'
59
67
  ]
@@ -62,6 +70,8 @@ const REQUIRED_CHECKS = [
62
70
  path: 'docs/magicball-cli-invocation-examples.md',
63
71
  requiredSnippets: [
64
72
  'sce project portfolio show --json',
73
+ 'sce project candidate inspect --root "C:/workspace/customer-order-demo" --json',
74
+ 'sce project onboarding import --root "C:/workspace/customer-order-demo" --json',
65
75
  'sce project target resolve --request "continue customer-order-demo" --json',
66
76
  'sce project supervision show --project workspace:customer-order-demo --json'
67
77
  ]
@@ -71,6 +81,8 @@ const REQUIRED_CHECKS = [
71
81
  requiredSnippets: [
72
82
  '## Phase 0: Multi-project Workspace Shell',
73
83
  'sce project portfolio show --json',
84
+ 'sce project candidate inspect --root "<path>" --json',
85
+ 'sce project onboarding import --root "<path>" --json',
74
86
  'sce project target resolve --request "<text>" --current-project <project-id> --json',
75
87
  'sce project supervision show --project <project-id> --json'
76
88
  ]
@@ -80,14 +92,15 @@ const REQUIRED_CHECKS = [
80
92
  requiredSnippets: [
81
93
  '- `docs/magicball-project-portfolio-contract.md`',
82
94
  '- project switcher from `project portfolio show`',
95
+ '- local-root candidate receipt from `project candidate inspect`',
83
96
  '- project health summary from `project supervision show`'
84
97
  ]
85
98
  },
86
99
  {
87
100
  path: 'docs/magicball-integration-issue-tracker.md',
88
101
  requiredSnippets: [
89
- '- `project portfolio show/target resolve/supervision show`',
90
- '5. treat `project portfolio / target resolve / supervision` as the default multi-project shell truth'
102
+ '- `project portfolio show/candidate inspect/onboarding import/target resolve/supervision show`',
103
+ '5. treat `project portfolio / candidate inspect / onboarding import / target resolve / supervision` as the default multi-project shell truth'
91
104
  ]
92
105
  },
93
106
  {
@@ -243,6 +243,6 @@ A Spec is a complete feature definition with three parts:
243
243
  ---
244
244
 
245
245
  **Project Type**: Spec-driven development
246
- **sce Version**: 3.6.60
247
- **Last Updated**: 2026-03-19
246
+ **sce Version**: 3.6.61
247
+ **Last Updated**: 2026-03-20
248
248
  **Purpose**: Guide AI tools to work effectively with this project