learningfoundry 0.79.2__tar.gz → 0.79.3__tar.gz

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 (121) hide show
  1. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/.gitignore +19 -6
  2. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/CHANGELOG.md +15 -0
  3. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/PKG-INFO +1 -1
  4. learningfoundry-0.79.3/docs/specs/nbfoundry/README.md +176 -0
  5. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/docs/specs/pyve/README.md +123 -215
  6. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/pyproject.toml +1 -1
  7. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/__init__.py +1 -1
  8. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/pipeline.py +7 -3
  9. learningfoundry-0.79.2/docs/specs/nbfoundry/README.md +0 -100
  10. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/LICENSE +0 -0
  11. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/README.md +0 -0
  12. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/docs/specs/quizazz/README.md +0 -0
  13. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/__main__.py +0 -0
  14. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/asset_resolver.py +0 -0
  15. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/cli.py +0 -0
  16. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/config.py +0 -0
  17. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/directives.py +0 -0
  18. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/exceptions.py +0 -0
  19. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/generator.py +0 -0
  20. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/integrations/__init__.py +0 -0
  21. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/integrations/d3foundry_stub.py +0 -0
  22. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/integrations/nbfoundry_stub.py +0 -0
  23. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/integrations/protocols.py +0 -0
  24. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/integrations/quizazz.py +0 -0
  25. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/logging_config.py +0 -0
  26. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/parser.py +0 -0
  27. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/py.typed +0 -0
  28. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/resolver.py +0 -0
  29. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/schema_extensions.py +0 -0
  30. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/schema_v1.py +0 -0
  31. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/README.md +0 -0
  32. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/finish.spec.ts +0 -0
  33. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/fixtures/curriculum.json +0 -0
  34. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/global-teardown.ts +0 -0
  35. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/lifecycle.spec.ts +0 -0
  36. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/navigation.spec.ts +0 -0
  37. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/progress.spec.ts +0 -0
  38. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/reset.spec.ts +0 -0
  39. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/text-block-bottom.spec.ts +0 -0
  40. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/e2e/video.spec.ts +0 -0
  41. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/package.json +0 -0
  42. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/playwright.config.ts +0 -0
  43. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/pnpm-lock.yaml +0 -0
  44. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/pnpm-workspace.yaml +0 -0
  45. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/app.css +0 -0
  46. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/app.html +0 -0
  47. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/AssessmentBlock.svelte +0 -0
  48. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/AssessmentBlock.test.ts +0 -0
  49. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ContentBlock.svelte +0 -0
  50. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ExerciseBlock.svelte +0 -0
  51. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonList.svelte +0 -0
  52. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonList.test.ts +0 -0
  53. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonView.svelte +0 -0
  54. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonView.test.ts +0 -0
  55. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/LockedLessonPlaceholder.svelte +0 -0
  56. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ModuleList.svelte +0 -0
  57. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ModuleList.test.ts +0 -0
  58. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/Navigation.svelte +0 -0
  59. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/PlaceholderBlock.svelte +0 -0
  60. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ProgressBar.svelte +0 -0
  61. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ProgressDashboard.svelte +0 -0
  62. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ProgressDashboard.test.ts +0 -0
  63. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/RecordingPausedBanner.svelte +0 -0
  64. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/RecordingPausedBanner.test.ts +0 -0
  65. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ResetCourseButton.svelte +0 -0
  66. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/ResetCourseButton.test.ts +0 -0
  67. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.observer.test.ts +0 -0
  68. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.svelte +0 -0
  69. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.test.ts +0 -0
  70. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/VideoBlock.svelte +0 -0
  71. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/VideoBlock.test.ts +0 -0
  72. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/VisualizationBlock.svelte +0 -0
  73. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/lesson-view.helpers.ts +0 -0
  74. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/module-list.helpers.ts +0 -0
  75. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/module-list.test.ts +0 -0
  76. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/mount.test.ts +0 -0
  77. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/navigation.helpers.ts +0 -0
  78. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/navigation.test.ts +0 -0
  79. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/components/progress-dashboard.helpers.ts +0 -0
  80. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/database.test.ts +0 -0
  81. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/database.ts +0 -0
  82. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/index.ts +0 -0
  83. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/progress.test.ts +0 -0
  84. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/progress.ts +0 -0
  85. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/user-id.test.ts +0 -0
  86. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/db/user-id.ts +0 -0
  87. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/stores/curriculum.test.ts +0 -0
  88. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/stores/curriculum.ts +0 -0
  89. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/stores/db-init.test.ts +0 -0
  90. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/stores/db-init.ts +0 -0
  91. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/stores/progress.test.ts +0 -0
  92. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/stores/progress.ts +0 -0
  93. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/types/index.ts +0 -0
  94. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/assessment-passed.test.ts +0 -0
  95. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/assessment-passed.ts +0 -0
  96. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/duration.test.ts +0 -0
  97. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/duration.ts +0 -0
  98. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/locking.test.ts +0 -0
  99. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/locking.ts +0 -0
  100. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/markdown-directives.ts +0 -0
  101. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/markdown.test.ts +0 -0
  102. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/markdown.ts +0 -0
  103. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/progress.test.ts +0 -0
  104. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/progress.ts +0 -0
  105. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/lib/utils/viewport-completion.ts +0 -0
  106. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/+layout.svelte +0 -0
  107. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/+layout.ts +0 -0
  108. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/+page.svelte +0 -0
  109. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/[lesson]/+page.svelte +0 -0
  110. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/[lesson]/page.test.ts +0 -0
  111. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/assessment/[id]/+page.svelte +0 -0
  112. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/assessment/[id]/page.test.ts +0 -0
  113. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/layout.helpers.ts +0 -0
  114. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/layout.scroll.test.ts +0 -0
  115. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/layout.scroll.ts +0 -0
  116. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/src/routes/layout.test.ts +0 -0
  117. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/static/.gitkeep +0 -0
  118. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/svelte.config.js +0 -0
  119. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/test-results/.last-run.json +0 -0
  120. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/tsconfig.json +0 -0
  121. {learningfoundry-0.79.2 → learningfoundry-0.79.3}/src/learningfoundry/sveltekit_template/vite.config.ts +0 -0
