@wenathlan/cobaia 2.0.167 → 2.0.168

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/db.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  * tables over HTTPS (GET/POST /api/v1/onboarding, GET /api/v1/ui/content);
9
9
  * no server route lives here.
10
10
  */
11
+ import { createOnboardingStore } from "@wenathlan/vault/onboardingstore";
11
12
  import { openStorageLane } from "@wenathlan/vault/storage";
12
13
  import { CONTENTKINDS, type contentrow, seedAdsLabel, seedAdsEmpty, seedAdsSlots, seedPlans, seedPosts, seedRoster, seedSupportLinks } from "./content";
13
14
  import { LABEL_MANIFEST, LABEL_REVISION } from "./labels";
@@ -70,6 +71,12 @@ export type OnboardingStateRow = {
70
71
  /** the stage flags the entry flow consults, as booleans. */
71
72
  export type OnboardingFlags = { introseen: boolean; onboardingseen: boolean };
72
73
 
74
+ /** the onboarding store of the site: the vault factory over the site's own
75
+ * storage lane (the seen-flags table rides the app schema, the read and the
76
+ * seen-wins upsert ride the shared mechanism — never duplicated inside the
77
+ * app; the storage lane law of the family, the labelstore precedent). */
78
+ const onboardingstore = createOnboardingStore(lane);
79
+
73
80
  /**
74
81
  * Reads the onboarding stage flags of one visitor (parameterized; undefined
75
82
  * before the first write). The browser accessor serves the same flags over
@@ -79,17 +86,13 @@ export type OnboardingFlags = { introseen: boolean; onboardingseen: boolean };
79
86
  * @returns the flags, or undefined when the visitor never wrote.
80
87
  */
81
88
  export function getOnboardingState(visitor: string): OnboardingFlags | undefined {
82
- const row = queryOne<Pick<OnboardingStateRow, "introseen" | "onboardingseen">>(
83
- "SELECT introseen, onboardingseen FROM onboarding_states WHERE visitor = ?",
84
- [visitor],
85
- );
86
- if (!row) return undefined;
87
- return { introseen: row.introseen === 1, onboardingseen: row.onboardingseen === 1 };
89
+ return onboardingstore.getOnboardingState(visitor);
88
90
  }
89
91
 
90
92
  /**
91
- * Marks one onboarding stage seen for a visitor — idempotent seen-wins
92
- * upsert (a stage seen anywhere stays seen), prepared and parameterized,
93
+ * Marks one onboarding stage seen for a visitor — the idempotent seen-wins
94
+ * upsert of the vault store (a stage seen anywhere stays seen; the later
95
+ * stage implies the earlier — the flow order), prepared and parameterized,
93
96
  * never concatenated SQL.
94
97
  *
95
98
  * @param visitor the visitor key of the row.
@@ -97,15 +100,7 @@ export function getOnboardingState(visitor: string): OnboardingFlags | undefined
97
100
  * @returns the flags after the write.
98
101
  */
99
102
  export function setOnboardingStage(visitor: string, stage: "intro" | "onboarding"): OnboardingFlags {
100
- execute(
101
- `INSERT INTO onboarding_states (visitor, introseen, onboardingseen, updatedAt)
102
- VALUES (?, ?, ?, datetime('now'))
103
- ON CONFLICT(visitor) DO UPDATE SET
104
- introseen = MAX(introseen, excluded.introseen),
105
- onboardingseen = MAX(onboardingseen, excluded.onboardingseen),
106
- updatedAt = datetime('now')`,
107
- [visitor, stage === "intro" ? 1 : 0, stage === "onboarding" ? 1 : 0],
108
- );
103
+ onboardingstore.setOnboardingStage(visitor, stage);
109
104
  return getOnboardingState(visitor) ?? { introseen: false, onboardingseen: false };
110
105
  }
111
106
 
@@ -29,7 +29,7 @@
29
29
 
30
30
  services:
31
31
  cobaia-engine:
32
- image: "ghcr.io/wenathlan/cobaia:${COBAIA_VERSION:-2.0.166}"
32
+ image: "ghcr.io/wenathlan/cobaia:${COBAIA_VERSION:-2.0.167}"
33
33
  # the engine never publishes a port by default: the face proxies it on
34
34
  # the private compose network (the vault precedent). uncomment to serve
35
35
  # the engine lane directly behind your own door:
@@ -41,7 +41,7 @@ services:
41
41
  restart: unless-stopped
42
42
 
43
43
  cobaia-face:
44
- image: "ghcr.io/wenathlan/cobaia:${COBAIA_VERSION:-2.0.166}"
44
+ image: "ghcr.io/wenathlan/cobaia:${COBAIA_VERSION:-2.0.167}"
45
45
  volumes:
46
46
  # the family conf over the default site conf: the resolver law, the
47
47
  # security headers, the gzip and cache law, the /api proxy lane.
package/homebrew.rb ADDED
@@ -0,0 +1,25 @@
1
+ # wenathlan-cobaia — the homebrew formula of the family (the homebrew channel).
2
+ # The lane stamps the version and sha256 at push time (the sums derive from
3
+ # the release tarball assets the release lane builds, never hardcoded).
4
+ class WenathlanCobaia < Formula
5
+ desc "The monetization surface — the plans, the support rails and the honest advertisement slots"
6
+ homepage "https://github.com/wenathlan/devthink"
7
+ url "https://github.com/wenathlan/devthink/releases/download/v2.0.167/wenathlan-cobaia-2.0.167.tgz"
8
+ version "2.0.167"
9
+ # sha256 stamped by the lane from the release asset (never hardcoded)
10
+ sha256 "0000000000000000000000000000000000000000000000000000000000000000"
11
+
12
+ depends_on "node"
13
+
14
+ def install
15
+ libexec.install Dir["*"]
16
+ (bin/"wenathlan-cobaia").write <<~SH
17
+ #!/bin/bash
18
+ exec "#{libexec}/cli.js" "$@"
19
+ SH
20
+ end
21
+
22
+ test do
23
+ assert_match "2.0.167", shell_output("#{bin}/wenathlan-cobaia --version")
24
+ end
25
+ end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wenathlan/cobaia",
3
- "version": "2.0.167",
3
+ "version": "2.0.168",
4
4
  "private": false,
5
5
  "description": "cobaia is the monetization surface of the wenathlan family: the content site that carries the plans, the support rails and the honest advertisement slots while the family apps stay clean; the name is provisional until the owner picks the official one",
6
6
  "license": "GPL-3.0-only",
@@ -14,9 +14,9 @@
14
14
  "test": "vitest run"
15
15
  },
