codemble 0.5.2__tar.gz → 0.6.0__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.
- {codemble-0.5.2 → codemble-0.6.0}/.gitignore +1 -0
- {codemble-0.5.2 → codemble-0.6.0}/CHANGELOG.md +82 -0
- {codemble-0.5.2 → codemble-0.6.0}/CLAUDE.md +84 -9
- codemble-0.6.0/CONTEXT.md +28 -0
- {codemble-0.5.2 → codemble-0.6.0}/PKG-INFO +9 -2
- {codemble-0.5.2 → codemble-0.6.0}/README.md +7 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/__init__.py +1 -1
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/base.py +8 -1
- {codemble-0.5.2 → codemble-0.6.0}/codemble/graph/layout.py +158 -32
- {codemble-0.5.2 → codemble-0.6.0}/codemble/graph/mapview.py +196 -8
- {codemble-0.5.2 → codemble-0.6.0}/codemble/server/app.py +69 -241
- codemble-0.6.0/codemble/server/project_activation.py +212 -0
- codemble-0.6.0/codemble/server/project_selection.py +117 -0
- codemble-0.5.2/codemble/web_dist/assets/index-DTjcBtZj.js → codemble-0.6.0/codemble/web_dist/assets/index-BWorxSL3.js +1 -1
- codemble-0.6.0/codemble/web_dist/assets/index-wY0ZuS7b.css +1 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/index.html +2 -2
- codemble-0.6.0/docs/plans/2026-07-20-oss-inspired-map-and-constellations.md +417 -0
- codemble-0.6.0/docs/releases/v0.5.3.md +50 -0
- codemble-0.6.0/docs/research/2026-07-20-opensource-graph-architecture-inspiration.md +192 -0
- codemble-0.6.0/docs/superpowers/specs/2026-07-21-navigation-design-clarity-design.md +183 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/package.json +1 -1
- {codemble-0.5.2 → codemble-0.6.0}/pyproject.toml +7 -2
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/check_suites.json +36 -36
- codemble-0.6.0/tests/test_dependency_compatibility.py +43 -0
- codemble-0.6.0/tests/test_graph_finalization.py +277 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_mapview.py +190 -3
- codemble-0.6.0/tests/test_project_activation.py +68 -0
- codemble-0.6.0/tests/test_project_selection.py +71 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_python_ast.py +2 -2
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_server.py +5 -1
- {codemble-0.5.2 → codemble-0.6.0}/web/package-lock.json +2 -2
- {codemble-0.5.2 → codemble-0.6.0}/web/package.json +3 -2
- codemble-0.6.0/web/scripts/benchmark_learner_projection.mjs +114 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/scripts/check_graph_data.mjs +130 -0
- codemble-0.6.0/web/scripts/check_learner_projection.mjs +165 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/scripts/check_learner_session.mjs +104 -0
- codemble-0.6.0/web/scripts/check_name_atlas.mjs +75 -0
- codemble-0.6.0/web/scripts/check_project_mapping.mjs +189 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/App.jsx +295 -69
- {codemble-0.5.2 → codemble-0.6.0}/web/src/GalaxyCanvas.jsx +133 -20
- {codemble-0.5.2 → codemble-0.6.0}/web/src/GuidanceLayer.jsx +4 -4
- {codemble-0.5.2 → codemble-0.6.0}/web/src/MapView.jsx +201 -12
- {codemble-0.5.2 → codemble-0.6.0}/web/src/galaxyMaterials.js +86 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/graphData.js +189 -23
- codemble-0.6.0/web/src/learnerProjection.js +212 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/learnerSession.js +93 -372
- codemble-0.6.0/web/src/nameAtlas.js +177 -0
- codemble-0.6.0/web/src/projectMapping.js +344 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/styles.css +422 -9
- {codemble-0.5.2 → codemble-0.6.0}/web/src/tokens.css +13 -0
- codemble-0.5.2/codemble/web_dist/assets/index-Dbk76SAB.css +0 -1
- codemble-0.5.2/tests/test_graph_finalization.py +0 -121
- {codemble-0.5.2 → codemble-0.6.0}/.env.example +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.gitattributes +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/ISSUE_TEMPLATE/early_tester.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/dependabot.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/workflows/ci.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/workflows/pages.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/.github/workflows/publish-pypi.yml +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/AGENTS.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/CODE_OF_CONDUCT.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/CONTRIBUTING.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/LICENSE +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/NOTICE +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/SECURITY.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/TESTING.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/assets/demo.gif +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/discovery.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/parse_progress.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/project.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/python_ast.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/adapters/typescript_tree_sitter.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/checks/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/checks/service.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/cli.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/graph/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/graph/finalize.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/lens/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/lens/javascript_typescript.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/lens/python.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/llm/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/llm/local_status.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/llm/providers.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/llm/structural.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/llm/study.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/paths.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/progress/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/progress/store.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/server/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/server/parse_job.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/server/runtime.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/shippori-mincho-latin-500-normal-C-QwvIb3.woff +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/shippori-mincho-latin-500-normal-XI1O8euf.woff2 +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/shippori-mincho-latin-700-normal-CkoCYOiI.woff +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/shippori-mincho-latin-700-normal-DHcmzUO5.woff2 +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/zen-kaku-gothic-new-latin-400-normal-BEdayliK.woff2 +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/codemble/web_dist/assets/zen-kaku-gothic-new-latin-400-normal-CPSmNJAU.woff +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/adr/README.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/2026-07-19-audience-modes-and-local-narration-design.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/2026-07-19-audience-modes-backend-plan.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/2026-07-19-audience-modes-frontend-plan.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/2026-07-19-install-ux-folder-picker-design.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/2026-07-19-install-ux-folder-picker-plan.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/README.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/plans/phase-1.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/checklist.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/v0.1.0.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/v0.2.0.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/v0.4.0.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/v0.5.0.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/v0.5.1.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/releases/v0.5.2.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/research/README.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/superpowers/plans/2026-07-19-galaxy-ux-phase-a.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/superpowers/plans/2026-07-19-galaxy-ux-phase-b.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/superpowers/plans/2026-07-19-galaxy-ux-phase-c.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/superpowers/plans/2026-07-19-galaxy-ux-shared-contract.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs/superpowers/specs/2026-07-19-galaxy-ux-overhaul-design.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/.gitignore +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/astro.config.mjs +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/design.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/favicon-16.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/favicon-32.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/icon-180.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/icon-192.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/icon-512.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/icon.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/icons/asterism.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/icons/install.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/mark-animated.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/mark.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/hero-chart.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/hero-field.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/hero-gold.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/kasumi-rule.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/plate-galaxy.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/plate-study.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/plate-system.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/brand/plates/seal.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/favicon.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/easy-mode.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/galaxy-lit.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/galaxy.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/loading.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/map-architecture.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/map-workflow.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/study-panel.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/public/shots/system.png +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/scripts/build-plates.mjs +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/assets/codemble-mark-dark.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/assets/codemble-mark-light.svg +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/components/Search.astro +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/components/Tatebanko.astro +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/architecture.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/build-from-source.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/checks-and-lighting.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/contributing.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/correctness.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/early-testing.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/installation.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/introduction.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m1-parser.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m10-polyglot-release.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m12-galaxy-look.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m13-scale.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m2-galaxy.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m3-study.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m4-lens.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m5-checks.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m6-release.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m8-typescript.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/progress/m9-typescript-lens.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/quickstart.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/roadmap.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/star-chart.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/study-panel.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content/docs/the-galaxy.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/content.config.ts +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/pages/index.astro +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/styles/custom.css +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/styles/landing.css +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/docs-site/src/styles/tokens.css +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/scripts/dev.sh +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/scripts/record_demo.sh +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/conftest.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/concepts_sample.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/impact/alpha.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/impact/beta.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/impact/gamma.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/impact/helpers.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/impact/noisy.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/python_worker.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/broken.ts +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/legacy.js +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/local.js +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/main.ts +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/reexport.ts +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/util.ts +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/polyglot/src/widget.tsx +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/repeated_calls.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/.gitignore +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/ambiguous.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/api.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/app.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/broken.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/cli.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/pkg/__init__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/pkg/helpers.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/pkg/service.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/pkg/util.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/runner/__main__.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/fixtures/sampleproj/shared.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_checks.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_parse_job.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_parse_progress.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_progress.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_project_parser.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_providers.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_smoke.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_structural.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_study.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/tests/test_typescript_tree_sitter.py +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/README.md +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/index.html +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/scripts/record_demo.mjs +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/ModeControl.jsx +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/StudyPanel.jsx +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/galaxyEffects.js +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/src/main.jsx +0 -0
- {codemble-0.5.2 → codemble-0.6.0}/web/vite.config.js +0 -0
|
@@ -5,6 +5,88 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning:
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.6.0] - 2026-07-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **You can move the camera.** Dragging now orbits around whatever you are
|
|
12
|
+
looking at, and the wheel zooms. Panning stays off and both rotation and zoom
|
|
13
|
+
are clamped per level, so the thing you are studying can never leave the
|
|
14
|
+
screen — "you cannot get lost" still holds. Changing level is now click,
|
|
15
|
+
Enter, Escape, or the breadcrumb, because one gesture cannot mean both zoom
|
|
16
|
+
and travel.
|
|
17
|
+
- **Systems have names.** The most important stars carry their filename on the
|
|
18
|
+
canvas — Home first, then modules you have proved, then the ones most of your
|
|
19
|
+
project calls — with more names appearing as you zoom in and none allowed to
|
|
20
|
+
overlap another.
|
|
21
|
+
- **Find any module by name.** `⌘K` / `Ctrl-K` opens a filter-as-you-type jump
|
|
22
|
+
list, and a new Modules rail lists every file grouped by the import
|
|
23
|
+
communities the galaxy already places together. Both reach every module,
|
|
24
|
+
including ones the sky has not charted yet.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Project selection, project activation, project mapping, the galaxy Name
|
|
28
|
+
Atlas, and the Learner Projection now each sit behind one focused module
|
|
29
|
+
interface. The HTTP API, parser graph, checks, persistence, and learner-visible
|
|
30
|
+
interactions are unchanged; focused contract suites pin each new seam.
|
|
31
|
+
- Learner projections are now dependency-scoped and indexed instead of being
|
|
32
|
+
recomputed on every session commit. A 1,000-node hover benchmark dropped from
|
|
33
|
+
about 0.331 ms to 0.001 ms per commit, and hover-only commits preserve the
|
|
34
|
+
identity of every unaffected graph, map, chart, reveal, hint, and module-index
|
|
35
|
+
projection so the 3D renderer does not rebuild stable scene data.
|
|
36
|
+
- **The galaxy no longer draws your whole project at once.** It opens on Home
|
|
37
|
+
and everything within two import routes of it; proving a module reveals what
|
|
38
|
+
it connects to, so the map is uncovered by understanding. Modules you have not
|
|
39
|
+
reached are still drawn — faint, unnamed, and without their routes — so the
|
|
40
|
+
project's real size is never misreported, and they stay clickable. A
|
|
41
|
+
**Show all** toggle draws everything whenever you want it. On a 90-system
|
|
42
|
+
project this is 22 systems and their routes instead of 90 and theirs.
|
|
43
|
+
- **The canvas got its stage back.** The display-size heading that covered the
|
|
44
|
+
left half of the view is now one line of text (`90 systems · 22 charted`), the
|
|
45
|
+
module path at system level lives in the breadcrumb instead of wrapping across
|
|
46
|
+
three lines, and the twelve-row legend is behind a **Key** button.
|
|
47
|
+
- **Map boxes say which module they are.** A box is a fixed width, and it used
|
|
48
|
+
to truncate the dotted module id — so `codemble.server.app` and
|
|
49
|
+
`codemble.server.runtime` both read `codemble.server…`, two different modules
|
|
50
|
+
showing identical text. Boxes are now named by the tail of their real path
|
|
51
|
+
(`server/app.py`, `server/runtime.py`); the full identifier is still in the
|
|
52
|
+
tooltip and read out to screen readers.
|
|
53
|
+
- **You can zoom and pan the Map.** The architecture diagram is far taller than
|
|
54
|
+
the window — on this project, nine import layers of which about four were
|
|
55
|
+
visible. Zoom in and out, drag empty space to pan, or press **Fit** to see the
|
|
56
|
+
whole shape at once. Every coordinate is still the one the backend computed;
|
|
57
|
+
zoom only changes how large it is drawn.
|
|
58
|
+
- The 2D Architecture map is now a deterministic layered diagram rather than
|
|
59
|
+
a straight-line grid: four barycenter sweeps reduce import-edge crossings,
|
|
60
|
+
backend-assigned ports keep fan-out distinct, weighted arrowed paths expose
|
|
61
|
+
direction, and cycles or long routes travel around the diagram flank. Possible
|
|
62
|
+
relationships remain dashed, and React still consumes graph-owned geometry.
|
|
63
|
+
- Galaxy systems now form deterministic constellations from parser-proven
|
|
64
|
+
import communities instead of scattering solely by identifier hash. The
|
|
65
|
+
additive `community` region field is exposed in graph schema 5 for later
|
|
66
|
+
renderers, while progress, Home, and system-internal orbital layout stay
|
|
67
|
+
unchanged.
|
|
68
|
+
|
|
69
|
+
## [0.5.3] - 2026-07-21
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
- **A fresh install crashed on every JavaScript/TypeScript project.** The
|
|
73
|
+
dependency range allowed `tree-sitter` 0.26.0, which is ABI-incompatible with
|
|
74
|
+
the newest published grammar wheels (`tree-sitter-javascript` 0.25.0,
|
|
75
|
+
`tree-sitter-typescript` 0.23.2 — the only ones that exist). Partway through a
|
|
76
|
+
real project the parser died with a SIGSEGV inside `node_get_named_children`,
|
|
77
|
+
taking the whole process down with it: `codemble <path>` exited with no
|
|
78
|
+
traceback, and the app's local server vanished mid-parse, so the loading
|
|
79
|
+
screen reported "Lost contact with the local server" and stopped there.
|
|
80
|
+
Bisected on one machine, one project, one Python: 0.26.0 crashes every run,
|
|
81
|
+
0.25.2 is clean. The core is now pinned `<0.26`.
|
|
82
|
+
An install that already had 0.25.x kept working, which is why this stayed
|
|
83
|
+
invisible from a developer checkout — it only reached people installing
|
|
84
|
+
fresh, including through the documented `uvx codemble` path.
|
|
85
|
+
- A dependency guard now asserts the resolved `tree-sitter` version stays below
|
|
86
|
+
the segfaulting release. A behavioural test cannot catch this: a small snippet
|
|
87
|
+
parses and walks fine on 0.26.0, and every fixture in the suite is small,
|
|
88
|
+
which is exactly how it reached a release.
|
|
89
|
+
|
|
8
90
|
## [0.5.2] - 2026-07-20
|
|
9
91
|
|
|
10
92
|
### Fixed
|
|
@@ -167,13 +167,22 @@ Polish, then the coordinated launch (Show HN / X; lit-galaxy GIF as hero).
|
|
|
167
167
|
|
|
168
168
|
## Current State **[AGENT-MAINTAINED]**
|
|
169
169
|
|
|
170
|
-
**Current milestone: Phase 1 tester evidence** · Last updated: 2026-07-
|
|
171
|
-
Session note: architecture-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
170
|
+
**Current milestone: Phase 1 tester evidence** · Last updated: 2026-07-21 ·
|
|
171
|
+
Session note: the v0.6.0 architecture-depth pass is complete in five
|
|
172
|
+
behavior-preserving waves: project selection owns the home-jailed filesystem
|
|
173
|
+
policy; project activation atomically owns parse-to-live binding and graph/map
|
|
174
|
+
caches; project mapping owns picker attempts, polling, retry, outage, stale
|
|
175
|
+
responses, and release; the Name Atlas owns deterministic plate placement; and
|
|
176
|
+
the indexed Learner Projection reuses every unaffected derived view. The
|
|
177
|
+
1,000-node hover benchmark moved from ~0.331 ms to ~0.001 ms per commit, while
|
|
178
|
+
the HTTP, graph, check, persistence, and UI contracts stayed fixed. The current
|
|
179
|
+
milestone does not advance: issue #13 still requires human tester evidence.
|
|
180
|
+
Earlier architecture-deepening maintenance completed after the verified v0.2.0
|
|
181
|
+
release; all four report recommendations merged in phases. The public site was
|
|
182
|
+
then redesigned to the Formal Edo palette and Edo star-atlas genre, with an
|
|
183
|
+
expanding Pagefind search shared by the landing and docs; no parser, graph,
|
|
184
|
+
checks, persistence, or app behaviour was touched. A tester-run rehearsal of
|
|
185
|
+
the shipped loop then
|
|
177
186
|
verified Home calibration, study source, checks, illumination, and restart
|
|
178
187
|
persistence end to end, and found one real defect: multi-answer checks with four
|
|
179
188
|
or more answers offered no wrong option, so select-all lit a region without
|
|
@@ -230,7 +239,42 @@ rebind a released project. Phase C plus that gap-fix wave shipped as **v0.5.0**;
|
|
|
230
239
|
the parse work collided with an independent implementation of the same three
|
|
231
240
|
foundational commits on `main`, reconciled by taking the branch's verified
|
|
232
241
|
superset while preserving main's unique `CODEMBLE_DATA_DIR`/config-isolation
|
|
233
|
-
fix, which lived in files the branch never touched.
|
|
242
|
+
fix, which lived in files the branch never touched. The Architecture map now
|
|
243
|
+
uses deterministic barycenter ordering and backend-routed, directional,
|
|
244
|
+
weight-scaled SVG paths; cycle and long-span routes use clear flank corridors,
|
|
245
|
+
while possible relationships remain dashed and React remains a pure renderer.
|
|
246
|
+
Galaxy regions now place in deterministic constellations derived only from
|
|
247
|
+
parser-proven import communities, with the community ID exposed in graph schema
|
|
248
|
+
5 and progress signatures remaining coordinate-independent. A tester then
|
|
249
|
+
reported the 169-system galaxy unnavigable and undifferentiated, which resolved
|
|
250
|
+
into four separate defects: the camera could not move at all, no star carried a
|
|
251
|
+
name and there was no search or index, every region route drew unconditionally
|
|
252
|
+
so the mesh outshone the stars, and a display-size heading plus a twelve-row
|
|
253
|
+
always-on legend covered the stage. All four are fixed — bounded orbit,
|
|
254
|
+
progressive reveal keyed to a new `hops_from_home` graph field (schema 6),
|
|
255
|
+
ranked and decluttered name plates, a command palette plus an index sidebar over
|
|
256
|
+
one shared module index, and chrome demoted to a single line with the legend
|
|
257
|
+
behind a disclosure. On this repository the default galaxy went from 90 systems
|
|
258
|
+
with their whole route mesh to 22 charted with the rest drawn faint, unnamed and
|
|
259
|
+
edgeless; nothing was removed from the graph and no region re-dimmed. Four
|
|
260
|
+
defects were caught by running it rather than by the suite: a sprite map cleared
|
|
261
|
+
by an effect that ran after the one that filled it, an undefined constant that
|
|
262
|
+
threw inside the declutter timer and silently erased every name, plates that
|
|
263
|
+
claimed one screen cell regardless of their real width, and an open sidebar
|
|
264
|
+
occluding the system panel's primary action. A fifth followed: labels offered
|
|
265
|
+
only one position each, directly above their star, so at galaxy zoom nearly
|
|
266
|
+
every plate lost its slot to a neighbour and a 90-system sky carried one name.
|
|
267
|
+
Names now try a short list of slots around the star and collision-test where the
|
|
268
|
+
plate actually draws rather than where its star sits — 1 name became roughly 24
|
|
269
|
+
with everything shown, 9 by default. The same navigation and clarity pass was
|
|
270
|
+
then applied to the Map layer, where two of the three galaxy problems turned out
|
|
271
|
+
to exist in a sharper form: a fixed-width box truncated the dotted region id, so
|
|
272
|
+
`codemble.server.app` and `codemble.server.runtime` both rendered as
|
|
273
|
+
`codemble.server…` — identical text for different modules — and a 960x2640
|
|
274
|
+
diagram sat in a plain scroll box showing four of its nine import layers. Boxes
|
|
275
|
+
are now named by the tail of their real path (map schema 3, zero visible-text
|
|
276
|
+
collisions across all 90 boxes on this repository) and the Map gained zoom, Fit,
|
|
277
|
+
and drag-to-pan. Progressive reveal was deliberately not extended to the Map.
|
|
234
278
|
|
|
235
279
|
### M0 — Repo, docs & website scaffold ✅ (2026-07-19)
|
|
236
280
|
- [x] Root: README, LICENSE (Apache-2.0), CoC, SECURITY, CONTRIBUTING,
|
|
@@ -396,6 +440,23 @@ interactive galaxy; re-fetching the graph does not re-sort the world; the scale
|
|
|
396
440
|
prompt is actionable entirely in-app; generated check suites are byte-identical
|
|
397
441
|
to before the index change.
|
|
398
442
|
|
|
443
|
+
### M14 — Architecture depth and indexed learner views ✅ (2026-07-21)
|
|
444
|
+
- [x] Put canonical browse-root resolution, folder listing, and recent-project
|
|
445
|
+
filtering behind `ProjectSelector`
|
|
446
|
+
- [x] Make parse-to-live binding, stale-worker refusal, release, and graph/map
|
|
447
|
+
cache lifetime atomic behind `ProjectActivation`
|
|
448
|
+
- [x] Put picker attempts, parse polling/backoff, retry, outage, reset, and
|
|
449
|
+
stale-response guards behind one Project Mapping Run
|
|
450
|
+
- [x] Put name ranking, camera budget, projection, slots, collision cells,
|
|
451
|
+
sprite metadata, and cleanup behind one deterministic Name Atlas
|
|
452
|
+
- [x] Index learner projections by their real dependencies and prove hover-only
|
|
453
|
+
commits reuse stable outputs; benchmark the 1,000-node case
|
|
454
|
+
|
|
455
|
+
**Acceptance:** public HTTP payloads and parser/check/persistence contracts are
|
|
456
|
+
unchanged; focused module suites and the existing end-to-end session/server
|
|
457
|
+
suites pass; the production SPA is rebuilt; the 1,000-node projection benchmark
|
|
458
|
+
shows lower repeated-commit work without changing derived values.
|
|
459
|
+
|
|
399
460
|
## Decision Log **[AGENT-MAINTAINED — append only]**
|
|
400
461
|
|
|
401
462
|
| Date | Decision | Why |
|
|
@@ -462,10 +523,24 @@ to before the index change.
|
|
|
462
523
|
| 2026-07-20 | Progress reporting is a thread-scoped per-file hook (`note_file_parsed`) bound by `ProjectParser`, not a new `LanguageAdapter` parameter | The public adapter seam must stay unchanged for Phase 2 languages; one hook site per adapter also gives cancellation its exact "between files" meaning |
|
|
463
524
|
| 2026-07-20 | Phase C adds `DELETE /api/progress`, the `CLEAR_PROGRESS` session event, and a `clearProgress` adapter method beyond the shared contract's Phase C rows | The contract's Phase C rows covered parse progress only, while the no-reset-progress-control gap is mapped to Phase C by the spec; recorded here rather than silently widened |
|
|
464
525
|
| 2026-07-20 | Generated check suites are pinned by a committed golden fixture before any performance work touches `checks/service.py` | The Correctness Contract makes suite drift top-severity, and a refactor that changes an answer is invisible without a byte-level pin |
|
|
526
|
+
| 2026-07-20 | Architecture map edges get backend-computed ports, barycenter ordering, arrowheads, and weight-scaled strokes; `MAP_SCHEMA_VERSION` 2; directory groups stay payload metadata | Within-layer order was arbitrary and direction was invisible in 2D while being parser truth; ordering stays deterministic (fixed sweeps, sorted ties); group containers wait for hierarchical layout |
|
|
527
|
+
| 2026-07-20 | Galaxy regions place by deterministic import-community constellations (pure-Python label propagation in `layout.py`); `community` is an additive Region field; layout bytes change once | Hash-order placement scattered coupled modules; communities are parser-truth-derived and deterministic; progress signatures hash file content so nothing re-dims (M12 precedent) |
|
|
528
|
+
| 2026-07-21 | **Bounded orbit** replaces the fixed camera, amending the free-flight Non-Goal: `controlType('orbit')` with panning disabled, per-level distance clamps, and clamped polar angle. The wheel becomes zoom; level changes move to click/Enter/Escape/breadcrumb | Approved by UD after a tester reported the galaxy unnavigable. Panning is the one degree of freedom that can strand a learner in empty space with nothing to navigate back by, so it stays off — rotation and zoom are clamped instead, which keeps "you cannot get lost" true. One gesture cannot mean both zoom and change-level, so the wheel's old meaning had to move |
|
|
529
|
+
| 2026-07-21 | Galaxy uses **progressive reveal**: floor (within 2 import hops of Home) ∪ neighbours of every lit region ∪ the current selection's neighbours, with a persisted Show-all toggle. An unrevealed region is drawn faint, unnamed, edgeless — never removed | Approved by UD. 169 systems and their whole route mesh was the hairball; dropping the *edges* of what is not yet charted thins the sky without a separate density control. Regions stay drawn and clickable because hiding one would misreport the project's size, which is precisely the kind of wrong a learner cannot detect. Reveal is recomputed from proven progress, never stored, so it cannot drift out of step with it |
|
|
530
|
+
| 2026-07-21 | `Region.hops_from_home` is graph-layer truth (schema 6): undirected BFS from Home over proven import routes, `None` when unreachable; `with_entrypoint` recomputes it | Reveal is game logic and belongs in `LearnerSession`, but the *distance* is a fact about the project and belongs in the graph. The frontend was already re-walking this exact BFS for the Easy-mode hint, so the two could in principle have disagreed about one number; there is now one source. `None` is never softened to a large number, or "unreachable" would read as "very far" |
|
|
531
|
+
| 2026-07-21 | Canvas name plates are ranked (Home → lit → centrality), budgeted by camera distance, and decluttered by claiming the full screen-cell rectangle each plate covers | A name is the cheapest differentiation there is and the sky had none. Claiming one cell per plate let a wide name cover three neighbours, and claiming only a row let two plates straddling a boundary collide — the rectangle is the only version that actually holds. Plate geometry is published on the sprite by the module that sizes it, so the constant is not duplicated across files |
|
|
532
|
+
| 2026-07-21 | Finding a module is a command palette **and** an index sidebar over one shared `moduleIndex`; sidebar rows show each path minus its group's shared prefix | Approved by UD. Progressive reveal makes targeted retrieval mandatory — a thinned sky must never hide a module from someone who knows its name — and both surfaces reach every module whether charted or not. Basenames alone are useless in a Python project where every package carries an `__init__.py`, so rows keep enough real path to be told apart |
|
|
533
|
+
| 2026-07-21 | Progressive reveal stays **galaxy-only**; the Map always draws every module | Approved by UD when the navigation work was extended to the Map. The Map's job is "how it all fits together", and a layered import diagram with holes in it teaches less than a complete one; the galaxy already offers the thinned view for learners who want it |
|
|
534
|
+
| 2026-07-21 | Architecture boxes are named by the tail of their file path (`short_label`, map schema 3); `label` keeps the full identifier for title and aria | A box is a fixed width, so its text always truncates on a real project — and truncating a dotted region id rendered `codemble.server.app` and `codemble.server.runtime` as the same glyphs. Identical text for different modules is worse than no label, and it is exactly the kind of wrong a learner cannot detect. The path tail also survives the `__init__.py` collision a basename alone cannot |
|
|
535
|
+
| 2026-07-21 | The Map gains zoom, Fit, and drag-to-pan; panning rides the container's own scroll and zoom only scales the rendered size | The 2D counterpart of bounded orbit: a 960x2640 diagram in a plain scroll box showed four of nine layers and no way to see the whole shape. Scroll-based panning keeps native scrollbars, keyboard scrolling and screen-reader behaviour intact, and because every coordinate inside the SVG stays backend-computed, React remains a pure renderer of graph-owned geometry. It opens at true size rather than auto-fitting: fitting on mount measured the scroller before layout settled and landed on a scale that was neither fitted nor honest |
|
|
536
|
+
| 2026-07-21 | v0.6.0 deepens five private boundaries without changing the HTTP, graph, check, persistence, or learner-visible contracts: Project Selection, Project Activation, Project Mapping Run, Name Atlas, and Learner Projection | Approved by UD as five behavior-preserving waves in one release PR. The deletion test now holds at each seam, stale activation and mapping responses lose atomically, and dependency-scoped learner projections measured ~0.331 ms → ~0.001 ms per hover commit on a synthetic 1,000-node project while preserving derived outputs |
|
|
465
537
|
|
|
466
538
|
## Non-Goals — do NOT build (point here when asked)
|
|
467
539
|
|
|
468
|
-
- ❌ Free-flight 3D navigation —
|
|
540
|
+
- ❌ ~~Free-flight 3D navigation~~ — superseded 2026-07-21: **bounded orbit** is
|
|
541
|
+
approved (see Decision Log). The camera may rotate and zoom around the current
|
|
542
|
+
subject; panning, free translation, and any control that can leave the subject
|
|
543
|
+
off screen remain out
|
|
469
544
|
- ❌ XP, streaks, levels, leaderboards
|
|
470
545
|
- ❌ ~~A second 2D renderer/toggle in v1~~ — superseded 2026-07-19: the 2D Map layer is approved (see Decision Log); free-form/client-computed 2D layouts remain out
|
|
471
546
|
- ❌ Accounts, cloud hosting, multi-user; share link waits for Phase 3
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Codemble domain model
|
|
2
|
+
|
|
3
|
+
## Project Selection
|
|
4
|
+
|
|
5
|
+
The learner's choice of one local folder, constrained to Codemble's allowed
|
|
6
|
+
browse root, that may become the current project.
|
|
7
|
+
|
|
8
|
+
## Project Activation
|
|
9
|
+
|
|
10
|
+
The atomic transition from a successfully parsed project to the live checks,
|
|
11
|
+
study, graph, and map experience. Releasing a project cancels any unfinished
|
|
12
|
+
activation and leaves no project bound.
|
|
13
|
+
|
|
14
|
+
## Project Mapping Run
|
|
15
|
+
|
|
16
|
+
The learner-visible run that begins when a selected folder is accepted and
|
|
17
|
+
ends when its project activates, parsing fails, or the learner releases it.
|
|
18
|
+
|
|
19
|
+
## Name Atlas
|
|
20
|
+
|
|
21
|
+
The deterministic set of visible names placed around charted stars for the
|
|
22
|
+
current camera view, prioritizing Home, understood systems, and the learner's
|
|
23
|
+
current pointer subject without allowing name plates to collide.
|
|
24
|
+
|
|
25
|
+
## Learner Projection
|
|
26
|
+
|
|
27
|
+
The learner-facing view of the current project after language focus, proven
|
|
28
|
+
progress, navigation selection, and view preferences are applied.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codemble
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: A learning game that turns the code AI wrote for you into a galaxy you light up by understanding it.
|
|
5
5
|
Project-URL: Homepage, https://udhawan97.github.io/Codemble/
|
|
6
6
|
Project-URL: Repository, https://github.com/udhawan97/Codemble
|
|
@@ -22,7 +22,7 @@ Requires-Python: >=3.11
|
|
|
22
22
|
Requires-Dist: fastapi>=0.115
|
|
23
23
|
Requires-Dist: tree-sitter-javascript<0.26,>=0.25
|
|
24
24
|
Requires-Dist: tree-sitter-typescript<0.24,>=0.23.2
|
|
25
|
-
Requires-Dist: tree-sitter<0.
|
|
25
|
+
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
26
26
|
Requires-Dist: uvicorn>=0.30
|
|
27
27
|
Provides-Extra: dev
|
|
28
28
|
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
@@ -322,6 +322,13 @@ The [public roadmap](https://udhawan97.github.io/Codemble/roadmap/) separates
|
|
|
322
322
|
shipped work from planned work. Milestones move only when their acceptance
|
|
323
323
|
evidence exists.
|
|
324
324
|
|
|
325
|
+
## Acknowledgements
|
|
326
|
+
|
|
327
|
+
- [dagre](https://github.com/dagrejs/dagre) and [Eclipse ELK](https://github.com/eclipse-elk/elk) for the layered-diagram approach.
|
|
328
|
+
- [tt-a1i/archify](https://github.com/tt-a1i/archify) for 2D architecture-diagram inspiration.
|
|
329
|
+
- [Graphify](https://github.com/Graphify-Labs/graphify) for the community-constellation idea.
|
|
330
|
+
- Codemble's shipped open-source stack: [3d-force-graph](https://github.com/vasturiano/3d-force-graph), [tree-sitter](https://github.com/tree-sitter/tree-sitter), [FastAPI](https://github.com/fastapi/fastapi), [Vite](https://github.com/vitejs/vite), and [React](https://github.com/facebook/react).
|
|
331
|
+
|
|
325
332
|
## License
|
|
326
333
|
|
|
327
334
|
Codemble is released under the [Apache License 2.0](https://github.com/udhawan97/Codemble/blob/main/LICENSE).
|
|
@@ -290,6 +290,13 @@ The [public roadmap](https://udhawan97.github.io/Codemble/roadmap/) separates
|
|
|
290
290
|
shipped work from planned work. Milestones move only when their acceptance
|
|
291
291
|
evidence exists.
|
|
292
292
|
|
|
293
|
+
## Acknowledgements
|
|
294
|
+
|
|
295
|
+
- [dagre](https://github.com/dagrejs/dagre) and [Eclipse ELK](https://github.com/eclipse-elk/elk) for the layered-diagram approach.
|
|
296
|
+
- [tt-a1i/archify](https://github.com/tt-a1i/archify) for 2D architecture-diagram inspiration.
|
|
297
|
+
- [Graphify](https://github.com/Graphify-Labs/graphify) for the community-constellation idea.
|
|
298
|
+
- Codemble's shipped open-source stack: [3d-force-graph](https://github.com/vasturiano/3d-force-graph), [tree-sitter](https://github.com/tree-sitter/tree-sitter), [FastAPI](https://github.com/fastapi/fastapi), [Vite](https://github.com/vitejs/vite), and [React](https://github.com/facebook/react).
|
|
299
|
+
|
|
293
300
|
## License
|
|
294
301
|
|
|
295
302
|
Codemble is released under the [Apache License 2.0](https://github.com/udhawan97/Codemble/blob/main/LICENSE).
|
|
@@ -89,6 +89,13 @@ class Region:
|
|
|
89
89
|
x: float
|
|
90
90
|
y: float
|
|
91
91
|
z: float
|
|
92
|
+
community: int
|
|
93
|
+
# Undirected breadth-first distance from the Home region over proven import
|
|
94
|
+
# routes. ``None`` means no route reaches this region -- or that no Home is
|
|
95
|
+
# selected yet -- and is never softened into a large number, because the
|
|
96
|
+
# renderer reveals the sky by this distance and a guessed one would quietly
|
|
97
|
+
# promise a path the parser never found.
|
|
98
|
+
hops_from_home: int | None = None
|
|
92
99
|
|
|
93
100
|
|
|
94
101
|
@dataclass(frozen=True, slots=True)
|
|
@@ -115,7 +122,7 @@ class Graph:
|
|
|
115
122
|
regions: tuple[Region, ...] = ()
|
|
116
123
|
region_edges: tuple[RegionEdge, ...] = ()
|
|
117
124
|
partial_files: tuple[str, ...] = ()
|
|
118
|
-
schema_version: int = field(default=
|
|
125
|
+
schema_version: int = field(default=6, init=False)
|
|
119
126
|
|
|
120
127
|
def to_dict(self) -> dict[str, object]:
|
|
121
128
|
"""Return a JSON-ready representation in canonical collection order."""
|
|
@@ -24,22 +24,72 @@ def layout_graph(graph: Graph) -> Graph:
|
|
|
24
24
|
for node in graph.nodes:
|
|
25
25
|
grouped[node.region].append(node)
|
|
26
26
|
|
|
27
|
+
node_by_id = {node.id: node for node in graph.nodes}
|
|
28
|
+
|
|
29
|
+
routes: dict[tuple[str, str], list[bool]] = defaultdict(list)
|
|
30
|
+
for edge in graph.edges:
|
|
31
|
+
if edge.kind != "import" or edge.external:
|
|
32
|
+
continue
|
|
33
|
+
src_node = node_by_id.get(edge.src)
|
|
34
|
+
dst_node = node_by_id.get(edge.dst)
|
|
35
|
+
if src_node is None or dst_node is None or src_node.region == dst_node.region:
|
|
36
|
+
continue
|
|
37
|
+
routes[(src_node.region, dst_node.region)].append(edge.certain)
|
|
38
|
+
|
|
39
|
+
# ``all``, deliberately, where ``mapview.py``'s ``_workflow`` uses ``any``
|
|
40
|
+
# for a call pair. The two marks claim different things. A workflow row
|
|
41
|
+
# asserts only that a relationship exists, so one proven call site settles
|
|
42
|
+
# it and the ambiguous ones beside it change nothing. A route is a single
|
|
43
|
+
# line standing in for ``weight`` imports, so calling it certain asserts
|
|
44
|
+
# every one of them: one unproven import among them and the whole route
|
|
45
|
+
# drops to possible. Under-claiming is the only direction that is safe
|
|
46
|
+
# when one mark speaks for many edges.
|
|
47
|
+
region_edges = tuple(
|
|
48
|
+
RegionEdge(src=src, dst=dst, weight=len(certainties), certain=all(certainties))
|
|
49
|
+
for (src, dst), certainties in sorted(routes.items())
|
|
50
|
+
)
|
|
51
|
+
|
|
27
52
|
region_order = sorted(grouped, key=lambda region_id: (_digest(region_id), region_id))
|
|
53
|
+
communities = _communities(tuple(grouped), region_edges)
|
|
54
|
+
community_members: dict[int, list[str]] = defaultdict(list)
|
|
55
|
+
for region_id in region_order:
|
|
56
|
+
community_members[communities[region_id]].append(region_id)
|
|
57
|
+
|
|
58
|
+
region_positions: dict[str, tuple[float, float, float]] = {}
|
|
59
|
+
cumulative_members = 0
|
|
60
|
+
for community in sorted(community_members):
|
|
61
|
+
members = community_members[community]
|
|
62
|
+
community_angle = community * _GOLDEN_ANGLE
|
|
63
|
+
community_radius = 42.0 + 54.0 * math.sqrt(cumulative_members)
|
|
64
|
+
community_x = math.cos(community_angle) * community_radius
|
|
65
|
+
community_z = math.sin(community_angle) * community_radius
|
|
66
|
+
for member_index, region_id in enumerate(members):
|
|
67
|
+
local_angle = (
|
|
68
|
+
member_index * _GOLDEN_ANGLE
|
|
69
|
+
+ _fraction(region_id, "phase") * 0.18
|
|
70
|
+
)
|
|
71
|
+
local_radius = 16.0 + 12.0 * math.sqrt(member_index)
|
|
72
|
+
region_positions[region_id] = (
|
|
73
|
+
_rounded(community_x + math.cos(local_angle) * local_radius),
|
|
74
|
+
_rounded(((_fraction(region_id, "height") * 2.0) - 1.0) * 28.0),
|
|
75
|
+
_rounded(community_z + math.sin(local_angle) * local_radius),
|
|
76
|
+
)
|
|
77
|
+
cumulative_members += len(members)
|
|
78
|
+
|
|
79
|
+
home_region = next(
|
|
80
|
+
(node.region for node in graph.nodes if node.id == graph.selected_entrypoint),
|
|
81
|
+
None,
|
|
82
|
+
)
|
|
83
|
+
hops = _hops_from_home(tuple(grouped), region_edges, home_region)
|
|
84
|
+
|
|
28
85
|
regions: list[Region] = []
|
|
29
86
|
positioned_nodes: list[Node] = []
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for region_index, region_id in enumerate(region_order):
|
|
87
|
+
for region_id in region_order:
|
|
33
88
|
members = sorted(
|
|
34
89
|
grouped[region_id],
|
|
35
90
|
key=lambda node: (node.kind != "module", node.id),
|
|
36
91
|
)
|
|
37
|
-
|
|
38
|
-
region_radius = 42.0 + 54.0 * math.sqrt(region_index)
|
|
39
|
-
region_x = _rounded(math.cos(region_angle) * region_radius)
|
|
40
|
-
region_y = _rounded(((_fraction(region_id, "height") * 2.0) - 1.0) * 28.0)
|
|
41
|
-
region_z = _rounded(math.sin(region_angle) * region_radius)
|
|
42
|
-
|
|
92
|
+
region_x, region_y, region_z = region_positions[region_id]
|
|
43
93
|
module_nodes = [node for node in members if node.kind == "module"]
|
|
44
94
|
loc = sum(node.loc for node in module_nodes) or sum(node.loc for node in members)
|
|
45
95
|
regions.append(
|
|
@@ -54,6 +104,8 @@ def layout_graph(graph: Graph) -> Graph:
|
|
|
54
104
|
x=region_x,
|
|
55
105
|
y=region_y,
|
|
56
106
|
z=region_z,
|
|
107
|
+
community=communities[region_id],
|
|
108
|
+
hops_from_home=hops.get(region_id),
|
|
57
109
|
)
|
|
58
110
|
)
|
|
59
111
|
|
|
@@ -85,28 +137,6 @@ def layout_graph(graph: Graph) -> Graph:
|
|
|
85
137
|
)
|
|
86
138
|
)
|
|
87
139
|
|
|
88
|
-
routes: dict[tuple[str, str], list[bool]] = defaultdict(list)
|
|
89
|
-
for edge in graph.edges:
|
|
90
|
-
if edge.kind != "import" or edge.external:
|
|
91
|
-
continue
|
|
92
|
-
src_node = node_by_id.get(edge.src)
|
|
93
|
-
dst_node = node_by_id.get(edge.dst)
|
|
94
|
-
if src_node is None or dst_node is None or src_node.region == dst_node.region:
|
|
95
|
-
continue
|
|
96
|
-
routes[(src_node.region, dst_node.region)].append(edge.certain)
|
|
97
|
-
|
|
98
|
-
# ``all``, deliberately, where ``mapview.py``'s ``_workflow`` uses ``any``
|
|
99
|
-
# for a call pair. The two marks claim different things. A workflow row
|
|
100
|
-
# asserts only that a relationship exists, so one proven call site settles
|
|
101
|
-
# it and the ambiguous ones beside it change nothing. A route is a single
|
|
102
|
-
# line standing in for ``weight`` imports, so calling it certain asserts
|
|
103
|
-
# every one of them: one unproven import among them and the whole route
|
|
104
|
-
# drops to possible. Under-claiming is the only direction that is safe
|
|
105
|
-
# when one mark speaks for many edges.
|
|
106
|
-
region_edges = tuple(
|
|
107
|
-
RegionEdge(src=src, dst=dst, weight=len(certainties), certain=all(certainties))
|
|
108
|
-
for (src, dst), certainties in sorted(routes.items())
|
|
109
|
-
)
|
|
110
140
|
return replace(
|
|
111
141
|
graph,
|
|
112
142
|
nodes=tuple(sorted(positioned_nodes, key=lambda node: node.id)),
|
|
@@ -115,6 +145,90 @@ def layout_graph(graph: Graph) -> Graph:
|
|
|
115
145
|
)
|
|
116
146
|
|
|
117
147
|
|
|
148
|
+
def _communities(
|
|
149
|
+
region_ids: tuple[str, ...], routes: tuple[RegionEdge, ...]
|
|
150
|
+
) -> dict[str, int]:
|
|
151
|
+
"""Find deterministic import communities with label propagation.
|
|
152
|
+
|
|
153
|
+
Label propagation after Raghavan, Albert & Kumara (2007), "Near linear
|
|
154
|
+
time algorithm to detect community structures in large-scale networks";
|
|
155
|
+
constellation idea inspired by graphify. Implemented independently; no
|
|
156
|
+
code copied.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
ordered = sorted(region_ids)
|
|
160
|
+
neighbors: dict[str, set[str]] = {region_id: set() for region_id in ordered}
|
|
161
|
+
for route in sorted(routes, key=lambda edge: (edge.src, edge.dst)):
|
|
162
|
+
if route.src not in neighbors or route.dst not in neighbors or route.src == route.dst:
|
|
163
|
+
continue
|
|
164
|
+
neighbors[route.src].add(route.dst)
|
|
165
|
+
neighbors[route.dst].add(route.src)
|
|
166
|
+
|
|
167
|
+
labels = {region_id: index for index, region_id in enumerate(ordered)}
|
|
168
|
+
for _ in range(10):
|
|
169
|
+
next_labels = dict(labels)
|
|
170
|
+
for region_id in ordered:
|
|
171
|
+
if not neighbors[region_id]:
|
|
172
|
+
continue
|
|
173
|
+
frequencies: dict[int, int] = defaultdict(int)
|
|
174
|
+
for neighbor in sorted(neighbors[region_id]):
|
|
175
|
+
frequencies[labels[neighbor]] += 1
|
|
176
|
+
highest = max(frequencies.values())
|
|
177
|
+
next_labels[region_id] = min(
|
|
178
|
+
label for label, count in frequencies.items() if count == highest
|
|
179
|
+
)
|
|
180
|
+
if next_labels == labels:
|
|
181
|
+
break
|
|
182
|
+
labels = next_labels
|
|
183
|
+
|
|
184
|
+
dense_labels: dict[int, int] = {}
|
|
185
|
+
communities: dict[str, int] = {}
|
|
186
|
+
for region_id in ordered:
|
|
187
|
+
label = labels[region_id]
|
|
188
|
+
if label not in dense_labels:
|
|
189
|
+
dense_labels[label] = len(dense_labels)
|
|
190
|
+
communities[region_id] = dense_labels[label]
|
|
191
|
+
return communities
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _hops_from_home(
|
|
195
|
+
region_ids: tuple[str, ...],
|
|
196
|
+
routes: tuple[RegionEdge, ...],
|
|
197
|
+
home: str | None,
|
|
198
|
+
) -> dict[str, int]:
|
|
199
|
+
"""Return each region's import-route distance from Home.
|
|
200
|
+
|
|
201
|
+
Undirected on purpose: an import route is a relationship between two
|
|
202
|
+
modules, and a learner following it from Home arrives just as surely at the
|
|
203
|
+
importer as at the imported. Direction is still parser truth and is still
|
|
204
|
+
drawn as an arrow -- it just is not what "how far from Home" means.
|
|
205
|
+
|
|
206
|
+
Regions absent from the result have no route to Home at all. The caller
|
|
207
|
+
stores that as ``None`` rather than a sentinel distance, so "unreachable"
|
|
208
|
+
can never be mistaken for "very far".
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
if home is None or home not in set(region_ids):
|
|
212
|
+
return {}
|
|
213
|
+
|
|
214
|
+
neighbors: dict[str, set[str]] = {region_id: set() for region_id in region_ids}
|
|
215
|
+
for route in routes:
|
|
216
|
+
if route.src not in neighbors or route.dst not in neighbors:
|
|
217
|
+
continue
|
|
218
|
+
neighbors[route.src].add(route.dst)
|
|
219
|
+
neighbors[route.dst].add(route.src)
|
|
220
|
+
|
|
221
|
+
hops = {home: 0}
|
|
222
|
+
queue: deque[str] = deque([home])
|
|
223
|
+
while queue:
|
|
224
|
+
current = queue.popleft()
|
|
225
|
+
for neighbor in sorted(neighbors[current]):
|
|
226
|
+
if neighbor not in hops:
|
|
227
|
+
hops[neighbor] = hops[current] + 1
|
|
228
|
+
queue.append(neighbor)
|
|
229
|
+
return hops
|
|
230
|
+
|
|
231
|
+
|
|
118
232
|
def _call_depths(members: list[Node], edges: tuple[Edge, ...]) -> dict[str, int]:
|
|
119
233
|
"""Return each member's orbit ring: call depth from the system's entry node.
|
|
120
234
|
|
|
@@ -169,8 +283,20 @@ def with_entrypoint(graph: Graph, node_id: str) -> Graph:
|
|
|
169
283
|
raise ValueError(f"entrypoint is not a parser-ranked candidate: {node_id}")
|
|
170
284
|
node_by_id = {node.id: node for node in graph.nodes}
|
|
171
285
|
selected = node_by_id[node_id]
|
|
286
|
+
# Every distance is measured from Home, so moving Home invalidates all of
|
|
287
|
+
# them. Coordinates and communities are untouched: they depend on the import
|
|
288
|
+
# structure, not on which entrypoint the learner chose, so a Home change
|
|
289
|
+
# must not re-sort the sky or re-dim a region.
|
|
290
|
+
hops = _hops_from_home(
|
|
291
|
+
tuple(region.id for region in graph.regions), graph.region_edges, selected.region
|
|
292
|
+
)
|
|
172
293
|
regions = tuple(
|
|
173
|
-
replace(
|
|
294
|
+
replace(
|
|
295
|
+
region,
|
|
296
|
+
home=region.id == selected.region,
|
|
297
|
+
hops_from_home=hops.get(region.id),
|
|
298
|
+
)
|
|
299
|
+
for region in graph.regions
|
|
174
300
|
)
|
|
175
301
|
return replace(graph, selected_entrypoint=node_id, regions=regions)
|
|
176
302
|
|