@@ -1,6 +1,16 @@
1
- # macOS only
1
+ # >>> pyve:managed:gitignore >>>
2
+ # pyve-managed .gitignore — do not edit between the markers;
3
+ # add your own ignores above or below the managed section.
4
+ # macOS
2
5
  .DS_Store
3
6
 
7
+ # Pyve-managed
8
+ .pyve/
9
+ .envrc
10
+ .env
11
+ .vscode/settings.json
12
+ .venv
13
+
4
14
  # Python build and test artifacts
5
15
  __pycache__
6
16
  *.pyc
@@ -19,12 +29,15 @@ build/
19
29
  .ipynb_checkpoints/
20
30
  *.ipynb_checkpoints
21
31
 
32
+ # <<< pyve:managed:gitignore <<<
33
+ # macOS only
34
+
35
+ # Python build and test artifacts
36
+
37
+ # Jupyter notebooks
38
+
22
39
  # Pyve virtual environment
23
- .pyve/envs
24
- .pyve/testenv
25
- .envrc
26
- .env
27
- .vscode/settings.json
40
+ .pyve
28
41
 
29
42
  # Node / SvelteKit
30
43
  node_modules/
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.79.3] - 2026-06-15
11
+
12
+ Fix `learningfoundry preview` appearing to hang silently during the `pnpm install` step and then reporting an empty failure on Ctrl-C. The install step captured pnpm's output (with no timeout) while leaving stdin attached, so progress and any interactive prompt were hidden — a prompting or slow install looked like a silent hang — and a non-zero exit produced `` `pnpm install` failed in `dist`: `` with nothing after the colon. Fixed via Story K.a.
13
+
14
+ ### Fixed
15
+
16
+ - **`pipeline.py` — `pnpm install` now streams to the terminal.** The install `subprocess.run(...)` dropped `capture_output=True, text=True`, so pnpm inherits the terminal exactly like the sibling `pnpm run dev` call. Download progress and any interactive prompt (pnpm 10 build-script approval, store/lock waits, registry auth) are now visible instead of being piped away into a silent hang.
17
+ - **`pipeline.py` — actionable install-failure message.** The `returncode != 0` branch no longer interpolates the (often empty) captured `result.stderr`. It now reads `` `pnpm install` failed in `<dir>` (exit code N); see the pnpm output above. `` — so a real failure or a Ctrl-C no longer dead-ends at a bare colon.
18
+
19
+ ### Verified
20
+
21
+ - `pyve test` → 413 passed (was 411; +2 new regression tests in `TestRunPreviewInstallVisibility`, which fail pre-fix and pass post-fix).
22
+ - Prevention scan: `grep -rn 'subprocess.run\|capture_output' src/learningfoundry/` returns only the two pipeline calls (install now streams; `pnpm run dev` already streamed). No other instance of the anti-pattern in learningfoundry code.
23
+
10
24
  ## [0.79.2] - 2026-06-02