16
16
  "dependencies": {
17
- "@ai-sdk/openai": "^4.0.110",
18
- "@ai-sdk/openai-compatible": "^3.0.86",
19
- "@ai-sdk/provider-utils": "^5.0.75",
17
+ "@ai-sdk/openai": "^4.0.111",
18
+ "@ai-sdk/openai-compatible": "^3.0.87",
19
+ "@ai-sdk/provider-utils": "^5.0.76",
20
20
  "@anthropic-ai/sdk": "^1.6.0",
21
21
  "@biomejs/biome": "^2.6.6",
22
22
  "@capacitor/android": "^8.6.0",
@@ -86,15 +86,15 @@
86
86
  "@types/react-dom": "^19.3.0",
87
87
  "@types/three": "^0.187.0",
88
88
  "@vitejs/plugin-react": "^6.2.1",
89
- "@wenathlan/argan": "~2.0.166",
90
- "@wenathlan/cadria": "~2.0.166",
91
- "@wenathlan/debonair": "~2.0.166",
92
- "@wenathlan/devthink": "~2.0.166",
93
- "@wenathlan/getry": "~2.0.166",
94
- "@wenathlan/saddle": "~2.0.166",
95
- "@wenathlan/vault": "~2.0.166",
89
+ "@wenathlan/argan": "~2.0.167",
90
+ "@wenathlan/cadria": "~2.0.167",
91
+ "@wenathlan/debonair": "~2.0.167",
92
+ "@wenathlan/devthink": "~2.0.167",
93
+ "@wenathlan/getry": "~2.0.167",
94
+ "@wenathlan/saddle": "~2.0.167",
95
+ "@wenathlan/vault": "~2.0.167",
96
96
  "add": "^2.0.9",
97
- "ai": "^7.0.164",
97
+ "ai": "^7.0.165",
98
98
  "autoprefixer": "^10.6.2",
99
99
  "axios": "^1.21.0",
100
100
  "baseline-browser-mapping": "^2.11.35",
@@ -105,7 +105,7 @@
105
105
  "class-variance-authority": "^0.7.1",
106
106
  "clsx": "^2.1.1",
107
107
  "cmdk": "^1.1.1",
108
- "commander": "^15.0.0",
108
+ "commander": "^15.1.0",
109
109
  "date-fns": "^4.4.0",
110
110
  "deepmerge-ts": "^8.0.2",
111
111
  "dockerode": "^5.0.1",
@@ -123,13 +123,12 @@
123
123
  "framer-motion": "^14.10.1",
124
124
  "glob": "^14.0.4",
125
125
  "gsap": "^3.15.1",
126
- "hono": "^4.13.16",
126
+ "hono": "^4.13.17",
127
127
  "ink": "^8.0.0",
128
128
  "input-otp": "^1.5.0",
129
129
  "js-yaml": "^5.5.0",
130
130
  "jsdom": "^31.0.0",
131
131
  "katex": "^0.19.1",
132
- "lucide-react": "^1.70.0",
133
132
  "minimatch": "^11.0.5",
134
133
  "motion": "^14.10.1",
135
134
  "mysql2": "^3.24.6",
@@ -188,9 +187,9 @@
188
187
  "zustand": "^5.1.0"
189
188
  },
