Skip to content

Commit 7591ded

Browse files
feat(workspace-hub): polish backlog follow-through
1 parent 813ac54 commit 7591ded

18 files changed

Lines changed: 614 additions & 38 deletions

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
- Updated [HANDOVER](HANDOVER.md) completion review, the project review addendum pointer to [CHANGELOG](CHANGELOG.md) for resolved Hub items, and aligned [docs/README](README.md), wiki stubs, contributor templates, and [AGENTS.md](../AGENTS.md) with the same stance.
77
- Rewrote [00-overview](00-overview.md) and [02-local-runtime-handover](02-local-runtime-handover.md) so runtime guidance uses generic mapped-host terminology while keeping stable manifest enum and field names (`servbay`, `servbayPath`, `servbaySubdomain`) documented in [repos/workspace-hub/docs/manifest.md](../repos/workspace-hub/docs/manifest.md).
88
- Added a tracked [docs/plans/readme-docs-closeout.md](plans/readme-docs-closeout.md) plan for future reference, and aligned small Workspace Hub/operator copy surfaces with the same mapped-host wording in `repos/workspace-hub`, `tools/scripts/doctor-workspace.sh`, and `tools/scripts/setup-workspace-profile.sh`.
9+
- Extended `repos/workspace-hub` backlog follow-through with better repo-list prioritization for pinned and recent work, clearer Python-aware dependency readiness, intake-result surfacing in repo details, capability search and inspection, explicit mapped-host routing status, and richer runtime troubleshooting guidance.
10+
- Advanced Workspace memory graph support from file-open-only toward Phase 2 by surfacing derived-edge counts, node-type breakdown, and an in-app graph report preview for the selected target, while clarifying intentional MCP profile usage in the Workspace Hub settings panel.
911

1012
## 2026-04-10
1113

docs/HANDOVER.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,9 @@ Status: complete and validated
201201

202202
Current likely pickup:
203203

204-
- apply the managed MCP v1 profiles on the machines that need them and use `safe-readonly` versus `default-full` intentionally rather than treating MCP as an always-on hidden dependency
205-
- `repos/workspace-hub` memory-graph Phase 2 only if operators need richer relationship extraction, filtering, or in-app embedding beyond the current file-open flow
206-
- `repos/workspace-hub` capability drill-down if operators need deeper per-capability install/update health than the current snapshot
207-
- `repos/workspace-hub` repo-intake polish if new repos still need clearer first-run notes, optional ability guidance, or tighter starter docs
204+
- apply the managed MCP v1 profiles on the machines that need them and keep `safe-readonly` versus `default-full` intentional rather than hidden defaults
205+
- extend cross-stack dependency detection beyond the current Node, Composer, and Python-local-environment heuristics if operators need deeper readiness checks
206+
- deepen Memory Graph beyond the current in-app report preview and node-type breakdown only if operators need richer relationship extraction or graph interaction
208207
- keep future batches end-to-end and update this file when a batch becomes the new practical pickup point
209208

210209
## Acceptance closeout (2026-04-10)
@@ -295,11 +294,9 @@ What appears complete or substantially complete:
295294

296295
What still reads as open or incomplete in the docs:
297296

298-
- deeper repo diagnostics and drill-down polish
299-
- favourites and last-opened polish
300-
- clearer dependency-readiness feedback
301-
- clearer mapped-host and proxy preview polish where operators use that workflow
302-
- additional runtime troubleshooting documentation
297+
- broader cross-stack dependency-readiness heuristics beyond the current Node, Composer, and Python-local-environment checks
298+
- deeper mapped-host preview generation and operator-specific routing automation beyond the current routing status and guidance
299+
- richer in-app graph exploration if operators need more than the current report preview and node-type breakdown
303300

304301
## Core memory decision (2026-04-08)
305302

repos/workspace-hub/docs/memory-graph.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,11 @@ Status: implemented
171171

172172
### Phase 2
173173

174-
- enrich the adapter with conversation exports and wake-up summaries
175-
- add bridge-node and centrality summaries
176-
- support basic filtering by node type and confidence
174+
Status: partially implemented
175+
176+
- graph snapshots now surface derived-edge counts and node-type breakdown in Workspace Hub
177+
- the Workspace memory page now shows an in-app preview of `graph-report.md` for the selected target
178+
- later work can still enrich the adapter with conversation exports, bridge-node or centrality summaries, and interactive filtering by node type or confidence
177179

178180
### Phase 3
179181

