jettypod 4.4.118 → 4.4.121
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/.env +4 -3
- package/Cargo.lock +6450 -0
- package/Cargo.toml +35 -0
- package/README.md +5 -1
- package/TAURI-MIGRATION-PLAN.md +840 -0
- package/apps/dashboard/app/connect-claude/page.tsx +5 -6
- package/apps/dashboard/app/decision/[id]/page.tsx +63 -58
- package/apps/dashboard/app/demo/gates/page.tsx +43 -45
- package/apps/dashboard/app/design-system/page.tsx +868 -0
- package/apps/dashboard/app/globals.css +80 -4
- package/apps/dashboard/app/install-claude/page.tsx +4 -6
- package/apps/dashboard/app/login/page.tsx +72 -54
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +61 -13
- package/apps/dashboard/app/signup/page.tsx +242 -0
- package/apps/dashboard/app/subscribe/page.tsx +0 -2
- package/apps/dashboard/app/tests/page.tsx +37 -4
- package/apps/dashboard/app/welcome/page.tsx +13 -16
- package/apps/dashboard/app/work/[id]/page.tsx +117 -118
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +92 -85
- package/apps/dashboard/components/CardMenu.tsx +45 -12
- package/apps/dashboard/components/ClaudePanel.tsx +771 -850
- package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
- package/apps/dashboard/components/CopyableId.tsx +3 -4
- package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
- package/apps/dashboard/components/DragContext.tsx +134 -63
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +6 -7
- package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +26 -7
- package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
- package/apps/dashboard/components/EpicGroup.tsx +359 -0
- package/apps/dashboard/components/GateCard.tsx +79 -17
- package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
- package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
- package/apps/dashboard/components/JettyLoader.tsx +37 -0
- package/apps/dashboard/components/KanbanBoard.tsx +368 -958
- package/apps/dashboard/components/KanbanCard.tsx +740 -0
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
- package/apps/dashboard/components/MainNav.tsx +38 -73
- package/apps/dashboard/components/MessageBlock.tsx +468 -0
- package/apps/dashboard/components/ModeStartCard.tsx +15 -16
- package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
- package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
- package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
- package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
- package/apps/dashboard/components/ReviewFooter.tsx +139 -0
- package/apps/dashboard/components/SessionList.tsx +19 -19
- package/apps/dashboard/components/SubscribeContent.tsx +91 -47
- package/apps/dashboard/components/TestTree.tsx +16 -16
- package/apps/dashboard/components/TipCard.tsx +16 -17
- package/apps/dashboard/components/Toast.tsx +5 -6
- package/apps/dashboard/components/TypeIcon.tsx +55 -0
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
- package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
- package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
- package/apps/dashboard/components/WorkItemTree.tsx +11 -32
- package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
- package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
- package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
- package/apps/dashboard/components/settings/EnvVarsSection.tsx +74 -152
- package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
- package/apps/dashboard/components/ui/Button.tsx +104 -0
- package/apps/dashboard/components/ui/Input.tsx +78 -0
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
- package/apps/dashboard/contexts/UsageContext.tsx +87 -32
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
- package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/constants.ts +43 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1265
- package/apps/dashboard/lib/environment-config.ts +173 -0
- package/apps/dashboard/lib/environment-verification.ts +119 -0
- package/apps/dashboard/lib/kanban-utils.ts +270 -0
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- package/apps/dashboard/lib/run-migrations.js +27 -2
- package/apps/dashboard/lib/service-recovery.ts +326 -0
- package/apps/dashboard/lib/session-state-machine.ts +1 -0
- package/apps/dashboard/lib/session-state-utils.ts +0 -164
- package/apps/dashboard/lib/session-stream-manager.ts +308 -134
- package/apps/dashboard/lib/shadows.ts +7 -0
- package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
- package/apps/dashboard/lib/tauri-bridge.ts +102 -0
- package/apps/dashboard/lib/tauri.ts +106 -0
- package/apps/dashboard/lib/utils.ts +6 -0
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -32
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
- package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/apps/dashboard/public/in-flight-seagull.png +0 -0
- package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
- package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
- package/apps/dashboard/public/jettypod_logo.png +0 -0
- package/apps/dashboard/public/pier-icon.png +0 -0
- package/apps/dashboard/public/star-icon.png +0 -0
- package/apps/dashboard/public/wrench-icon.png +0 -0
- package/apps/dashboard/scripts/tauri-build.js +228 -0
- package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
- package/apps/dashboard/scripts/ws-server.js +191 -0
- package/apps/dashboard/src/main.tsx +12 -0
- package/apps/dashboard/src/router.tsx +107 -0
- package/apps/dashboard/src/vite-env.d.ts +1 -0
- package/apps/dashboard/tsconfig.json +7 -12
- package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
- package/apps/dashboard/vite.config.ts +33 -0
- package/apps/update-server/src/index.ts +228 -80
- package/claude-hooks/global-guardrails.js +14 -13
- package/crates/jettypod-cli/Cargo.toml +19 -0
- package/crates/jettypod-cli/src/commands.rs +1249 -0
- package/crates/jettypod-cli/src/main.rs +595 -0
- package/crates/jettypod-core/Cargo.toml +26 -0
- package/crates/jettypod-core/build.rs +98 -0
- package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
- package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
- package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
- package/crates/jettypod-core/src/auth.rs +294 -0
- package/crates/jettypod-core/src/config.rs +397 -0
- package/crates/jettypod-core/src/db/mod.rs +507 -0
- package/crates/jettypod-core/src/db/recovery.rs +114 -0
- package/crates/jettypod-core/src/db/startup.rs +101 -0
- package/crates/jettypod-core/src/db/validate.rs +149 -0
- package/crates/jettypod-core/src/error.rs +76 -0
- package/crates/jettypod-core/src/git.rs +458 -0
- package/crates/jettypod-core/src/lib.rs +20 -0
- package/crates/jettypod-core/src/sessions.rs +625 -0
- package/crates/jettypod-core/src/skills.rs +556 -0
- package/crates/jettypod-core/src/work.rs +1086 -0
- package/crates/jettypod-core/src/worktree.rs +628 -0
- package/crates/jettypod-core/src/ws.rs +767 -0
- package/cucumber-test.cjs +6 -0
- package/cucumber.js +9 -3
- package/docs/COMMAND_REFERENCE.md +34 -0
- package/hooks/post-checkout +32 -75
- package/hooks/post-merge +111 -10
- package/jest.setup.js +1 -0
- package/jettypod.js +145 -116
- package/lib/bdd-preflight.js +96 -0
- package/lib/chore-taxonomy.js +33 -10
- package/lib/database.js +36 -16
- package/lib/db-watcher.js +1 -1
- package/lib/git-hooks/pre-commit +1 -1
- package/lib/jettypod-backup.js +27 -4
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/027-plan-at-creation-column.js +3 -1
- package/lib/migrations/029-remove-autoincrement.js +307 -0
- package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/migrations/index.js +47 -4
- package/lib/schema.js +10 -5
- package/lib/seed-onboarding.js +1 -1
- package/lib/update-command/index.js +9 -175
- package/lib/work-commands/index.js +144 -19
- package/lib/work-tracking/index.js +148 -27
- package/lib/worktree-diagnostics.js +16 -16
- package/lib/worktree-facade.js +1 -1
- package/lib/worktree-manager.js +8 -8
- package/lib/worktree-reconciler.js +5 -5
- package/package.json +9 -2
- package/scripts/ndjson-to-cucumber-json.js +152 -0
- package/scripts/postinstall.js +25 -0
- package/skills-templates/bug-mode/SKILL.md +79 -20
- package/skills-templates/bug-planning/SKILL.md +25 -29
- package/skills-templates/chore-mode/SKILL.md +171 -69
- package/skills-templates/chore-mode/verification.js +51 -10
- package/skills-templates/chore-planning/SKILL.md +47 -18
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +82 -48
- package/skills-templates/external-transition/SKILL.md +47 -47
- package/skills-templates/feature-planning/SKILL.md +173 -74
- package/skills-templates/production-mode/SKILL.md +69 -49
- package/skills-templates/request-routing/SKILL.md +4 -4
- package/skills-templates/simple-improvement/SKILL.md +74 -29
- package/skills-templates/speed-mode/SKILL.md +217 -141
- package/skills-templates/stable-mode/SKILL.md +148 -89
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -378
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
- package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
- package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
- package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
- package/apps/dashboard/app/api/kanban/route.ts +0 -15
- package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
- package/apps/dashboard/app/api/settings/general/route.ts +0 -21
- package/apps/dashboard/app/api/tests/route.ts +0 -9
- package/apps/dashboard/app/api/tests/run/route.ts +0 -82
- package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
- package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
- package/apps/dashboard/app/api/usage/route.ts +0 -17
- package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -43
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
- package/apps/dashboard/electron/ipc-handlers.js +0 -1028
- package/apps/dashboard/electron/main.js +0 -2124
- package/apps/dashboard/electron/preload.js +0 -123
- package/apps/dashboard/electron/session-manager.js +0 -141
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/claude-process-manager.ts +0 -492
- package/apps/dashboard/lib/db-bridge.ts +0 -282
- package/apps/dashboard/lib/prototypes.ts +0 -202
- package/apps/dashboard/lib/test-results-db.ts +0 -307
- package/apps/dashboard/lib/tests.ts +0 -282
- package/apps/dashboard/next.config.js +0 -50
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
- package/docs/bdd-guidance.md +0 -390
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Downloads Node.js binaries for bundling with the Electron app.
|
|
4
|
-
* Run this before electron-builder to ensure Node.js is available.
|
|
5
|
-
*
|
|
6
|
-
* Usage: node scripts/download-node.js
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const https = require('https');
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const { execSync } = require('child_process');
|
|
13
|
-
|
|
14
|
-
const NODE_VERSION = 'v24.13.0'; // LTS Krypton
|
|
15
|
-
const ARCHITECTURES = ['arm64', 'x64'];
|
|
16
|
-
const OUTPUT_DIR = path.join(__dirname, '..', 'build-resources', 'node');
|
|
17
|
-
|
|
18
|
-
function download(url, dest) {
|
|
19
|
-
return new Promise((resolve, reject) => {
|
|
20
|
-
const file = fs.createWriteStream(dest);
|
|
21
|
-
https.get(url, (response) => {
|
|
22
|
-
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
23
|
-
// Follow redirect
|
|
24
|
-
https.get(response.headers.location, (res) => {
|
|
25
|
-
res.pipe(file);
|
|
26
|
-
file.on('finish', () => {
|
|
27
|
-
file.close(resolve);
|
|
28
|
-
});
|
|
29
|
-
}).on('error', reject);
|
|
30
|
-
} else {
|
|
31
|
-
response.pipe(file);
|
|
32
|
-
file.on('finish', () => {
|
|
33
|
-
file.close(resolve);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
}).on('error', reject);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function downloadAndExtract(arch) {
|
|
41
|
-
const archDir = path.join(OUTPUT_DIR, arch);
|
|
42
|
-
const nodeDir = path.join(archDir, 'bin');
|
|
43
|
-
|
|
44
|
-
// Check if already downloaded
|
|
45
|
-
if (fs.existsSync(path.join(nodeDir, 'node'))) {
|
|
46
|
-
console.log(`✓ Node.js ${arch} already exists`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const tarball = `node-${NODE_VERSION}-darwin-${arch}.tar.gz`;
|
|
51
|
-
const url = `https://nodejs.org/dist/${NODE_VERSION}/${tarball}`;
|
|
52
|
-
const tarPath = path.join(OUTPUT_DIR, tarball);
|
|
53
|
-
|
|
54
|
-
console.log(`Downloading Node.js ${NODE_VERSION} for ${arch}...`);
|
|
55
|
-
await download(url, tarPath);
|
|
56
|
-
|
|
57
|
-
console.log(`Extracting ${arch}...`);
|
|
58
|
-
fs.mkdirSync(archDir, { recursive: true });
|
|
59
|
-
execSync(`tar -xzf "${tarPath}" -C "${OUTPUT_DIR}"`, { stdio: 'inherit' });
|
|
60
|
-
|
|
61
|
-
// Move extracted directory to arch name
|
|
62
|
-
const extractedDir = path.join(OUTPUT_DIR, `node-${NODE_VERSION}-darwin-${arch}`);
|
|
63
|
-
if (fs.existsSync(archDir)) {
|
|
64
|
-
fs.rmSync(archDir, { recursive: true });
|
|
65
|
-
}
|
|
66
|
-
fs.renameSync(extractedDir, archDir);
|
|
67
|
-
|
|
68
|
-
// Remove unnecessary files to reduce size
|
|
69
|
-
const toRemove = ['include', 'share', 'CHANGELOG.md', 'LICENSE', 'README.md'];
|
|
70
|
-
for (const item of toRemove) {
|
|
71
|
-
const itemPath = path.join(archDir, item);
|
|
72
|
-
if (fs.existsSync(itemPath)) {
|
|
73
|
-
fs.rmSync(itemPath, { recursive: true });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Remove corepack (not needed)
|
|
78
|
-
const corepackBin = path.join(archDir, 'bin', 'corepack');
|
|
79
|
-
const corepackLib = path.join(archDir, 'lib', 'node_modules', 'corepack');
|
|
80
|
-
if (fs.existsSync(corepackBin)) fs.rmSync(corepackBin);
|
|
81
|
-
if (fs.existsSync(corepackLib)) fs.rmSync(corepackLib, { recursive: true });
|
|
82
|
-
|
|
83
|
-
// Clean up tarball
|
|
84
|
-
fs.rmSync(tarPath);
|
|
85
|
-
|
|
86
|
-
console.log(`✓ Node.js ${arch} ready`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async function main() {
|
|
90
|
-
console.log(`\nPreparing Node.js ${NODE_VERSION} for bundling...\n`);
|
|
91
|
-
|
|
92
|
-
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
93
|
-
|
|
94
|
-
for (const arch of ARCHITECTURES) {
|
|
95
|
-
await downloadAndExtract(arch);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
console.log('\n✓ All Node.js binaries ready for bundling\n');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
main().catch((err) => {
|
|
102
|
-
console.error('Failed to download Node.js:', err);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Upload release artifacts to Cloudflare R2.
|
|
5
|
-
* Run after electron-builder: npm run upload:r2
|
|
6
|
-
*
|
|
7
|
-
* Requires CLOUDFLARE_API_TOKEN env var (or wrangler login).
|
|
8
|
-
* Uploads: latest-mac.yml, DMG, ZIP, and blockmap files.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { execSync } = require('child_process');
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
|
|
15
|
-
const BUCKET_NAME = 'jettypod-releases';
|
|
16
|
-
const DIST_DIR = path.join(__dirname, '..', 'dist');
|
|
17
|
-
|
|
18
|
-
// File patterns to upload
|
|
19
|
-
const UPLOAD_PATTERNS = [
|
|
20
|
-
/^latest-mac\.yml$/,
|
|
21
|
-
/\.dmg$/,
|
|
22
|
-
/\.zip$/,
|
|
23
|
-
/\.blockmap$/,
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
function findArtifacts() {
|
|
27
|
-
if (!fs.existsSync(DIST_DIR)) {
|
|
28
|
-
console.error(`❌ dist/ directory not found at ${DIST_DIR}`);
|
|
29
|
-
console.error('Run electron:build first.');
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const files = fs.readdirSync(DIST_DIR);
|
|
34
|
-
return files.filter((file) =>
|
|
35
|
-
UPLOAD_PATTERNS.some((pattern) => pattern.test(file))
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function uploadFile(filename) {
|
|
40
|
-
const filePath = path.join(DIST_DIR, filename);
|
|
41
|
-
const stats = fs.statSync(filePath);
|
|
42
|
-
const sizeMB = (stats.size / (1024 * 1024)).toFixed(1);
|
|
43
|
-
|
|
44
|
-
console.log(` Uploading ${filename} (${sizeMB} MB)...`);
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
execSync(
|
|
48
|
-
`npx wrangler r2 object put "${BUCKET_NAME}/${filename}" --file="${filePath}" --remote`,
|
|
49
|
-
{ stdio: 'pipe' }
|
|
50
|
-
);
|
|
51
|
-
console.log(` ✅ ${filename}`);
|
|
52
|
-
return true;
|
|
53
|
-
} catch (error) {
|
|
54
|
-
console.error(` ❌ Failed to upload ${filename}: ${error.message}`);
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function main() {
|
|
60
|
-
console.log('🚀 Uploading release artifacts to R2...\n');
|
|
61
|
-
|
|
62
|
-
const artifacts = findArtifacts();
|
|
63
|
-
if (artifacts.length === 0) {
|
|
64
|
-
console.error('❌ No release artifacts found in dist/');
|
|
65
|
-
console.error('Expected: latest-mac.yml, .dmg, .zip, or .blockmap files');
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log(`Found ${artifacts.length} artifact(s):\n`);
|
|
70
|
-
|
|
71
|
-
let success = 0;
|
|
72
|
-
let failed = 0;
|
|
73
|
-
|
|
74
|
-
for (const artifact of artifacts) {
|
|
75
|
-
if (uploadFile(artifact)) {
|
|
76
|
-
success++;
|
|
77
|
-
} else {
|
|
78
|
-
failed++;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
console.log(`\n📦 Upload complete: ${success} uploaded, ${failed} failed`);
|
|
83
|
-
|
|
84
|
-
if (failed > 0) {
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
main();
|
package/docs/bdd-guidance.md
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
What BDD actually is (and isn’t)
|
|
2
|
-
|
|
3
|
-
BDD is a collaboration + specification technique that uses concrete examples to describe behavior in a shared language.
|
|
4
|
-
|
|
5
|
-
The “unit tests” are not the point. The examples are the point.
|
|
6
|
-
|
|
7
|
-
BDD tests should validate behavior that matters to users/business—without leaking implementation details.
|
|
8
|
-
|
|
9
|
-
BDD ≠ “write all tests in Gherkin.” BDD can be done with plain unit/integration tests too. Gherkin is just a common interface for readability and stakeholder alignment.
|
|
10
|
-
|
|
11
|
-
A good mental model:
|
|
12
|
-
|
|
13
|
-
Feature files describe “what” and “why.”
|
|
14
|
-
|
|
15
|
-
Step definitions implement “how,” but only at a high level.
|
|
16
|
-
|
|
17
|
-
Lower-level details live in helper layers (Page Objects, API clients, domain helpers).
|
|
18
|
-
|
|
19
|
-
The BDD flow (what “good” looks like)
|
|
20
|
-
|
|
21
|
-
Discovery (3 Amigos: product + dev + QA)
|
|
22
|
-
|
|
23
|
-
Agree on behavior via examples: happy path + edge cases.
|
|
24
|
-
|
|
25
|
-
Formulation
|
|
26
|
-
|
|
27
|
-
Turn examples into scenarios (often in Gherkin).
|
|
28
|
-
|
|
29
|
-
Automation
|
|
30
|
-
|
|
31
|
-
Implement step definitions that call into a small, reusable automation layer.
|
|
32
|
-
|
|
33
|
-
Living documentation
|
|
34
|
-
|
|
35
|
-
Keep scenarios accurate and stable; prune duplicates; version behavior over time.
|
|
36
|
-
|
|
37
|
-
Gherkin, done well (the style rules that save you later)
|
|
38
|
-
Core primitives
|
|
39
|
-
|
|
40
|
-
Feature: coherent behavior area
|
|
41
|
-
|
|
42
|
-
Scenario: one concrete example
|
|
43
|
-
|
|
44
|
-
Given/When/Then:
|
|
45
|
-
|
|
46
|
-
Given: preconditions / state
|
|
47
|
-
|
|
48
|
-
When: action
|
|
49
|
-
|
|
50
|
-
Then: observable outcomes
|
|
51
|
-
|
|
52
|
-
Good scenario traits
|
|
53
|
-
|
|
54
|
-
Small: one behavior, one reason to fail
|
|
55
|
-
|
|
56
|
-
Declarative: describes intent, not UI clicks
|
|
57
|
-
|
|
58
|
-
Stable: avoids brittle details (pixel-level UI, timing hacks)
|
|
59
|
-
|
|
60
|
-
Deterministic: no reliance on “whatever data happens to exist”
|
|
61
|
-
|
|
62
|
-
Example (good)
|
|
63
|
-
Scenario: User can retry a failed payment
|
|
64
|
-
Given a user with an unpaid invoice
|
|
65
|
-
And the payment processor returns "insufficient_funds"
|
|
66
|
-
When the user retries payment with a different card
|
|
67
|
-
Then the invoice is marked as paid
|
|
68
|
-
And the user sees a receipt
|
|
69
|
-
|
|
70
|
-
Example (brittle / not great)
|
|
71
|
-
Scenario: Pay invoice
|
|
72
|
-
Given I click the "Billing" tab
|
|
73
|
-
And I wait 2 seconds
|
|
74
|
-
And I click the third button on the page
|
|
75
|
-
When I type "4111111111111111" into the card field
|
|
76
|
-
Then I should see "Success"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
This is more of a UI macro recorder than a behavioral spec.
|
|
80
|
-
|
|
81
|
-
Step definitions: the most common place things go off the rails
|
|
82
|
-
The biggest rule
|
|
83
|
-
|
|
84
|
-
Step definitions should be thin.
|
|
85
|
-
They should:
|
|
86
|
-
|
|
87
|
-
parse parameters
|
|
88
|
-
|
|
89
|
-
call a helper/API/page-object method
|
|
90
|
-
|
|
91
|
-
assert outcomes at the correct level
|
|
92
|
-
|
|
93
|
-
They should not:
|
|
94
|
-
|
|
95
|
-
contain lots of branching logic
|
|
96
|
-
|
|
97
|
-
do complex loops
|
|
98
|
-
|
|
99
|
-
embed SQL queries
|
|
100
|
-
|
|
101
|
-
“know” too much about UI selectors
|
|
102
|
-
|
|
103
|
-
implement multi-step workflows inline
|
|
104
|
-
|
|
105
|
-
The “thin step” pattern
|
|
106
|
-
|
|
107
|
-
Step def → calls one intentful function (e.g., billing.retryPaymentWith(card)), rather than doing click/type/wait directly.
|
|
108
|
-
|
|
109
|
-
Example structure:
|
|
110
|
-
|
|
111
|
-
// step definition
|
|
112
|
-
When('the user retries payment with a different card', async () => {
|
|
113
|
-
await billing.retryPaymentWith(validCard2);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// helper layer (page object / service client)
|
|
117
|
-
async function retryPaymentWith(card) {
|
|
118
|
-
await openBilling();
|
|
119
|
-
await selectInvoice(...);
|
|
120
|
-
await enterCard(card);
|
|
121
|
-
await submit();
|
|
122
|
-
await waitForReceipt(); // smart wait, not sleep(2000)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
Handling “complex things” in BDD tests (the hard parts)
|
|
126
|
-
1) Asynchrony and eventual consistency
|
|
127
|
-
|
|
128
|
-
Problem: background jobs, queues, delayed writes, distributed systems.
|
|
129
|
-
|
|
130
|
-
Best practices
|
|
131
|
-
|
|
132
|
-
Prefer event-based or state-based polling with timeouts over fixed sleeps.
|
|
133
|
-
|
|
134
|
-
Assert intermediate states if meaningful (“processing” → “completed”).
|
|
135
|
-
|
|
136
|
-
If possible, expose a test-only hook (e.g., “job runner runs immediately” in test env).
|
|
137
|
-
|
|
138
|
-
What to do:
|
|
139
|
-
|
|
140
|
-
await waitFor(() => order.status === 'COMPLETED', { timeout: 10_000 })
|
|
141
|
-
|
|
142
|
-
avoid: sleep(5000)
|
|
143
|
-
|
|
144
|
-
2) External dependencies (payment providers, email/SMS, maps)
|
|
145
|
-
|
|
146
|
-
Problem: flaky tests, slow runs, rate limits.
|
|
147
|
-
|
|
148
|
-
Best practices
|
|
149
|
-
|
|
150
|
-
For most BDD runs: stub at the boundary (in-process fake server, contract stub).
|
|
151
|
-
|
|
152
|
-
Have a smaller set of true end-to-end smoke tests that hit real external services (maybe nightly).
|
|
153
|
-
|
|
154
|
-
3) Authentication flows (OAuth, magic links)
|
|
155
|
-
|
|
156
|
-
Best practices
|
|
157
|
-
|
|
158
|
-
Prefer test auth shortcuts:
|
|
159
|
-
|
|
160
|
-
a test-only endpoint to mint tokens
|
|
161
|
-
|
|
162
|
-
bypass UI login with session injection
|
|
163
|
-
|
|
164
|
-
Keep one or two UI-login scenarios if you must, but don’t make every scenario pay the “login tax.”
|
|
165
|
-
|
|
166
|
-
4) Data setup that is “realistic” but not fragile
|
|
167
|
-
|
|
168
|
-
Problem: complicated prerequisites create scenario bloat.
|
|
169
|
-
|
|
170
|
-
Best practices
|
|
171
|
-
|
|
172
|
-
Use factories/fixtures with names that encode intent:
|
|
173
|
-
|
|
174
|
-
givenUserWithUnpaidInvoice()
|
|
175
|
-
|
|
176
|
-
givenWorkspaceWith3MembersAndNoAdmin()
|
|
177
|
-
|
|
178
|
-
Avoid “Given the database has…” in feature files. That’s implementation leakage.
|
|
179
|
-
|
|
180
|
-
5) Time, randomness, and IDs
|
|
181
|
-
|
|
182
|
-
Best practices
|
|
183
|
-
|
|
184
|
-
Freeze time (clock.set("2026-02-10T10:00:00Z")) or inject time providers.
|
|
185
|
-
|
|
186
|
-
Seed randomness.
|
|
187
|
-
|
|
188
|
-
Don’t assert on raw IDs; assert on meaning (“receipt exists”, “email sent to user”).
|
|
189
|
-
|
|
190
|
-
6) UI interactions that are inherently finicky
|
|
191
|
-
|
|
192
|
-
Best practices
|
|
193
|
-
|
|
194
|
-
Use stable locators (data-testid, ARIA roles) rather than CSS chains.
|
|
195
|
-
|
|
196
|
-
Use smart waits (element visible/enabled, network idle) not sleeps.
|
|
197
|
-
|
|
198
|
-
Put selectors in one place (page objects / screen model).
|
|
199
|
-
|
|
200
|
-
7) Distributed workflows (webhook in, job runs, UI updates)
|
|
201
|
-
|
|
202
|
-
Best practices
|
|
203
|
-
|
|
204
|
-
Split assertions by layer:
|
|
205
|
-
|
|
206
|
-
API-level scenario verifies webhook → status update
|
|
207
|
-
|
|
208
|
-
UI-level scenario verifies status display
|
|
209
|
-
|
|
210
|
-
Don’t force one scenario to validate every link in the chain unless it’s explicitly a top-level acceptance test.
|
|
211
|
-
|
|
212
|
-
The test pyramid in BDD terms (where each kind of test belongs)
|
|
213
|
-
|
|
214
|
-
A very effective setup:
|
|
215
|
-
|
|
216
|
-
Many unit tests (fast, deterministic): pure logic
|
|
217
|
-
|
|
218
|
-
Many integration/contract tests: service boundaries, DB, message bus (still fast-ish)
|
|
219
|
-
|
|
220
|
-
Some BDD scenarios: critical user journeys and key edge cases
|
|
221
|
-
|
|
222
|
-
Very few UI E2E: smoke and “are we totally broken?” checks
|
|
223
|
-
|
|
224
|
-
BDD scenarios can exist at multiple levels (API-level BDD is often a sweet spot).
|
|
225
|
-
|
|
226
|
-
Mocks, stubs, fakes: what they are (and why people argue about them)
|
|
227
|
-
Definitions (practical, not academic)
|
|
228
|
-
|
|
229
|
-
Mock: a test double you can verify interactions with
|
|
230
|
-
(“Was chargeCard() called with amount=4999?”)
|
|
231
|
-
|
|
232
|
-
Stub: a test double that returns predetermined responses
|
|
233
|
-
(“When /payments is called, return 402 insufficient_funds”)
|
|
234
|
-
|
|
235
|
-
Fake: a lightweight working implementation
|
|
236
|
-
(in-memory DB, fake email inbox, fake queue)
|
|
237
|
-
|
|
238
|
-
Spy: like a mock, but wraps a real object and records calls
|
|
239
|
-
|
|
240
|
-
When to use what
|
|
241
|
-
|
|
242
|
-
Use stubs/fakes for most BDD scenarios because they support behavior assertions (“user sees receipt”) without coupling to call patterns.
|
|
243
|
-
|
|
244
|
-
Use mocks sparingly, mostly in unit tests or when verifying a critical side effect is the purpose of the scenario.
|
|
245
|
-
|
|
246
|
-
The big danger of mocks in BDD
|
|
247
|
-
|
|
248
|
-
Mocks push you toward testing implementation details:
|
|
249
|
-
|
|
250
|
-
“did we call X?” rather than “did the user get the outcome?”
|
|
251
|
-
|
|
252
|
-
Sometimes verifying calls is legitimate (e.g., “audit event emitted”), but generally:
|
|
253
|
-
|
|
254
|
-
BDD asserts outcomes, not internal choreography.
|
|
255
|
-
|
|
256
|
-
Step definition best practices checklist (great for “is my AI behaving?”)
|
|
257
|
-
✅ Green flags
|
|
258
|
-
|
|
259
|
-
Steps are short (often 1–5 lines)
|
|
260
|
-
|
|
261
|
-
Steps call named helper methods (domain language)
|
|
262
|
-
|
|
263
|
-
Assertions are in Then steps (or helper assertions)
|
|
264
|
-
|
|
265
|
-
Givens set up intentful state, not low-level DB edits
|
|
266
|
-
|
|
267
|
-
Reuse happens through helper methods, not giant shared step defs
|
|
268
|
-
|
|
269
|
-
Steps avoid sleeps; use smart waits
|
|
270
|
-
|
|
271
|
-
Scenario language avoids UI specifics unless truly necessary
|
|
272
|
-
|
|
273
|
-
🚩 Red flags (AI assistants love these)
|
|
274
|
-
|
|
275
|
-
Step defs contain:
|
|
276
|
-
|
|
277
|
-
loops, conditionals, try/catch gymnastics
|
|
278
|
-
|
|
279
|
-
direct SQL / direct ORM writes sprinkled everywhere
|
|
280
|
-
|
|
281
|
-
lots of selectors + click/type chains inline
|
|
282
|
-
|
|
283
|
-
random sleeps/timeouts to “make it pass”
|
|
284
|
-
|
|
285
|
-
Steps are overly generic:
|
|
286
|
-
|
|
287
|
-
“When I do the thing”
|
|
288
|
-
|
|
289
|
-
“Then it works”
|
|
290
|
-
|
|
291
|
-
Heavy parameterization:
|
|
292
|
-
|
|
293
|
-
Steps with 6–10 parameters usually mean you’re encoding a DSL no one can read
|
|
294
|
-
|
|
295
|
-
Shared state is global and leaky across scenarios
|
|
296
|
-
|
|
297
|
-
One scenario validates 12 different outcomes (“kitchen sink test”)
|
|
298
|
-
|
|
299
|
-
A strict architecture that keeps BDD clean
|
|
300
|
-
|
|
301
|
-
If you want your AI to stay disciplined, give it a structure it can’t easily “freestyle” out of:
|
|
302
|
-
|
|
303
|
-
Recommended layers
|
|
304
|
-
|
|
305
|
-
Feature files (behavior)
|
|
306
|
-
|
|
307
|
-
Step definitions (glue)
|
|
308
|
-
|
|
309
|
-
Domain tasks / Screenplay actions (intentful operations)
|
|
310
|
-
|
|
311
|
-
Drivers
|
|
312
|
-
|
|
313
|
-
UI driver (page objects / screen model)
|
|
314
|
-
|
|
315
|
-
API client
|
|
316
|
-
|
|
317
|
-
DB helper (sparingly)
|
|
318
|
-
|
|
319
|
-
Message bus helper
|
|
320
|
-
|
|
321
|
-
Test fixtures/factories
|
|
322
|
-
|
|
323
|
-
Rule of thumb:
|
|
324
|
-
|
|
325
|
-
Step defs may depend on domain tasks
|
|
326
|
-
|
|
327
|
-
Domain tasks may depend on drivers
|
|
328
|
-
|
|
329
|
-
Feature files know nothing about drivers
|
|
330
|
-
|
|
331
|
-
This prevents selector soup from infecting Gherkin.
|
|
332
|
-
|
|
333
|
-
Making your AI assistant “strict” (practical constraints you can enforce)
|
|
334
|
-
|
|
335
|
-
Here are concrete constraints you can put in your prompt / code review rubric:
|
|
336
|
-
|
|
337
|
-
Step definition max complexity
|
|
338
|
-
|
|
339
|
-
No loops
|
|
340
|
-
|
|
341
|
-
No conditionals except trivial parameter mapping
|
|
342
|
-
|
|
343
|
-
No sleeps
|
|
344
|
-
|
|
345
|
-
Selectors forbidden in steps
|
|
346
|
-
|
|
347
|
-
Must live in page objects/screen models only
|
|
348
|
-
|
|
349
|
-
One intentful call per step
|
|
350
|
-
|
|
351
|
-
Steps call one task method
|
|
352
|
-
|
|
353
|
-
Outcome assertions only in Then
|
|
354
|
-
|
|
355
|
-
No shared global mutable state
|
|
356
|
-
|
|
357
|
-
Use scenario context object only
|
|
358
|
-
|
|
359
|
-
Deterministic data
|
|
360
|
-
|
|
361
|
-
Factories generate known entities; tests never depend on prod-like ambient data
|
|
362
|
-
|
|
363
|
-
If you tell the AI “follow best practices,” it’ll nod vigorously and then sleep(2000) anyway. If you tell it “sleep is banned,” it suddenly remembers how to wait for elements like an adult.
|
|
364
|
-
|
|
365
|
-
Quick example: translating complex behavior into clean steps
|
|
366
|
-
|
|
367
|
-
Complex behavior: “User triggers export; job runs async; user is notified; file is downloadable.”
|
|
368
|
-
|
|
369
|
-
Good BDD split:
|
|
370
|
-
|
|
371
|
-
Scenario asserts user-level behavior
|
|
372
|
-
|
|
373
|
-
Implementation uses polling and test doubles
|
|
374
|
-
|
|
375
|
-
Scenario: User can download a completed export
|
|
376
|
-
Given a user with 3 projects
|
|
377
|
-
When the user requests a project export
|
|
378
|
-
Then the export eventually completes
|
|
379
|
-
And the user can download the export file
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
Implementation strategy:
|
|
383
|
-
|
|
384
|
-
request export calls API
|
|
385
|
-
|
|
386
|
-
eventually completes polls status endpoint with timeout
|
|
387
|
-
|
|
388
|
-
can download checks signed URL returns 200 and file has expected headers
|
|
389
|
-
|
|
390
|
-
No sleeps, no digging into job queue internals (unless you’re specifically testing that).
|