190
189
  "devDependencies": {
191
- "@ai-sdk/openai": "^4.0.110",
192
- "@ai-sdk/openai-compatible": "^3.0.86",
193
- "@ai-sdk/provider-utils": "^5.0.75",
190
+ "@ai-sdk/openai": "^4.0.111",
191
+ "@ai-sdk/openai-compatible": "^3.0.87",
192
+ "@ai-sdk/provider-utils": "^5.0.76",
194
193
  "@anthropic-ai/sdk": "^1.6.0",
195
194
  "@biomejs/biome": "^2.6.6",
196
195
  "@capacitor/android": "^8.6.0",
@@ -260,15 +259,15 @@
260
259
  "@types/react-dom": "^19.3.0",
261
260
  "@types/three": "^0.187.0",
262
261
  "@vitejs/plugin-react": "^6.2.1",
263
- "@wenathlan/argan": "~2.0.166",
264
- "@wenathlan/cadria": "~2.0.166",
265
- "@wenathlan/debonair": "~2.0.166",
266
- "@wenathlan/devthink": "~2.0.166",
267
- "@wenathlan/getry": "~2.0.166",
268
- "@wenathlan/saddle": "~2.0.166",
269
- "@wenathlan/vault": "~2.0.166",
262
+ "@wenathlan/argan": "~2.0.167",
263
+ "@wenathlan/cadria": "~2.0.167",
264
+ "@wenathlan/debonair": "~2.0.167",
265
+ "@wenathlan/devthink": "~2.0.167",
266
+ "@wenathlan/getry": "~2.0.167",
267
+ "@wenathlan/saddle": "~2.0.167",
268
+ "@wenathlan/vault": "~2.0.167",
270
269
  "add": "^2.0.9",
271
- "ai": "^7.0.164",
270
+ "ai": "^7.0.165",
272
271
  "autoprefixer": "^10.6.2",
273
272
  "axios": "^1.21.0",
274
273
  "baseline-browser-mapping": "^2.11.35",
@@ -279,7 +278,7 @@
279
278
  "class-variance-authority": "^0.7.1",
280
279
  "clsx": "^2.1.1",
281
280
  "cmdk": "^1.1.1",
282
- "commander": "^15.0.0",
281
+ "commander": "^15.1.0",
283
282
  "date-fns": "^4.4.0",
284
283
  "deepmerge-ts": "^8.0.2",
285
284
  "dockerode": "^5.0.1",
@@ -297,13 +296,12 @@
297
296
  "framer-motion": "^14.10.1",
298
297
  "glob": "^14.0.4",
299
298
  "gsap": "^3.15.1",
300
- "hono": "^4.13.16",
299
+ "hono": "^4.13.17",
301
300
  "ink": "^8.0.0",
302
301
  "input-otp": "^1.5.0",
303
302
  "js-yaml": "^5.5.0",
304
303
  "jsdom": "^31.0.0",
305
304
  "katex": "^0.19.1",
306
- "lucide-react": "^1.70.0",
307
305
  "minimatch": "^11.0.5",
308
306
  "motion": "^14.10.1",
309
307
  "mysql2": "^3.24.6",
@@ -362,9 +360,9 @@
362
360
  "zustand": "^5.1.0"
363
361
  },