11
25
 
12
26
  Fix sql.js browser-ESM init failure in the SvelteKit template's dev-server preview path. Module and lesson routes were returning 500 in `learningfoundry preview` because `(await import('sql.js')).default` was `undefined` in the browser. Diagnosed in `docs/specs/bug-sql-js-browser-esm-spec.md`; fixed via Story J.y.
@@ -21,6 +35,7 @@ Fix sql.js browser-ESM init failure in the SvelteKit template's dev-server previ
21
35
  - `pnpm exec vitest run` → 278 passed (was 277; +1 new contract test for the CJS/ESM interop guard).
22
36
  - `pnpm exec svelte-check` → 0 errors, 0 warnings.
23
37
  - `pnpm exec vite build` → succeeds (confirms the gated `optimizeDeps.exclude` doesn't break prod build; the J.w comment's "covers both prod build" framing was incorrect).
38
+ - `pyve test` → 411 passed (Python pipeline regression check; bootstrapped the testenv editable install first since the testenv had been recently reinitialized).
24
39
  - Manual: dev-server `/{moduleId}/{lessonId}` route renders without 500 in the d802-deep-learning consumer (verification scheduled with the consumer team — captured as a `[ ]` follow-up).
25
40
 
26
41
  ## [0.79.1] - 2026-05-22
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learningfoundry
3
- Version: 0.79.2
3
+ Version: 0.79.3
4
4
  Summary: A curriculum engine that turns a YAML curriculum definition into a deployable SvelteKit learning application.
5
5
  Project-URL: Homepage, https://github.com/pointmatic/learningfoundry
6
6
  Project-URL: Repository, https://github.com/pointmatic/learningfoundry
