scene-capability-engine 3.6.59 → 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 +16 -0
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/docs/command-reference.md +37 -1
- package/docs/magicball-adaptation-task-checklist-v1.md +14 -0
- package/docs/magicball-cli-invocation-examples.md +2 -0
- package/docs/magicball-frontend-state-and-command-mapping.md +5 -0
- package/docs/magicball-integration-issue-tracker.md +2 -2
- package/docs/magicball-project-portfolio-contract.md +60 -4
- package/docs/magicball-sce-adaptation-guide.md +2 -0
- package/docs/magicball-ui-surface-checklist.md +2 -0
- package/docs/release-checklist.md +1 -1
- package/docs/releases/README.md +2 -0
- package/docs/releases/v3.6.60.md +19 -0
- package/docs/releases/v3.6.61.md +21 -0
- package/docs/zh/release-checklist.md +1 -1
- package/docs/zh/releases/README.md +2 -0
- package/docs/zh/releases/v3.6.60.md +19 -0
- package/docs/zh/releases/v3.6.61.md +21 -0
- package/lib/commands/project.js +52 -0
- package/lib/problem/project-problem-projection.js +9 -1
- package/lib/project/candidate-inspection-service.js +216 -0
- package/lib/project/root-onboarding-service.js +350 -0
- package/package.json +1 -1
- package/scripts/magicball-project-contract-audit.js +17 -4
- package/template/.sce/README.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ 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
|
+
|
|
20
|
+
## [3.6.60] - 2026-03-19
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Stabilized tracked `project-shared-problems.json` aging output to whole-day precision so release and publish gates no longer dirty the worktree on minute-scale `age_days` drift.
|
|
24
|
+
- Published the canonical follow-up package after `3.6.59` was already minted from the pre-fix candidate; use `3.6.60` when strict npm/tag/repository alignment matters.
|
|
25
|
+
|
|
10
26
|
## [3.6.59] - 2026-03-19
|
|
11
27
|
|
|
12
28
|
### 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.
|
|
234
|
-
**Last Updated**: 2026-03-
|
|
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.
|
|
239
|
-
**最后更新**:2026-03-
|
|
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
|
|
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.
|
|
205
|
-
3.
|
|
206
|
-
4.
|
|
207
|
-
5.
|
|
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
|
|
package/docs/releases/README.md
CHANGED
|
@@ -9,6 +9,8 @@ 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)
|
|
13
|
+
- [v3.6.60 release notes](./v3.6.60.md)
|
|
12
14
|
- [v3.6.59 release notes](./v3.6.59.md)
|
|
13
15
|
- [v3.6.58 release notes](./v3.6.58.md)
|
|
14
16
|
- [v3.6.57 release notes](./v3.6.57.md)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# v3.6.60 Release Notes
|
|
2
|
+
|
|
3
|
+
Release date: 2026-03-19
|
|
4
|
+
|
|
5
|
+
## Highlights
|
|
6
|
+
|
|
7
|
+
- Stabilized tracked `project-shared-problems.json` aging output to whole-day precision so release and publish gates no longer dirty the worktree on minute-scale `age_days` drift.
|
|
8
|
+
- Published the canonical follow-up package after `v3.6.59` had already been published from the pre-fix candidate.
|
|
9
|
+
|
|
10
|
+
## Validation
|
|
11
|
+
|
|
12
|
+
- `npx jest tests/unit/problem/project-problem-projection.test.js --runInBand`
|
|
13
|
+
- `npm run audit:release-docs`
|
|
14
|
+
- `node scripts/git-managed-gate.js --fail-on-violation --json`
|
|
15
|
+
- `npm publish --access public` via `prepublishOnly`
|
|
16
|
+
|
|
17
|
+
## Release Notes
|
|
18
|
+
|
|
19
|
+
- Use `v3.6.60` as the canonical aligned version for the shared-problem projection release line. It preserves the `v3.6.58/v3.6.59` MagicBall and multi-project contracts, and only closes the last publish-time worktree-drift gap in tracked project problem projection output.
|
|
@@ -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
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# v3.6.60 发布说明
|
|
2
|
+
|
|
3
|
+
发布日期:2026-03-19
|
|
4
|
+
|
|
5
|
+
## 重点变化
|
|
6
|
+
|
|
7
|
+
- 稳定了被 Git 跟踪的 `project-shared-problems.json` aging 输出;发布/发版时不再因为分钟级 `age_days` 漂移把工作区写脏。
|
|
8
|
+
- 在 `v3.6.59` 已经以未修复候选包形式发布后,补发了真正对齐的正式版本。
|
|
9
|
+
|
|
10
|
+
## 验证
|
|
11
|
+
|
|
12
|
+
- `npx jest tests/unit/problem/project-problem-projection.test.js --runInBand`
|
|
13
|
+
- `npm run audit:release-docs`
|
|
14
|
+
- `node scripts/git-managed-gate.js --fail-on-violation --json`
|
|
15
|
+
- 通过 `prepublishOnly` 执行 `npm publish --access public`
|
|
16
|
+
|
|
17
|
+
## 发布说明
|
|
18
|
+
|
|
19
|
+
- `v3.6.60` 才是这条 shared-problem projection 发布线的标准对齐版本。它保持 `v3.6.58/v3.6.59` 的 MagicBall 与多项目合同不变,只补上了项目共享问题投影在发版时的最后一个工作区漂移缺口。
|
|
@@ -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 导入”的正式语义。
|
package/lib/commands/project.js
CHANGED
|
@@ -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,
|
|
@@ -107,6 +107,14 @@ function toComparableProjection(payload) {
|
|
|
107
107
|
return sortKeysDeep(clone);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function normalizeProjectionAgeDays(value) {
|
|
111
|
+
const numeric = Number(value);
|
|
112
|
+
if (!Number.isFinite(numeric)) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return Math.max(0, Math.floor(numeric));
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
async function buildProjectSharedProblemProjection(projectPath = process.cwd(), options = {}, dependencies = {}) {
|
|
111
119
|
const fileSystem = dependencies.fileSystem || fs;
|
|
112
120
|
const studioIntakePolicy = dependencies.studioIntakePolicy || await loadStudioIntakePolicy(projectPath, fileSystem);
|
|
@@ -156,7 +164,7 @@ async function buildProjectSharedProblemProjection(projectPath = process.cwd(),
|
|
|
156
164
|
scene_id: record.scene_id || null,
|
|
157
165
|
lifecycle_state: record.lifecycle_state || null,
|
|
158
166
|
updated_at: record.updated_at || null,
|
|
159
|
-
age_days:
|
|
167
|
+
age_days: normalizeProjectionAgeDays(record.age_days),
|
|
160
168
|
tasks_total: Number.isFinite(Number(record.tasks_total)) ? Number(record.tasks_total) : 0,
|
|
161
169
|
tasks_done: Number.isFinite(Number(record.tasks_done)) ? Number(record.tasks_done) : 0,
|
|
162
170
|
tasks_progress: Number.isFinite(Number(record.tasks_progress)) ? Number(record.tasks_progress) : 0,
|
|
@@ -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
|
@@ -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
|
{
|
package/template/.sce/README.md
CHANGED
|
@@ -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.
|
|
247
|
-
**Last Updated**: 2026-03-
|
|
246
|
+
**sce Version**: 3.6.61
|
|
247
|
+
**Last Updated**: 2026-03-20
|
|
248
248
|
**Purpose**: Guide AI tools to work effectively with this project
|