repos/workspace-hub/docs/runtime-troubleshooting.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ If the repo has no inferred install command:
2020
2. Add `installCommand` to `.workspace/project.json` if the repo needs one.
2121
3. Re-run the install from Workspace Hub.
2222

23+
If the repo looks like a Python-style project:
24+
25+
1. Confirm whether it expects a local `.venv/` or `venv/`.
26+
2. If it does, create or refresh that environment before retrying the runtime.
27+
3. If it intentionally uses a global interpreter or another workflow, capture that in repo docs so the next operator does not misread the Hub warning.
28+
2329
If the missing piece is not a repo dependency but an optional workspace ability:
2430

2531
1. Do not hide that requirement in the repo manifest.
@@ -53,6 +59,12 @@ If Workspace Hub shows the repo as `running` but health is `unreachable`:
5359
3. Review the runtime log for the actual local URL.
5460
4. Save a `previewUrl` or `healthcheckUrl` override when the repo uses a fixed non-default address.
5561

62+
If the repo prefers mapped-host routing:
63+
64+
1. Confirm the repo has a stable mapped-host path or subdomain in the manifest.
65+
2. Save an explicit `previewUrl` once the routed hostname is known, instead of relying on transient runtime-log URLs.
66+
3. Check the operator’s reverse-proxy or local-host mapping outside Workspace Hub if the route is configured but still unreachable.
67+
5668
If `Open preview` attempted to start the repo but still failed to open a working preview:
5769

5870
1. Read the runtime log in the details panel first.
@@ -89,3 +101,11 @@ Practical response:
89101
Update `.workspace/project.json` when Workspace Hub keeps inferring the wrong command, package manager, preview URL, or preferred mode. Use saved overrides for temporary local corrections and the manifest for repo-native behaviour that should stay with the repo.
90102

91103
Do not use the manifest to imply that an optional workspace ability is present. If a workflow depends on one, document that explicitly in repo docs and keep the install path visible to the next contributor.
104+
105+
## Repo intake follow-up
106+
107+
After running repo intake:
108+
109+
1. Read the intake notes in the details panel before assuming the repo is fully ready.
110+
2. Check whether intake created a manifest or intentionally skipped it.
111+
3. Fill in any repo-specific install, dev, preview, or mapped-host details that conservative intake could not infer safely.

repos/workspace-hub/server/mempalace-graph.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,15 @@ export type MempalaceGraphArtifactPaths = {
3737
}
3838

3939
type MempalaceGraphDocument = {
40+
edges?: Array<{
41+
confidence?: number
42+
derived?: boolean
43+
type?: string
44+
}>
4045
generatedAt?: string
46+
nodes?: Array<{
47+
type?: string
48+
}>
4149
summary?: {
4250
edgeCount?: number
4351
nodeCount?: number
@@ -47,11 +55,14 @@ type MempalaceGraphDocument = {
4755
export type MempalaceGraphSnapshot = {
4856
artifacts: MempalaceGraphArtifactPaths
4957
available: boolean
58+
derivedEdgeCount: number | null
5059
edgeCount: number | null
5160
lastBuiltAt: string | null
5261
nodeCount: number | null
62+
nodeTypeCounts: Partial<Record<string, number>>
5363
outputDirectory: string | null
5464
outputDirectoryExists: boolean
65+
reportExcerpt: string[]
5566
}
5667

5768
async function fileExists(targetPath: string) {
@@ -75,11 +86,14 @@ export async function readMempalaceGraphSnapshot(
7586
reportPath: null,
7687
},
7788
available: false,
89+
derivedEdgeCount: null,
7890
edgeCount: null,
7991
lastBuiltAt: null,
8092
nodeCount: null,
93+
nodeTypeCounts: {},
8194
outputDirectory: null,
8295
outputDirectoryExists: false,
96+
reportExcerpt: [],
8397
}
8498
}
8599

@@ -101,17 +115,46 @@ export async function readMempalaceGraphSnapshot(
101115
}
102116
}
103117

118+
let reportExcerpt: string[] = []
119+
if (reportExists) {
120+
try {
121+
reportExcerpt = (await readFile(reportPath, 'utf8'))
122+
.split(/\r?\n/u)
123+
.map((line) => line.trim())
124+
.filter(Boolean)
125+
.slice(0, 8)
126+
} catch {
127+
reportExcerpt = []
128+
}
129+
}
130+
131+
const nodeTypeCounts = Object.fromEntries(
132+
Object.entries(
133+
(document?.nodes ?? []).reduce<Record<string, number>>((counts, node) => {
134+
const type = typeof node.type === 'string' && node.type.trim() ? node.type.trim() : 'unknown'
135+
counts[type] = (counts[type] ?? 0) + 1
136+
return counts
137+
}, {}),
138+
).sort(([left], [right]) => left.localeCompare(right)),
139+
)
140+
const derivedEdgeCount = document
141+
? (document.edges ?? []).filter((edge) => edge.derived).length
142+
: null
143+
104144
return {
105145
artifacts: {
106146
htmlPath: htmlExists ? htmlPath : null,
107147
jsonPath: jsonExists ? jsonPath : null,
108148
reportPath: reportExists ? reportPath : null,
109149
},
110150
available: true,
151+
derivedEdgeCount,
111152
edgeCount: typeof document?.summary?.edgeCount === 'number' ? document.summary.edgeCount : null,
112153
lastBuiltAt: document?.generatedAt ?? null,
113154
nodeCount: typeof document?.summary?.nodeCount === 'number' ? document.summary.nodeCount : null,
155+
nodeTypeCounts,
114156
outputDirectory,
115157
outputDirectoryExists,
158+
reportExcerpt,
116159
}
117160
}