@@ -0,0 +1,176 @@
1
+ # nbfoundry
2
+
3
+ [![CI](https://github.com/pointmatic/nbfoundry/actions/workflows/ci.yml/badge.svg)](https://github.com/pointmatic/nbfoundry/actions/workflows/ci.yml)
4
+ [![codecov](https://codecov.io/gh/pointmatic/nbfoundry/branch/main/graph/badge.svg)](https://codecov.io/gh/pointmatic/nbfoundry)
5
+ [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
6
+
7
+ Marimo-based notebook framework for ML/DS work. One notebook source compiles into two
8
+ artifacts: a standalone runnable application and an `ExerciseBlock`-compatible artifact
9
+ that drops into a learningfoundry curriculum.
10
+
11
+ For the why, see [`docs/specs/concept.md`](docs/specs/concept.md). For the what, see
12
+ [`docs/specs/features.md`](docs/specs/features.md). For the how, see
13
+ [`docs/specs/tech-spec.md`](docs/specs/tech-spec.md).
14
+
15
+ ## Installation
16
+
17
+ `nbfoundry` targets Python 3.12.13 with **Pyve + venv** (exclusively — no conda or
18
+ micromamba). The Metal ML stack is fully pip-installable on Apple Silicon, so each
19
+ scaffolded project ships per-stage pip requirements instead of a conda env file:
20
+
21
+ - [`requirements-base.txt`](src/nbfoundry/templates/requirements-base.txt) — framework-agnostic core (the `data_*` stages).
22
+ - [`requirements-torch.txt`](src/nbfoundry/templates/requirements-torch.txt) — torch-family stack (the `model_*` stages); `-r`-includes the base.
23
+ - [`requirements-tf.txt`](src/nbfoundry/templates/requirements-tf.txt) — TensorFlow-family option; `-r`-includes the base.
24
+
25
+ These ship as package data: `nbfoundry init` copies the **stage-appropriate** file into
26
+ every scaffolded project, and the standalone artifact emitter falls back to
27
+ `requirements-base.txt` when the source carries none.
28
+
29
+ ### Apple Silicon quickstart
30
+
31
+ The stack defaults to Apple Silicon with Metal/MPS acceleration: PyTorch via the bare
32
+ `torch` MPS wheel, TensorFlow via `tensorflow-macos` + `tensorflow-metal`, and the
33
+ bundled Keras 3 namespace from TF 2.16+. The torch stack also ships the wider
34
+ cross-project set (HuggingFace `transformers` / `datasets` / `peft`, Optuna, the
35
+ plotly/seaborn/pyarrow utility set, and the Pointmatic-internal `ml-datarefinery`).
36
+
37
+ Scaffold a project and build its venv with plain pip — no micromamba:
38
+
39
+ ```bash
40
+ nbfoundry init demo --template model_experimentation
41
+ cd demo
42
+ pyve init # creates the project venv
43
+ pip install -r requirements-torch.txt # torch + HuggingFace + Optuna on MPS
44
+ ```
45
+
46
+ A `data_*` scaffold instead emits `requirements-base.txt` (no ML framework). Because
47
+ `torch` and `tensorflow` are never co-installed in one venv, a learner cannot hit the
48
+ PyTorch-MPS / TensorFlow-Metal co-residence crash.
49
+
50
+ > **Framework Metal verification** is done dev-side via the lazy named smoke envs
51
+ > (`pyve test --env smoke-torch …` / `--env smoke-tensorflow …`); see
52
+ > [`docs/specs/env-dependencies.md`](docs/specs/env-dependencies.md).
53
+
54
+ #### Cross-platform users (CUDA / CPU-only)
55
+
56
+ The requirements files ship comment-delimited swap guidance:
57
+
58
+ - **PyTorch CUDA:** install torch from the PyTorch index instead of the bare line, e.g.
59
+ `pip install torch --index-url https://download.pytorch.org/whl/cu128` (`cpu` / `cu126`
60
+ / `cu128`).
61
+ - **TensorFlow CPU-only or Linux+CUDA:** in `requirements-tf.txt`, replace the
62
+ `tensorflow-macos` / `tensorflow-metal` lines with `tensorflow>=2.16` (CPU-only) or
63
+ `tensorflow[and-cuda]>=2.16` (Linux + CUDA).
64
+
65
+ Both notes are documented inline at the top of the relevant requirements file.
66
+
67
+ ### Development setup (Pyve two-env)
68
+
69
+ ```bash
70
+ pyve init
71
+ pyve run pip install -e .
72
+ pyve env init
73
+ pyve env run pip install -e .
74
+ pyve env install -r requirements-dev.txt
75
+ ```
76
+
77
+ Run the suite with `pyve test` (lint: `pyve env run ruff check .`; types:
78
+ `pyve env run mypy`). Hardware smokes are opt-in: `pyve test --env smoke-torch …`
79
+ / `--env smoke-tensorflow … -m hardware`.
80
+
81
+ ## Usage
82
+
83
+ `nbfoundry` exposes four commands. Compile and validate are **offline,
84
+ deterministic, and side-effect-free** — they read only the files you point them at
85
+ and never touch the network.
86
+
87
+ ### 1. Scaffold a notebook — `init`
88
+
89
+ ```bash
90
+ nbfoundry init demo --template data_exploration
91
+ ```
92
+
93
+ `--template` is one of the five lifecycle stages: `data_exploration`,
94
+ `data_preparation`, `model_experimentation`, `model_optimization`,
95
+ `model_evaluation` (defaults to `data_exploration`). The scaffold contains a
96
+ reactive Marimo `notebook.py` plus the stage-appropriate `requirements-*.txt`.
97
+
98
+ ### 2. Run it as a standalone app — `compile`
99
+
100
+ ```bash
101
+ nbfoundry compile demo --out dist
102
+ ```
103
+
104
+ Produces a self-contained artifact directory (the compiled notebook(s), the
105
+ `requirements-*.txt`, and a `launch.py` entry point) that runs locally with no
106
+ server or cloud dependency.
107
+
108
+ ### 3. Compile to a learningfoundry exercise — `compile-exercise`
109
+
110
+ Author an exercise YAML whose sections carry (or reference, via `code_file`) the
111
+ notebook code, plus instructional metadata:
112
+
113
+ ```yaml
114
+ # exercise.yaml
115
+ title: Explore a dataset
116
+ description: Load, describe, and visualize the data.
117
+ sections:
118
+ - title: Load
119
+ description: Read the CSV into a DataFrame.
120
+ code: |
121
+ import pandas as pd
122
+ df = pd.read_csv("data.csv")
123
+ ```
124
+
125
+ ```bash
126
+ nbfoundry compile-exercise exercise.yaml --out exercise.json # or omit --out for stdout
127
+ ```
128
+
129
+ The output is an `ExerciseBlock`-compatible dict (the BR-1 wire shape) that drops
130
+ into a learningfoundry curriculum. The full schema — instructions, hints,
131
+ expected outputs, and the BR-4 `submission` block with client-side grading — is
132
+ defined in [`docs/specs/learningfoundry/dependency-spec.md`](docs/specs/learningfoundry/dependency-spec.md).
133
+
134
+ ### 4. Validate without compiling — `validate`
135
+
136
+ ```bash
137
+ nbfoundry validate exercise.yaml # exit 0 when clean; exit 1 with all errors on stdout
138
+ ```
139
+
140
+ ### Two surfaces from one source (AC-3)
141
+
142
+ The same notebook source feeds **both** outputs: `compile` turns it into a
143
+ runnable standalone app, while `compile-exercise` (whose sections reference that
144
+ same notebook via `code_file`) turns it into an embeddable curriculum exercise —
145
+ no rewrite when the purpose shifts. That dual-surface compile is the core of
146
+ nbfoundry's value (see [`docs/specs/concept.md`](docs/specs/concept.md)).
147
+
148
+ ## Further reading
149
+
150
+ - [`docs/specs/concept.md`](docs/specs/concept.md) — why nbfoundry exists (problem/solution space).
151
+ - [`docs/specs/features.md`](docs/specs/features.md) — what it does (requirements, behavior, acceptance criteria).
152
+ - [`docs/specs/tech-spec.md`](docs/specs/tech-spec.md) — how it is built (architecture, dependencies, testing strategy).
153
+ - [`docs/specs/learningfoundry/dependency-spec.md`](docs/specs/learningfoundry/dependency-spec.md) — the `ExerciseBlock` contract the compiled exercise targets.
154
+ - [`docs/specs/env-dependencies.md`](docs/specs/env-dependencies.md) — the dev/test environment topology.
155
+
156
+ ## Releasing to PyPI
157
+
158
+ Releases ship through [`.github/workflows/publish.yml`](.github/workflows/publish.yml),
159
+ which is triggered by pushing a `v*` tag. The workflow builds an sdist + wheel with
160
+ `hatch build` and publishes via PyPI [trusted publishing](https://docs.pypi.org/trusted-publishers/)
161
+ (OIDC, no long-lived API tokens).
162
+
163
+ One-time PyPI setup: register `nbfoundry` on PyPI and add a pending trusted publisher
164
+ under the project's *Publishing* settings — owner `pointmatic`, repository `nbfoundry`,
165
+ workflow `publish.yml`, environment `pypi`.
166
+
167
+ Per-release procedure:
168
+
169
+ 1. Land the version-bump story on `main` (package version in `src/nbfoundry/_version.py`
170
+ and a matching `CHANGELOG.md` entry).
171
+ 2. Tag the commit with the matching `v<version>` (e.g. `git tag v0.29.0 && git push origin v0.29.0`).
172
+ 3. The workflow verifies the tag matches `hatch version`, builds the distributions, and
173
+ publishes to PyPI under the `pypi` GitHub environment.
174
+
175
+ The workflow refuses to publish if the tag and `hatch version` disagree, so the only way
176
+ to ship a release is to tag the same commit that owns the version bump.