364
362
  "overrides": {
365
- "@ai-sdk/openai": "^4.0.110",
366
- "@ai-sdk/openai-compatible": "^3.0.86",
367
- "@ai-sdk/provider-utils": "^5.0.75",
363
+ "@ai-sdk/openai": "^4.0.111",
364
+ "@ai-sdk/openai-compatible": "^3.0.87",
365
+ "@ai-sdk/provider-utils": "^5.0.76",
368
366
  "@anthropic-ai/sdk": "^1.6.0",
369
367
  "@biomejs/biome": "^2.6.6",
370
368
  "@capacitor/android": "^8.6.0",
@@ -434,15 +432,15 @@
434
432
  "@types/react-dom": "^19.3.0",
435
433
  "@types/three": "^0.187.0",
436
434
  "@vitejs/plugin-react": "^6.2.1",
437
- "@wenathlan/argan": "~2.0.166",
438
- "@wenathlan/cadria": "~2.0.166",
439
- "@wenathlan/debonair": "~2.0.166",
440
- "@wenathlan/devthink": "~2.0.166",
441
- "@wenathlan/getry": "~2.0.166",
442
- "@wenathlan/saddle": "~2.0.166",
443
- "@wenathlan/vault": "~2.0.166",
435
+ "@wenathlan/argan": "~2.0.167",
436
+ "@wenathlan/cadria": "~2.0.167",
437
+ "@wenathlan/debonair": "~2.0.167",
438
+ "@wenathlan/devthink": "~2.0.167",
439
+ "@wenathlan/getry": "~2.0.167",
440
+ "@wenathlan/saddle": "~2.0.167",
441
+ "@wenathlan/vault": "~2.0.167",
444
442
  "add": "^2.0.9",
445
- "ai": "^7.0.164",
443
+ "ai": "^7.0.165",
446
444
  "autoprefixer": "^10.6.2",
447
445
  "axios": "^1.21.0",
448
446
  "baseline-browser-mapping": "^2.11.35",
@@ -453,7 +451,7 @@
453
451
  "class-variance-authority": "^0.7.1",
454
452
  "clsx": "^2.1.1",
455
453
  "cmdk": "^1.1.1",
456
- "commander": "^15.0.0",
454
+ "commander": "^15.1.0",
457
455
  "date-fns": "^4.4.0",
458
456
  "deepmerge-ts": "^8.0.2",
459
457
  "dockerode": "^5.0.1",
@@ -471,13 +469,12 @@
471
469
  "framer-motion": "^14.10.1",
472
470
  "glob": "^14.0.4",
473
471
  "gsap": "^3.15.1",
474
- "hono": "^4.13.16",
472
+ "hono": "^4.13.17",
475
473
  "ink": "^8.0.0",
476
474
  "input-otp": "^1.5.0",
477
475
  "js-yaml": "^5.5.0",
478
476
  "jsdom": "^31.0.0",
479
477
  "katex": "^0.19.1",
480
- "lucide-react": "^1.70.0",
481
478
  "minimatch": "^11.0.5",
482
479
  "motion": "^14.10.1",
483
480
  "mysql2": "^3.24.6",
@@ -48,7 +48,6 @@ minimumReleaseAgeExclude:
48
48
  - '@opencode-ai/sdk@1.19.5'
49
49
  - react-resizable-panels@4.15.1 || 4.15.2 || 4.15.3 || 4.16.0 || 4.16.1 || 4.17.0 || 4.18.0 || 4.18.1 || 4.19.0 || 4.19.1
50
50
  - '@types/google.maps@3.66.9 || 3.66.10'
51
- - lucide-react@1.64.0 || 1.65.0 || 1.66.0 || 1.67.0 || 1.68.0 || 1.69.0 || 1.70.0
52
51
  - playwright@1.65.0
53
52
  - playwright-core@1.65.0
54
53
  - '@anthropic-ai/sdk@1.6.0'
@@ -56,13 +55,13 @@ minimumReleaseAgeExclude:
56
55
  - '@tanstack/query-core@5.106.1 || 5.107.1'
57
56
  - '@tanstack/react-query@5.106.1 || 5.107.1'
58
57
  - openai@7.52.0 || 7.53.0
59
- - '@wenathlan/argan@2.0.155 || 2.0.165 || 2.0.166'
60
- - '@wenathlan/cadria@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8'
61
- - '@wenathlan/debonair@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8'
62
- - '@wenathlan/devthink@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.7'
63
- - '@wenathlan/getry@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166'
64
- - '@wenathlan/saddle@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8'
65
- - '@wenathlan/vault@2.0.152 || 2.0.153 || 2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8'
58
+ - '@wenathlan/argan@2.0.155 || 2.0.165 || 2.0.166 || 2.0.167'
59
+ - '@wenathlan/cadria@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8 || 2.0.167'
60
+ - '@wenathlan/debonair@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8 || 2.0.167'
61
+ - '@wenathlan/devthink@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.7 || 2.0.167'
62
+ - '@wenathlan/getry@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.0.167'
63
+ - '@wenathlan/saddle@2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8 || 2.0.167'
64
+ - '@wenathlan/vault@2.0.152 || 2.0.153 || 2.0.154 || 2.0.155 || 2.0.156 || 2.0.165 || 2.0.166 || 2.1.8 || 2.0.167'
66
65
  - jsdom@31.0.0
67
66
  - pnpm@12.18.0
68
67
  - "@pnpm/exe.linux-x64@12.18.0"
@@ -90,10 +89,10 @@ minimumReleaseAgeExclude:
90
89
  - tailwindcss@4.3.4 || 4.3.5
91
90
  - vitest@5.1.0
92
91
  - gsap@3.15.1
93
- - '@wenathlan/cobaia@2.0.166'
94
- - '@wenathlan/forge@2.0.166'
95
- - '@wenathlan/foundry@2.0.166'
96
- - '@wenathlan/stealthhead@2.0.166'
92
+ - '@wenathlan/cobaia@2.0.166 || 2.0.167'
93
+ - '@wenathlan/forge@2.0.166 || 2.0.167'
94
+ - '@wenathlan/foundry@2.0.166 || 2.0.167'
95
+ - '@wenathlan/stealthhead@2.0.166 || 2.0.167'
97
96
  - framer-motion@14.10.1
98
97
  - motion-dom@14.10.1
99
98
  - motion@14.10.1
package/pom.xml CHANGED
@@ -12,7 +12,7 @@
12
12
  <scm><url>https://github.com/wenathlan/devthink</url><connection>scm:git:https://github.com/wenathlan/devthink.git</connection></scm>
13
13
  <distributionManagement><repository><id>github</id><name>GitHub Packages</name><url>https://maven.pkg.github.com/wenathlan/devthink</url></repository></distributionManagement>
14
14
  <properties>
15
- <revision>2.0.167</revision>
15
+ <revision>2.0.168</revision>
16
16
  </properties>
17
17
  <build>
18
18
  <resources>
package/pyproject.toml ADDED
@@ -0,0 +1,23 @@
1
+ # cobaia — the python documentation envelope of the family (the pypi channel).
2
+ # Ships the same envelope the gem face packs: README, docs and tests.
3
+ # Built by the publish lane with `python -m build` (PEP 517); the version
4
+ # rides the COBAIA_VERSION environment the metadata lockstep stamps — the
5
+ # pyproject table cannot fetch an environment the gemspec reads, so the
6
+ # static version below is the fallback the lane sed-stamps before building.
7
+ [build-system]
8
+ requires = ["setuptools>=75"]
9
+ build-backend = "setuptools.build_meta"
10
+
11
+ [project]
12
+ name = "wenathlan-cobaia"
13
+ version = "2.0.167"
14
+ authors = [{name = "wenathlan"}]
15
+ description = "The monetization surface — the plans, the support rails and the honest advertisement slots"
16
+ readme = "README.md"
17
+ license = {text = "GPL-3.0-only"}
18
+ requires-python = ">=3.11"
19
+
20
+ # The envelope carries no python code — the empty package list keeps the
21
+ # build honest while the MANIFEST.in packs the envelope files.
22
+ [tool.setuptools]
23
+ packages = []
@@ -20,28 +20,24 @@
20
20
  // this module never judges engine vocabulary, so the mirror cannot drift
21
21
  // when the registry grows). The module never spawns anything and resolves
22
22
  // no join: it names the lanes' truth; the platform resolves the sandbox.
23
- // Zero consumer data, zero imports.
23
+ // Zero consumer data; the one import below is the owner's lane seat
24
+ // factory and nothing else enters.
25
+ //
26
+ // The engine seat (wave W5, the dedup law): the seat resolver, the env
27
+ // reader and the lane lookup no longer live in this file — they ride the
28
+ // saddle factory (laneseating of ../saddle/workloadlaw.ts, the owner's
29
+ // workload law) bound once to this app's own data, re-exported under the
30
+ // same names the surface always spoke. The rows stay mirrored (the drift
31
+ // pin the battery wants), only the engine moved: zero behavior change,
32
+ // zero public-surface change.
33
+
34
+ import { type AppWorkloadLane, type LaneSeatResult, laneseating } from "../saddle/workloadlaw.ts";
24
35
 
25
36
  /** one mirrored workload lane row — the same five keys the engine law pins
26
- * (app, lane, role, demand, today). */
27
- export type AppWorkloadLane = {
28
- /** the official app slug every row carries. */
29
- readonly app: string;
30
- /** the lane id inside the app (kebab-case, the engine catalog id). */
31
- readonly lane: string;
32
- /** what the lane does, in one sentence (the engine row's sentence). */
33
- readonly role: string;
34
- /** the demand the lane puts on the sandbox (gpu, network, isolation,
35
- * boot budget — never a modality name: the nuclear law). */
36
- readonly demand: {
37
- readonly gpu: boolean;
38
- readonly network: boolean;
39
- readonly isolation: "process" | "namespace" | "vm" | "hardware";
40
- readonly bootbudget?: number;
41
- };
42
- /** where the heavy pass runs today — the honest truth the census reads. */
43
- readonly today: "engine-lane" | "app-lane";
44
- };
37
+ * (app, lane, role, demand, today); the shape itself rides the owner's
38
+ * law now (wave W5 — saddle/workloadlaw.ts answers the type once),
39
+ * re-exported under the historical surface name. */
40
+ export type { AppWorkloadLane };
45
41
 
46
42
  /** the app slug every mirrored row carries. */
47
43
  export const APPSLUG = "cobaia";
@@ -83,10 +79,13 @@ export const SANDBOXLANEENV = "COBAIA_SANDBOX_LANE";
83
79
  * modality id the deploy named (validated by the platform's join at
84
80
  * dispatch, never judged here). every answer is honest: an absent or
85
81
  * blank choice answers the declared default, never a silent switch. */
86
- export type LaneSeatResult =
87
- | { ok: true; seat: "local"; sandboxed: false }
88
- | { ok: true; seat: "join"; sandboxed: true; pinned: false }
89
- | { ok: true; seat: string; sandboxed: true; pinned: true };
82
+ export type { LaneSeatResult };
83
+
84
+ /** the lane seat engine of this app — the saddle factory bound once to
85
+ * this app's own data (the dedup law: the engine lives in the owner, the
86
+ * app keeps its data), the seats re-exported below under the names the
87
+ * surface always spoke. */
88
+ const seating = laneseating({ slug: APPSLUG, envvar: SANDBOXLANEENV, lanes: WORKLOADLANES });
90
89
 
91
90
  /**
92
91
  * Resolves the sandbox lane seat of this application — the deploy's
@@ -101,16 +100,7 @@ export type LaneSeatResult =
101
100
  * @param choice the deploy's choice (the env value or the config row).
102
101
  * @returns the seat declaration.
103
102
  */
104
- export function lanesandbox(choice: string | undefined): LaneSeatResult {
105
- const name = choice?.trim() ?? "";
106
- if (name.length === 0 || name === "local") {
107
- return { ok: true, seat: "local", sandboxed: false };
108
- }
109
- if (name === "join") {
110
- return { ok: true, seat: "join", sandboxed: true, pinned: false };
111
- }
112
- return { ok: true, seat: name, sandboxed: true, pinned: true };
113
- }
103
+ export const lanesandbox = seating.lanesandbox;
114
104
 
115
105
  /**
116
106
  * Reads the lane seat off one environment lookup — the deploy pins
@@ -121,9 +111,7 @@ export function lanesandbox(choice: string | undefined): LaneSeatResult {
121
111
  * @param lookup the environment lookup function (the env carrier).
122
112
  * @returns the seat declaration.
123
113
  */
124
- export function lanesandboxfromenv(lookup: (key: string) => string | undefined): LaneSeatResult {
125
- return lanesandbox(lookup(SANDBOXLANEENV));
126
- }
114
+ export const lanesandboxfromenv = seating.lanesandboxfromenv;
127
115
 
128
116
  /**
129
117
  * Answers one mirrored lane row by lane id — the lookup the app surfaces
@@ -133,6 +121,4 @@ export function lanesandboxfromenv(lookup: (key: string) => string | undefined):
133
121
  * @returns the mirrored row, or undefined when the app carries no such
134
122
  * lane.
135
123
  */
136
- export function laneof(lane: string): AppWorkloadLane | undefined {
137
- return WORKLOADLANES.find((row) => row.lane === lane);
138
- }
124
+ export const laneof = seating.laneof;
package/sol/Sol.tsx CHANGED
@@ -29,7 +29,7 @@ import OnboardingAnchor from "./onboarding/onboarding";
29
29
  import LegalAnchor from "./legal/legal";
30
30
  import NotFoundAnchor from "./notfound/notfound";
31
31
  import PricingAnchor from "./pricing/pricing";
32
- import { EntryGate } from "./wingets";
32
+ import { EntryGate } from "../wingets";
33
33
  import { Shell } from "./shell/Shell";
34
34
  import SupportAnchor from "./support/support";
35
35
 
package/sol/apps/apps.tsx CHANGED
@@ -17,7 +17,7 @@ import { useContent } from "../../content.ts";
17
17
  import { familyurl } from "../../familyurl";
18
18
  import { label, labelfill } from "../../labels";
19
19
  import { roster } from "../../monetization.ts";
20
- import { AdSlot } from "../wingets";
20
+ import { AdSlot } from "../../wingets";
21
21
 
22
22
  export function Apps() {
23
23
  useContent();
package/sol/blog/blog.tsx CHANGED
@@ -17,7 +17,7 @@ import type { CSSProperties } from "react";
17
17
  import { useContent } from "../../content.ts";
18
18
  import { label, labelfill } from "../../labels";
19
19
  import { type blogpost, postindex, postwords } from "../../monetization";
20
- import { AdSlot } from "../wingets";
20
+ import { AdSlot } from "../../wingets";
21
21
 
22
22
  /** the meta line of one post: the date and the honest word count. */
23
23
  function PostMeta({ post }: { post: blogpost }) {
package/sol/home/home.tsx CHANGED
@@ -22,7 +22,7 @@ import { useContent } from "../../content.ts";
22
22
  import { familyurl } from "../../familyurl";
23
23
  import { type LabelKey, label, labelfill } from "../../labels";
24
24
  import { roster } from "../../monetization.ts";
25
- import { AdSlot, BrandMark, EntryGate } from "../wingets";
25
+ import { AdSlot, BrandMark, EntryGate } from "../../wingets";
26
26
 
27
27
  /** the featured deploy units of the landing preview (the full directory
28
28
  * lives on /apps): the OS, the engine, the store and one studio — read
@@ -31,7 +31,7 @@ import { useCallback, useEffect, useState } from "react";
31
31
  import { Link } from "wouter";
32
32
  import { label } from "../../labels";
33
33
  import { markOnboardingStage } from "../../onboardingstate.ts";
34
- import { BrandMark, IntroFooter, IntroPlans, IntroSplash } from "../wingets";
34
+ import { BrandMark, IntroFooter, IntroPlans, IntroSplash } from "../../wingets";
35
35
 
36
36
  /** the lifecycle of the opening act: resolving (the motion preference
37
37
  * decides) → playing (the akash floor over the landing) → done (the
@@ -19,7 +19,7 @@ import { useState } from "react";
19
19
  import { useLocation } from "wouter";
20
20
  import { type LabelKey, label } from "../../labels";
21
21
  import { markOnboardingStage } from "../../onboardingstate.ts";
22
- import { BrandMark } from "../wingets";
22
+ import { BrandMark } from "../../wingets";
23
23
 
24
24
  /** the three frames of the first run, in order — the copy rides the store
25
25
  * (the id keeps the ladder segments keyed, the key answers the eyebrow). */
@@ -27,7 +27,7 @@ import { useContent } from "../../content.ts";
27
27
  import { familyurl } from "../../familyurl";
28
28
  import { type LabelKey, label } from "../../labels";
29
29
  import { roster } from "../../monetization.ts";
30
- import { BrandMark } from "../wingets";
30
+ import { BrandMark } from "../../wingets";
31
31
 
32
32
  /** the section links of the top bar, in reading order (home is the mark).
33
33
  * the href rides the route table, the text rides the label accessor. */
package/tauri.conf.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://schema.tauri.app/config/2",
3
3
  "productName": "Cobaia",
4
- "version": "2.0.167",
4
+ "version": "2.0.168",
5
5
  "identifier": "com.wenathlan.cobaia",
6
6
  "build": {
7
7
  "beforeDevCommand": "pnpm dev",
package/tsconfig.json CHANGED
@@ -14,6 +14,7 @@
14
14
  "types": ["vite/client", "node"],
15
15
  "paths": {
16
16
  "@wenathlan/vault/labelstore": ["../vault/labelstore.ts"],
17
+ "@wenathlan/vault/onboardingstore": ["../vault/onboardingstore.ts"],
17
18
  "@wenathlan/vault/storagelane": ["../vault/storagelane.ts"]
18
19
  }
19
20
  },
package/vite.config.ts CHANGED
@@ -23,8 +23,22 @@ export default defineConfig({
23
23
  * the mechanism is never duplicated inside cobaia. the entry retires
24
24
  * when the published envelope carries the built label store. */
25
25
  "@wenathlan/vault/labelstore": path.resolve(import.meta.dirname, "../vault/labelstore.ts"),
26
+ "@wenathlan/vault/onboardingstore": path.resolve(import.meta.dirname, "../vault/onboardingstore.ts"),
26
27
  "@": path.resolve(import.meta.dirname, "sol"),
27
28
  },
29
+ /** the single-instance law (the PR12 renderparity heal): the family
30
+ * sources the thin doors re-export (devthink/reveal.ts and the sibling
31
+ * bridges) live outside this app root, and the resolver answers their
32
+ * bare react and wouter imports against the folder the file sits in —
33
+ * a second physical copy rides the bundle when the sibling tree carries
34
+ * its own node_modules (the two-react crash: the wouter context hook
35
+ * then answers the dispatcher of the copy that never renders, and the
36
+ * app dies at the workbench-failure face on both doors, the typed url
37
+ * and the panel window alike). the dedupe forces every react,
38
+ * react-dom and wouter specifier of the graph onto one resolved
39
+ * instance — the cross-app source rides, the duplication dies, one
40
+ * design answers both doors. */
41
+ dedupe: ["react", "react-dom", "react-dom/client", "wouter"],
28
42
  },
29
43
  root: import.meta.dirname,
30
44
  build: {
package/vitest.config.ts CHANGED
@@ -11,6 +11,14 @@ export default defineConfig({
11
11
  * the mechanism is never duplicated inside cobaia. the entry retires
12
12
  * when the published envelope carries the built label store. */
13
13
  "@wenathlan/vault/labelstore": path.resolve(import.meta.dirname, "../vault/labelstore.ts"),
14
+ /** the onboarding store bridge of the ownership law: the app's
15
+ * seen-flags mechanism rides the vault factory through the published
16
+ * library specifier ("@wenathlan/vault/onboardingstore"), and this
17
+ * bridge resolves the specifier to the monorepo source the sibling
18
+ * engine carries — the mechanism is never duplicated inside cobaia.
19
+ * the entry retires when the published envelope carries the built
20
+ * onboarding store. */
21
+ "@wenathlan/vault/onboardingstore": path.resolve(import.meta.dirname, "../vault/onboardingstore.ts"),
14
22
 
15
23
  /** the storage lane bridge of the ownership law: the app's sqlite
16
24
  * mechanism rides the vault factory through the published library
package/wingets.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * wingets.ts — the universal winget surface of cobaia: every winget of the
3
+ * sol/wingets folder answers from this one module at the app root, so the
4
+ * pages import their components from a single place and the dotted winget
5
+ * file name lives exactly once (here) instead of repeating across every
6
+ * page — the components stay complete independent files inside the
7
+ * sol/wingets folder.
8
+ */
9
+ export { type familymember, roster } from "./monetization.ts";
10
+ export * from "./sol/wingets/HomeEntrygate.tsx";
11
+ export { default as EntryGate } from "./sol/wingets/HomeEntrygate.tsx";
12
+ export * from "./sol/wingets/IntroFooter.tsx";
13
+ export { default as IntroFooter } from "./sol/wingets/IntroFooter.tsx";
14
+ export * from "./sol/wingets/IntroPlans.tsx";
15
+ export { default as IntroPlans } from "./sol/wingets/IntroPlans.tsx";
16
+ export * from "./sol/wingets/IntroSplash.tsx";
17
+ export { default as IntroSplash } from "./sol/wingets/IntroSplash.tsx";
18
+ export * from "./sol/wingets/ShellAdsslot.tsx";
19
+ export { default as ShellAdsslot } from "./sol/wingets/ShellAdsslot.tsx";
20
+ export * from "./sol/wingets/ShellBrand.tsx";
21
+ export { default as ShellBrand } from "./sol/wingets/ShellBrand.tsx";