create-principles-disciple 1.145.43 → 1.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/console/dist/server/routes/examiner-executor.d.ts +8 -2
  3. package/console/dist/server/routes/examiner-executor.js +10 -4
  4. package/console/dist/server/routes/examiner.d.ts +23 -17
  5. package/console/dist/server/routes/examiner.js +30 -27
  6. package/console/dist/ui/App.js +1 -1
  7. package/console/dist/ui/components/layout/app-sidebar.d.ts +3 -4
  8. package/console/dist/ui/components/layout/app-sidebar.js +5 -7
  9. package/console/dist/ui/i18n/en.json +1 -1
  10. package/console/dist/ui/i18n/zh-CN.json +1 -1
  11. package/console/dist/ui/utils/runtime-mode.d.ts +2 -1
  12. package/console/dist/ui/utils/runtime-mode.js +2 -1
  13. package/console/dist/web/assets/app.js +23 -23
  14. package/console/lab/lib/challenge-actor.mjs +394 -0
  15. package/console/lab/lib/challenge-arms.mjs +102 -0
  16. package/console/lab/lib/challenge-behavior-report.mjs +2484 -0
  17. package/console/lab/lib/challenge-compile.mjs +211 -0
  18. package/console/lab/lib/challenge-freeze.mjs +215 -0
  19. package/console/lab/lib/challenge-isolation-probe.mjs +550 -0
  20. package/console/lab/lib/challenge-ledger.mjs +206 -0
  21. package/console/lab/lib/challenge-openclaw-host.mjs +630 -0
  22. package/console/lab/lib/challenge-runner.mjs +868 -0
  23. package/console/lab/lib/challenge-stage-r-gate.mjs +458 -0
  24. package/console/lab/lib/challenge-statistics.mjs +591 -0
  25. package/console/lab/lib/claim-evidence-env.mjs +652 -0
  26. package/console/lab/lib/claim-evidence-scorer.mjs +784 -0
  27. package/console/lab/lib/examiner-injection-evidence.mjs +367 -0
  28. package/console/lab/lib/examiner-study.mjs +1203 -0
  29. package/console/lab/lib/examiner-v07-cases.mjs +187 -0
  30. package/console/lab/lib/examiner-v2-cases.mjs +61 -0
  31. package/console/lab/lib/fixture.mjs +382 -0
  32. package/console/lab/lib/g2.mjs +146 -0
  33. package/console/lab/lib/host-actor.mjs +987 -0
  34. package/console/lab/lib/sidecar.mjs +273 -0
  35. package/package.json +1 -1
  36. package/release-manager/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "productVersion": "2.5.0",
4
- "sourceCommit": "5ee57d2ce6030686a3e8b98c78a9dfb636eb21bd"
3
+ "productVersion": "2.6.0",
4
+ "sourceCommit": "dc2883bad3d0b3ce17a4a103f9b62bacdda50916"
5
5
  }
@@ -90,8 +90,14 @@ export declare function canonicalPathSync(target: string): string | null;
90
90
  export declare function isSamePath(a: string, b: string): boolean;
91
91
  /** Strict containment: equal paths are NOT inside each other. */
92
92
  export declare function isPathInside(root: string, candidate: string): boolean;
93
- /** Resolve the two roots once. `PD_EXAMINER_STUDIES_ROOT` overrides the WRITABLE root only. */
94
- export declare function resolveStudyRoots(labRoot: string): StudyRoots;
93
+ /**
94
+ * Resolve the two roots once. Console-created studies are WORKSPACE DATA, not
95
+ * repository content: they default to `<dataRoot>/studies`, where the caller
96
+ * passes `<workspace>/.pd/examiner`. `PD_EXAMINER_STUDIES_ROOT` overrides the
97
+ * WRITABLE root only (tests / operators). The historical research assets ship
98
+ * with the lab payload (absent in an installation → an empty read-only list).
99
+ */
100
+ export declare function resolveStudyRoots(labRoot: string, dataRoot: string): StudyRoots;
95
101
  export declare function isJsonObject(value: unknown): value is Record<string, unknown>;
96
102
  /** Boundary-checked study file reader (fails closed on missing/malformed). */
97
103
  export declare function readStudyJson(studyDir: string, rel: string): Record<string, unknown>;