repos/workspace-hub/server/workspace.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,10 +1338,17 @@ async function readDependencyState(options: {
13381338
names: string[]
13391339
packageManager: string
13401340
previewCommand: string | null
1341+
type?: RepoType
13411342
}): Promise<RepoDependencyState> {
13421343
const normalizedPackageManager = options.packageManager.split('@')[0] ?? ''
13431344
const hasPackageJson = options.names.includes('package.json')
13441345
const hasComposerJson = options.names.includes('composer.json')
1346+
const hasPythonProject =
1347+
options.names.includes('pyproject.toml') ||
1348+
options.names.includes('requirements.txt') ||
1349+
options.names.includes('Pipfile') ||
1350+
options.names.includes('setup.py') ||
1351+
options.names.includes('uv.lock')
13451352
const isNodeStyleRepo =
13461353
hasPackageJson ||
13471354
normalizedPackageManager === 'npm' ||
@@ -1406,6 +1413,38 @@ async function readDependencyState(options: {
14061413
} satisfies RepoDependencyState
14071414
}
14081415

1416+
if (hasPythonProject) {
1417+
const virtualEnvPaths = ['.venv', 'venv'].map((directoryName) =>
1418+
path.join(options.fullPath, directoryName),
1419+
)
1420+
1421+
for (const installPath of virtualEnvPaths) {
1422+
if (await fileExists(installPath)) {
1423+
return {
1424+
installPath,
1425+
reason: `${path.basename(installPath)}/ is present for this Python-style repo.`,
1426+
state: 'ready',
1427+
} satisfies RepoDependencyState
1428+
}
1429+
}
1430+
1431+
return {
1432+
installPath: virtualEnvPaths[0] ?? null,
1433+
reason:
1434+
'Python project markers were found, but no local virtual environment was detected. Confirm whether this repo expects .venv/, venv/, or a global interpreter workflow.',
1435+
state: options.installCommand ? 'missing' : 'unknown',
1436+
} satisfies RepoDependencyState
1437+
}
1438+
1439+
if (options.type === 'wordpress' && !options.installCommand && !hasComposerJson) {
1440+
return {
1441+
installPath: null,
1442+
reason:
1443+
'This WordPress repo usually relies on an external runtime, so repo-local dependency readiness is not treated as a blocker.',
1444+
state: 'not-applicable',
1445+
} satisfies RepoDependencyState
1446+
}
1447+
14091448
if (options.installCommand) {
14101449
return {
14111450
installPath: null,
@@ -1784,6 +1823,15 @@ function isRepoCandidate(names: string[], collection: string | null) {
17841823
return true
17851824
}
17861825

1826+
if (
1827+
names.includes('pyproject.toml') ||
1828+
names.includes('requirements.txt') ||
1829+
names.includes('Pipfile') ||
1830+
names.includes('setup.py')
1831+
) {
1832+
return true
1833+
}
1834+
17871835
if (
17881836
names.includes('index.html') ||
17891837
names.includes('wp-config.php') ||
@@ -1920,6 +1968,7 @@ async function buildRepoRecord(
19201968
names,
19211969
packageManager,
19221970
previewCommand,
1971+
type,
19231972
}),
19241973
])
19251974

0 commit comments

Comments
 (0)