@@ -69,10 +69,16 @@ export function isPathInside(root, candidate) {
69
69
  const prefix = r.endsWith(path.sep) ? r : `${r}${path.sep}`;
70
70
  return c !== r && c.startsWith(prefix);
71
71
  }
72
- /** Resolve the two roots once. `PD_EXAMINER_STUDIES_ROOT` overrides the WRITABLE root only. */
73
- export function resolveStudyRoots(labRoot) {
72
+ /**
73
+ * Resolve the two roots once. Console-created studies are WORKSPACE DATA, not
74
+ * repository content: they default to `<dataRoot>/studies`, where the caller
75
+ * passes `<workspace>/.pd/examiner`. `PD_EXAMINER_STUDIES_ROOT` overrides the
76
+ * WRITABLE root only (tests / operators). The historical research assets ship
77
+ * with the lab payload (absent in an installation → an empty read-only list).
78
+ */
79
+ export function resolveStudyRoots(labRoot, dataRoot) {
74
80
  const override = process.env.PD_EXAMINER_STUDIES_ROOT;
75
- const writable = override ? path.resolve(override) : path.join(path.resolve(labRoot), 'examiner-console-studies');
81
+ const writable = override ? path.resolve(override) : path.join(path.resolve(dataRoot), 'studies');
76
82
  const historical = path.join(path.resolve(labRoot), 'examiner-studies');
77
83
  // Canonical forms when resolvable; the unresolved form is kept so the
78
84
  // boundary gates below can fail closed with a stable reason.
@@ -571,7 +577,7 @@ export async function materializeConsoleStudy(modules, labRoot, roots, selection
571
577
  * never real-host behavior.
572
578
  */
573
579
  export async function materializeOfflineStudy(modules, labRoot, inputs) {
574
- const fixtureUrl = pathToFileURL(path.join(labRoot, '..', '..', '__tests__', 'fixtures', 'examiner-v07.mjs')).href;
580
+ const fixtureUrl = pathToFileURL(path.join(labRoot, 'lib', 'examiner-v07-cases.mjs')).href;
575
581
  const fixture = (await import(fixtureUrl));
576
582
  const freezeUrl = pathToFileURL(path.join(labRoot, 'lib', 'challenge-freeze.mjs')).href;
577
583
  const freeze = (await import(freezeUrl));
@@ -3,10 +3,13 @@
3
3
  *
4
4
  * Product entry for the Examiner research pipeline on top of the existing
5
5
  * principle-learning-lab (single research engine — no parallel Runner/Ledger/
6
- * Scorer/Report). This round is authorized for the LOCAL DEV environment
7
- * only: the lab lives in the repository under scripts/dev/, so in an
8
- * installed runtime the routes answer 503 `examiner_dev_tooling_unavailable`
9
- * instead of pretending.
6
+ * Scorer/Report). The lab runtime subset ships WITH the console payload
7
+ * (`console/lab`), so this surface is available in an installed runtime; a
8
+ * developer checkout is served from the repository's scripts/dev/ lab.
9
+ *
10
+ * Offline only: every study carries SYNTHETIC_OFFLINE gold and is executed by
11
+ * the deterministic local stub. Real model calls and real host runs stay
12
+ * behind the Owner-authorization / Stage R gate and are refused here.
10
13
  *
11
14
  * Surface (all offline, zero real model/host calls):
12
15
  * GET /api/v1/examiner/overview — gates, host capability, study list
@@ -26,21 +29,24 @@
26
29
  * the experiment mode as a boolean. No request string crosses into the
27
30
  * research pipeline, and no request value constructs a path.
28
31
  *
29
- * Two roots (P1-1): NEW console studies are written under
30
- * `examiner-console-studies/` and require the server's own ownership marker;
31
- * the committed `examiner-studies/` research assets are visible but ALWAYS
32
- * read-only here (POST /run and POST /report answer 403). The frozen
33
- * `challenge-studies/` area is never a write target. Real-host execution is
34
- * refused by this route class itself.
32
+ * Two roots (P1-1): NEW console studies are WORKSPACE DATA — written under
33
+ * `<workspace>/.pd/examiner/studies` — and require the server's own ownership
34
+ * marker; the committed `examiner-studies/` research assets that travel with
35
+ * the lab payload are visible but ALWAYS read-only here (POST /run and
36
+ * POST /report answer 403). The frozen `challenge-studies/` area is never a
37
+ * write target. Real-host execution is refused by this route class itself.
35
38
  */
36
39
  import type { IncomingMessage, ServerResponse } from 'node:http';
37
40
  /**
38
- * P2-1: the Examiner is a DEV-ONLY capability. The gate is SERVER-SIDE and
39
- * cannot be set by a client — it reads only the running module's own URL and
40
- * the operator's environment: a source run (tsx/vitest, import.meta.url ends
41
- * with .ts) or an explicit `PD_EXAMINER_DEV=1`. `NODE_ENV=production` is an
42
- * UNCONDITIONAL hard gate: not even the DEV override can enable the surface
43
- * there, so a production build (`dist/server.js`) answers 404 — never 503.
41
+ * Locate the principle-learning-lab payload. Two layouts are legitimate and
42
+ * both are resolved from THIS module's own location — never from a request:
43
+ *
44
+ * installed <console>/lab (shipped by the installer)
45
+ * repository <repo>/scripts/dev/principle-learning-lab
46
+ *
47
+ * `PD_EXAMINER_LAB_ROOT` overrides both (operators / tests). A candidate only
48
+ * counts when the lab entry module is actually present, so a half-copied
49
+ * payload fails closed into the 503 branch instead of half-working.
44
50
  */
45
- export declare function isExaminerDevRuntime(moduleUrl: string, env?: Record<string, string | undefined>): boolean;
51
+ export declare function resolveLabRoot(moduleUrl: string, env?: Record<string, string | undefined>): string | null;
46
52
  export declare function handleExaminerRoute(req: IncomingMessage, res: ServerResponse, workspaceDir: string, subPath: string): Promise<void>;
@@ -1,32 +1,34 @@
1
+ import fs from 'node:fs';
1
2
  import path from 'node:path';
2
3
  import { fileURLToPath } from 'node:url';
3
4
  import { sendSuccess, sendError, sendNotFound } from '../utils/response.js';
4
5
  import { PrinciplesConsoleModel } from '../models/PrinciplesConsoleModel.js';
5
6
  import { loadLabModules, loadHostCapability, preflightCheck, offlineStudyPass, reportMaterialize, listStudyNames, resolveStudyAccess, resolveStudyRoots, studyStatusPayload, studyReportPayload, materializeConsoleStudy, verifyConsoleOwnership, writableRootProblem, isJsonObject, ExaminerPreflightBlockedError, } from './examiner-executor.js';
6
7
  /* eslint-disable @typescript-eslint/max-params -- node:http handler signature (req, res, workspaceDir, subPath) matches every other route */
7
- const here = path.dirname(fileURLToPath(import.meta.url));
8
- const repoRoot = path.resolve(here, '..', '..', '..', '..', '..');
9
- const labRoot = path.join(repoRoot, 'scripts', 'dev', 'principle-learning-lab');
8
+ const LAB_ENTRY = path.join('lib', 'examiner-study.mjs');
10
9
  const PRINCIPLE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
11
10
  const STUDY_ROUTE_PATTERN = /^\/studies\/([A-Za-z0-9-]+)(\/[^/]*)?$/;
12
11
  /**
13
- * P2-1: the Examiner is a DEV-ONLY capability. The gate is SERVER-SIDE and
14
- * cannot be set by a client — it reads only the running module's own URL and
15
- * the operator's environment: a source run (tsx/vitest, import.meta.url ends
16
- * with .ts) or an explicit `PD_EXAMINER_DEV=1`. `NODE_ENV=production` is an
17
- * UNCONDITIONAL hard gate: not even the DEV override can enable the surface
18
- * there, so a production build (`dist/server.js`) answers 404 — never 503.
12
+ * Locate the principle-learning-lab payload. Two layouts are legitimate and
13
+ * both are resolved from THIS module's own location — never from a request:
14
+ *
15
+ * installed <console>/lab (shipped by the installer)
16
+ * repository <repo>/scripts/dev/principle-learning-lab
17
+ *
18
+ * `PD_EXAMINER_LAB_ROOT` overrides both (operators / tests). A candidate only
19
+ * counts when the lab entry module is actually present, so a half-copied
20
+ * payload fails closed into the 503 branch instead of half-working.
19
21
  */
20
- export function isExaminerDevRuntime(moduleUrl, env = process.env) {
21
- if (env.NODE_ENV === 'production')
22
- return false;
23
- if (env.PD_EXAMINER_DEV === '1')
24
- return true;
25
- return moduleUrl.endsWith('.ts');
26
- }
27
- function examinerDevEnabled() {
28
- return isExaminerDevRuntime(import.meta.url);
22
+ export function resolveLabRoot(moduleUrl, env = process.env) {
23
+ const here = path.dirname(fileURLToPath(moduleUrl));
24
+ const candidates = [
25
+ env.PD_EXAMINER_LAB_ROOT ? path.resolve(env.PD_EXAMINER_LAB_ROOT) : null,
26
+ path.resolve(here, '..', '..', '..', 'lab'),
27
+ path.resolve(here, '..', '..', '..', '..', '..', 'scripts', 'dev', 'principle-learning-lab'),
28
+ ].filter((candidate) => candidate !== null);
29
+ return candidates.find((candidate) => fs.existsSync(path.join(candidate, LAB_ENTRY))) ?? null;
29
30
  }
31
+ const labRoot = resolveLabRoot(import.meta.url);
30
32
  /** The runner's lock-time re-verification refusal (dynamic module: match the
31
33
  * contract name, not an instanceof across import boundaries). */
32
34
  function isFrozenStudyDriftError(error) {
@@ -64,6 +66,10 @@ let labUnavailableReason = null;
64
66
  async function loadLab() {
65
67
  if (hostCapability)
66
68
  return hostCapability;
69
+ if (!labRoot) {
70
+ labUnavailableReason = 'the principle-learning-lab payload was not found (expected <console>/lab or the repository scripts/dev lab)';
71
+ return null;
72
+ }
67
73
  try {
68
74
  labModules = await loadLabModules(labRoot);
69
75
  hostCapability = await loadHostCapability(labRoot);
@@ -99,17 +105,13 @@ function readBody(req) {
99
105
  });
100
106
  }
101
107
  export async function handleExaminerRoute(req, res, workspaceDir, subPath) {
102
- if (!examinerDevEnabled()) {
103
- sendError(res, 404, 'examiner_not_enabled', 'The Examiner research surface is a local-dev capability and is not enabled in this runtime');
104
- return;
105
- }
106
108
  const lab = await loadLab();
107
- if (!lab || !labModules) {
108
- sendError(res, 503, 'examiner_dev_tooling_unavailable', `Examiner dev tooling is not available in this installation (the principle-learning-lab lives in the repository's scripts/dev area): ${labUnavailableReason ?? 'unknown reason'}`);
109
+ if (!lab || !labModules || !labRoot) {
110
+ sendError(res, 503, 'examiner_lab_unavailable', `the Examiner research engine could not be loaded from this installation: ${labUnavailableReason ?? 'unknown reason'}`);
109
111
  return;
110
112
  }
111
113
  const method = req.method ?? 'GET';
112
- const roots = resolveStudyRoots(labRoot);
114
+ const roots = resolveStudyRoots(labRoot, path.join(workspaceDir, '.pd', 'examiner'));
113
115
  const context = { labRoot, roots };
114
116
  const rootProblem = writableRootProblem(roots);
115
117
  // GET /overview
@@ -118,17 +120,18 @@ export async function handleExaminerRoute(req, res, workspaceDir, subPath) {
118
120
  const historical = listStudyNames(roots.historical);
119
121
  sendSuccess(res, {
120
122
  spec: 'SPEC-PD-EXAMINER-001 v0.9',
121
- executionScope: 'offline-stub only — real model calls and real host runs are NOT authorized this round',
123
+ executionScope: 'offline-stub only — every study uses SYNTHETIC_OFFLINE gold and the deterministic local stub; this panel validates the research contract, never real host behavior',
122
124
  gates: {
123
125
  gate1_challenge_trust: 'BLOCKED — independent human Gold review has not been performed for any Examiner suite (code tests can never substitute)',
124
126
  gate2_behavior_evaluation: 'NOT_READY — requires Gate 1 + real-host authorization',
125
127
  gate3_llm_authoring: 'NOT_STARTED',
126
- gate4_product_integration: 'NOT_STARTED (local-dev console integration; the production UI/API is disabled)',
128
+ gate4_product_integration: 'ACTIVE — the offline research panel (create / run / report) ships with the console; real model calls and real host runs stay refused under this gate',
127
129
  },
128
130
  hostInjectionCapability: lab,
129
131
  fullConclusionMinimumLevel: 'CONTEXT_PRESENT',
130
132
  writableStudiesRoot: roots.writable,
131
133
  historicalStudiesRoot: roots.historical,
134
+ studiesAreWorkspaceData: true,
132
135
  rootProblem,
133
136
  studies: [...writable, ...historical.filter((name) => !writable.includes(name))],
134
137
  readOnlyStudies: [
@@ -165,7 +165,7 @@ function AuthRoutes() {
165
165
  }, []);
166
166
  return (_jsxs(Routes, { children: [_jsx(Route, { path: "/splash", element: _jsx(SplashScreen, { onComplete: () => {
167
167
  setShowSplash(false);
168
- } }) }), _jsx(Route, { path: "/login", element: _jsx(LoginForm, { onAuthSuccess: handleAuthSuccess }) }), _jsx(Route, { path: "/*", element: authed === null ? (_jsx("div", { className: "min-h-screen bg-paper flex items-center justify-center", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsx("div", { className: "w-8 h-8 border-2 border-gov border-t-transparent rounded-full animate-spin" }), _jsx("span", { className: "text-[12px] font-mono tracking-wider text-ink-3 uppercase", children: "Verifying Session..." })] }) })) : authed ? (_jsx(NotificationProvider, { children: _jsx(AuthenticatedShell, { featureFlags: featureFlags ?? undefined, children: _jsx(ErrorBoundary, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Navigate, { to: "/focus", replace: true }) }), _jsx(Route, { path: "/welcome", element: _jsx(WelcomePage, { workspaceId: currentWorkspaceId }) }), _jsx(Route, { path: "/focus", element: _jsx(FocusPage, { featureFlags: featureFlags ?? undefined }) }), _jsx(Route, { path: "/pain", element: _jsx(PainPage, {}) }), _jsx(Route, { path: "/principles", element: _jsx(PrinciplesPage, {}) }), _jsx(Route, { path: "/principles/core", element: _jsx(CorePrinciplesPage, {}) }), _jsx(Route, { path: "/principles/:id", element: _jsx(PrincipleDetailPage, {}) }), _jsx(Route, { path: "/activation", element: _jsx(ActivationPage, {}) }), _jsx(Route, { path: "/debt", element: _jsx(DebtPage, {}) }), IS_DEV ? (_jsx(Route, { path: "/examiner", element: _jsx(ExaminerPage, {}) })) : (_jsx(Route, { path: "/examiner", element: _jsx(Navigate, { to: "/focus", replace: true }) })), _jsx(Route, { path: "/control-center", element: _jsx(ControlCenterPage, {}) }), _jsx(Route, { path: "/settings", element: _jsx(SettingsPage, {}) }), _jsx(Route, { path: "/update", element: _jsx(UpdatePage, {}) }), _jsx(Route, { path: "/report-problem", element: _jsx(ReportProblemPage, {}) }), _jsx(Route, { path: "/failed-tasks", element: _jsx(FailedTasksPage, {}) }), _jsx(Route, { path: "/intent", element: _jsx(IntentPage, {}) }), IS_DEV && (_jsx(Route, { path: "/design-system", element: _jsx(DesignSystemPage, {}) })), !IS_DEV && (_jsx(Route, { path: "/design-system", element: _jsx(Navigate, { to: "/focus", replace: true }) }))] }) }) }) })) : (_jsx(Navigate, { to: "/login", replace: true })) })] }));
168
+ } }) }), _jsx(Route, { path: "/login", element: _jsx(LoginForm, { onAuthSuccess: handleAuthSuccess }) }), _jsx(Route, { path: "/*", element: authed === null ? (_jsx("div", { className: "min-h-screen bg-paper flex items-center justify-center", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsx("div", { className: "w-8 h-8 border-2 border-gov border-t-transparent rounded-full animate-spin" }), _jsx("span", { className: "text-[12px] font-mono tracking-wider text-ink-3 uppercase", children: "Verifying Session..." })] }) })) : authed ? (_jsx(NotificationProvider, { children: _jsx(AuthenticatedShell, { featureFlags: featureFlags ?? undefined, children: _jsx(ErrorBoundary, { children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(Navigate, { to: "/focus", replace: true }) }), _jsx(Route, { path: "/welcome", element: _jsx(WelcomePage, { workspaceId: currentWorkspaceId }) }), _jsx(Route, { path: "/focus", element: _jsx(FocusPage, { featureFlags: featureFlags ?? undefined }) }), _jsx(Route, { path: "/pain", element: _jsx(PainPage, {}) }), _jsx(Route, { path: "/principles", element: _jsx(PrinciplesPage, {}) }), _jsx(Route, { path: "/principles/core", element: _jsx(CorePrinciplesPage, {}) }), _jsx(Route, { path: "/principles/:id", element: _jsx(PrincipleDetailPage, {}) }), _jsx(Route, { path: "/activation", element: _jsx(ActivationPage, {}) }), _jsx(Route, { path: "/debt", element: _jsx(DebtPage, {}) }), _jsx(Route, { path: "/examiner", element: _jsx(ExaminerPage, {}) }), _jsx(Route, { path: "/control-center", element: _jsx(ControlCenterPage, {}) }), _jsx(Route, { path: "/settings", element: _jsx(SettingsPage, {}) }), _jsx(Route, { path: "/update", element: _jsx(UpdatePage, {}) }), _jsx(Route, { path: "/report-problem", element: _jsx(ReportProblemPage, {}) }), _jsx(Route, { path: "/failed-tasks", element: _jsx(FailedTasksPage, {}) }), _jsx(Route, { path: "/intent", element: _jsx(IntentPage, {}) }), IS_DEV && (_jsx(Route, { path: "/design-system", element: _jsx(DesignSystemPage, {}) })), !IS_DEV && (_jsx(Route, { path: "/design-system", element: _jsx(Navigate, { to: "/focus", replace: true }) }))] }) }) }) })) : (_jsx(Navigate, { to: "/login", replace: true })) })] }));
169
169
  }
170
170
  export function App() {
171
171
  return (_jsx(ThemeProvider, { children: _jsxs(I18nextProvider, { i18n: i18n, children: [_jsx(HashRouter, { children: _jsx(AuthRoutes, {}) }), _jsx(Toaster, {})] }) }));
@@ -1,10 +1,9 @@
1
1
  import * as React from "react";
2
2
  /**
3
- * P2-1: the Examiner is a DEV-only research surface. The navigation entry is
4
- * derived from the build-time mode, so a production bundle never advertises a
5
- * capability the server refuses.
3
+ * The Examiner is a shipped product surface: the offline research panel ships
4
+ * with the console payload, so the navigation entry is build-mode independent.
6
5
  */
7
- export declare function visibleMainNavItems(isDev: boolean): {
6
+ export declare function visibleMainNavItems(): {
8
7
  id: string;
9
8
  labelKey: string;
10
9
  href: string;
@@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
7
7
  import { useNotifications } from "../notifications/useNotifications.js";
8
8
  import { clearToken } from "../../api.js";
9
9
  import { cn } from "../../../lib/utils.js";
10
- import { IS_DEV } from "../../utils/runtime-mode.js";
11
10
  import { Sheet, SheetContent, SheetDescription, SheetTitle, SheetTrigger } from "../ui/sheet.js";
12
11
  function ThresholdMark({ className }) {
13
12
  return (_jsxs("svg", { viewBox: "0 0 28 28", className: className, fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: [_jsx("path", { d: "M6 4V24M22 4V24M2 14H26", strokeLinecap: "square" }), _jsx("circle", { cx: "14", cy: "14", r: "2.5", fill: "currentColor", stroke: "none" })] }));
@@ -22,12 +21,11 @@ const mainNavItems = [
22
21
  { id: "intent", labelKey: "components.sidebar.intent", href: "/intent", icon: Compass },
23
22
  ];
24
23
  /**
25
- * P2-1: the Examiner is a DEV-only research surface. The navigation entry is
26
- * derived from the build-time mode, so a production bundle never advertises a
27
- * capability the server refuses.
24
+ * The Examiner is a shipped product surface: the offline research panel ships
25
+ * with the console payload, so the navigation entry is build-mode independent.
28
26
  */
29
- export function visibleMainNavItems(isDev) {
30
- return isDev ? mainNavItems : mainNavItems.filter((item) => item.id !== "examiner");
27
+ export function visibleMainNavItems() {
28
+ return mainNavItems;
31
29
  }
32
30
  const toolNavItems = [
33
31
  { id: "control-center", labelKey: "components.sidebar.controlCenter", href: "/control-center", icon: Settings },
@@ -61,7 +59,7 @@ function SidebarContent({ featureFlags, onNavigate, touchTargets, }) {
61
59
  i18n.changeLanguage(nextLang);
62
60
  localStorage.setItem("pd-language", nextLang);
63
61
  };
64
- return (_jsxs(_Fragment, { children: [_jsxs("nav", { className: "flex-1 py-2 overflow-y-auto", "aria-label": t("components.sidebar.navigation", { defaultValue: "主导航" }), children: [_jsx("div", { className: "px-2", children: visibleMainNavItems(IS_DEV).map((item) => {
62
+ return (_jsxs(_Fragment, { children: [_jsxs("nav", { className: "flex-1 py-2 overflow-y-auto", "aria-label": t("components.sidebar.navigation", { defaultValue: "主导航" }), children: [_jsx("div", { className: "px-2", children: visibleMainNavItems().map((item) => {
65
63
  const Icon = item.icon;
66
64
  const active = isActive(item.href);
67
65
  return (_jsxs(Link, { to: item.href, "data-testid": `nav-${item.id}`, "aria-current": active ? "page" : undefined, onClick: onNavigate, className: cn("flex items-center gap-3 px-3 rounded-[var(--radius-sm)] text-[13px] transition-all duration-150", touchTargets ? "min-h-[44px] py-2" : "py-2", active
@@ -2076,7 +2076,7 @@
2076
2076
  "runDone": "Offline execution finished: {{count}} units succeeded.",
2077
2077
  "reportGenerated": "Report generated ({{status}}).",
2078
2078
  "errors": {
2079
- "unavailable": "Examiner dev tooling is unavailable in this installation.",
2079
+ "unavailable": "The Examiner research engine is unavailable in this installation (the installer payload does not include the research modules).",
2080
2080
  "create": "Failed to create the study.",
2081
2081
  "preflight": "Preflight failed.",
2082
2082
  "run": "Offline execution failed.",
@@ -2076,7 +2076,7 @@
2076
2076
  "runDone": "离线执行完成:{{count}} 个单元成功。",
2077
2077
  "reportGenerated": "报告已生成({{status}})。",
2078
2078
  "errors": {
2079
- "unavailable": "Examiner 开发工具在此安装中不可用。",
2079
+ "unavailable": "Examiner 研究引擎在此安装中不可用(安装包未包含研究模块)。",
2080
2080
  "create": "创建 Study 失败。",
2081
2081
  "preflight": "预检失败。",
2082
2082
  "run": "离线执行失败。",
@@ -5,6 +5,7 @@
5
5
  * a production bundle resolves this to `false` and dead-code elimination can
6
6
  * drop the DEV-only surfaces from the shipped UI. This is a trust boundary in
7
7
  * the CLIENT only — the server independently refuses the Examiner API outside
8
- * a dev runtime (see `examinerDevEnabled` in the examiner route).
8
+ * a dev runtime (see `resolveLabRoot` / the offline-only gates in the examiner
9
+ * route).
9
10
  */
10
11
  export declare const IS_DEV: boolean;
@@ -5,6 +5,7 @@
5
5
  * a production bundle resolves this to `false` and dead-code elimination can
6
6
  * drop the DEV-only surfaces from the shipped UI. This is a trust boundary in
7
7
  * the CLIENT only — the server independently refuses the Examiner API outside
8
- * a dev runtime (see `examinerDevEnabled` in the examiner route).
8
+ * a dev runtime (see `resolveLabRoot` / the offline-only gates in the examiner
9
+ * route).
9
10
  */
10
11
  export const IS_DEV = import.meta.env.DEV;