concurim 0.1.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.
Files changed (283) hide show
  1. concurim-0.1.0/.gitignore +14 -0
  2. concurim-0.1.0/CHANGELOG.md +68 -0
  3. concurim-0.1.0/CONTRIBUTING.md +69 -0
  4. concurim-0.1.0/LICENSE +21 -0
  5. concurim-0.1.0/PKG-INFO +95 -0
  6. concurim-0.1.0/README.md +65 -0
  7. concurim-0.1.0/SECURITY.md +37 -0
  8. concurim-0.1.0/docs/concepts.md +37 -0
  9. concurim-0.1.0/docs/cost.md +16 -0
  10. concurim-0.1.0/docs/getting-started.md +68 -0
  11. concurim-0.1.0/docs/images/logo.png +0 -0
  12. concurim-0.1.0/docs/images/viewer-dark.png +0 -0
  13. concurim-0.1.0/docs/images/viewer-light.png +0 -0
  14. concurim-0.1.0/docs/index.md +23 -0
  15. concurim-0.1.0/docs/limitations.md +207 -0
  16. concurim-0.1.0/docs/reading-a-run.md +62 -0
  17. concurim-0.1.0/docs/recording.md +93 -0
  18. concurim-0.1.0/docs/teaching.md +26 -0
  19. concurim-0.1.0/docs/trace-format.md +140 -0
  20. concurim-0.1.0/frontend/index.html +14 -0
  21. concurim-0.1.0/frontend/package-lock.json +1952 -0
  22. concurim-0.1.0/frontend/package.json +29 -0
  23. concurim-0.1.0/frontend/playwright.config.ts +21 -0
  24. concurim-0.1.0/frontend/src/App.tsx +121 -0
  25. concurim-0.1.0/frontend/src/Viewer.tsx +263 -0
  26. concurim-0.1.0/frontend/src/animations/palette.ts +32 -0
  27. concurim-0.1.0/frontend/src/api/client.ts +85 -0
  28. concurim-0.1.0/frontend/src/api/readDrop.ts +80 -0
  29. concurim-0.1.0/frontend/src/api/types.ts +117 -0
  30. concurim-0.1.0/frontend/src/capture.test.ts +31 -0
  31. concurim-0.1.0/frontend/src/capture.ts +120 -0
  32. concurim-0.1.0/frontend/src/captureClock.ts +43 -0
  33. concurim-0.1.0/frontend/src/compare/Compare.tsx +112 -0
  34. concurim-0.1.0/frontend/src/compare/Lanes.tsx +40 -0
  35. concurim-0.1.0/frontend/src/components/CodeView.tsx +107 -0
  36. concurim-0.1.0/frontend/src/components/Controls.tsx +90 -0
  37. concurim-0.1.0/frontend/src/components/EventDetail.tsx +68 -0
  38. concurim-0.1.0/frontend/src/components/EventLog.tsx +69 -0
  39. concurim-0.1.0/frontend/src/components/Explanation.tsx +25 -0
  40. concurim-0.1.0/frontend/src/components/Inspector.tsx +122 -0
  41. concurim-0.1.0/frontend/src/components/Lineage.tsx +77 -0
  42. concurim-0.1.0/frontend/src/components/RepoLink.tsx +14 -0
  43. concurim-0.1.0/frontend/src/components/StuckBanner.tsx +57 -0
  44. concurim-0.1.0/frontend/src/components/Timeline.tsx +178 -0
  45. concurim-0.1.0/frontend/src/components/TraceInfo.tsx +31 -0
  46. concurim-0.1.0/frontend/src/components/useSource.ts +26 -0
  47. concurim-0.1.0/frontend/src/export/ExportDialog.tsx +126 -0
  48. concurim-0.1.0/frontend/src/export/ExportFields.tsx +131 -0
  49. concurim-0.1.0/frontend/src/export/options.test.ts +50 -0
  50. concurim-0.1.0/frontend/src/export/options.ts +104 -0
  51. concurim-0.1.0/frontend/src/home/Gallery.tsx +198 -0
  52. concurim-0.1.0/frontend/src/home/Home.tsx +125 -0
  53. concurim-0.1.0/frontend/src/home/RunForm.tsx +116 -0
  54. concurim-0.1.0/frontend/src/home/RunPanel.tsx +131 -0
  55. concurim-0.1.0/frontend/src/home/RunProgress.tsx +58 -0
  56. concurim-0.1.0/frontend/src/main.tsx +16 -0
  57. concurim-0.1.0/frontend/src/models/compare.test.ts +41 -0
  58. concurim-0.1.0/frontend/src/models/compare.ts +100 -0
  59. concurim-0.1.0/frontend/src/models/fold.test.ts +42 -0
  60. concurim-0.1.0/frontend/src/models/fold.ts +58 -0
  61. concurim-0.1.0/frontend/src/models/layout.ts +222 -0
  62. concurim-0.1.0/frontend/src/models/layoutStore.ts +63 -0
  63. concurim-0.1.0/frontend/src/models/lineage.test.ts +47 -0
  64. concurim-0.1.0/frontend/src/models/lineage.ts +84 -0
  65. concurim-0.1.0/frontend/src/models/marks.test.ts +92 -0
  66. concurim-0.1.0/frontend/src/models/marks.ts +162 -0
  67. concurim-0.1.0/frontend/src/models/names.ts +8 -0
  68. concurim-0.1.0/frontend/src/models/replay.ts +210 -0
  69. concurim-0.1.0/frontend/src/models/resources.test.ts +46 -0
  70. concurim-0.1.0/frontend/src/models/resources.ts +95 -0
  71. concurim-0.1.0/frontend/src/models/selection.ts +4 -0
  72. concurim-0.1.0/frontend/src/models/states.test.ts +93 -0
  73. concurim-0.1.0/frontend/src/models/states.ts +59 -0
  74. concurim-0.1.0/frontend/src/models/sync.test.ts +135 -0
  75. concurim-0.1.0/frontend/src/playback/navigation.ts +34 -0
  76. concurim-0.1.0/frontend/src/playback/playback.test.ts +76 -0
  77. concurim-0.1.0/frontend/src/playback/stepping.test.ts +21 -0
  78. concurim-0.1.0/frontend/src/playback/stepping.ts +73 -0
  79. concurim-0.1.0/frontend/src/playback/useKeyboard.ts +51 -0
  80. concurim-0.1.0/frontend/src/playback/usePlayback.ts +75 -0
  81. concurim-0.1.0/frontend/src/styles/app.css +260 -0
  82. concurim-0.1.0/frontend/src/styles/capture.css +56 -0
  83. concurim-0.1.0/frontend/src/styles/export.css +43 -0
  84. concurim-0.1.0/frontend/src/styles/flow.css +102 -0
  85. concurim-0.1.0/frontend/src/styles/home.css +86 -0
  86. concurim-0.1.0/frontend/src/styles/theme.css +54 -0
  87. concurim-0.1.0/frontend/src/views/FlowToolbar.tsx +56 -0
  88. concurim-0.1.0/frontend/src/views/FlowView.tsx +261 -0
  89. concurim-0.1.0/frontend/src/views/GroupNode.tsx +38 -0
  90. concurim-0.1.0/frontend/src/views/ResourceView.tsx +141 -0
  91. concurim-0.1.0/frontend/src/views/SyncNode.tsx +104 -0
  92. concurim-0.1.0/frontend/src/views/Tokens.tsx +61 -0
  93. concurim-0.1.0/frontend/src/views/flowContext.ts +21 -0
  94. concurim-0.1.0/frontend/src/views/flowNodes.tsx +117 -0
  95. concurim-0.1.0/frontend/src/views/geometry.ts +57 -0
  96. concurim-0.1.0/frontend/tests/e2e/export.spec.ts +61 -0
  97. concurim-0.1.0/frontend/tests/e2e/graph.spec.ts +168 -0
  98. concurim-0.1.0/frontend/tests/e2e/home.spec.ts +221 -0
  99. concurim-0.1.0/frontend/tests/e2e/modes.spec.ts +70 -0
  100. concurim-0.1.0/frontend/tests/e2e/viewer.spec.ts +134 -0
  101. concurim-0.1.0/frontend/tests/e2e/visual.spec.ts +25 -0
  102. concurim-0.1.0/frontend/tests/e2e/visual.spec.ts-snapshots/dark-41-darwin.png +0 -0
  103. concurim-0.1.0/frontend/tests/e2e/visual.spec.ts-snapshots/light-101-darwin.png +0 -0
  104. concurim-0.1.0/frontend/tests/fixtures/asyncio-condition-sample.replay.json +1 -0
  105. concurim-0.1.0/frontend/tests/fixtures/asyncio-tasks-sample.replay.json +1 -0
  106. concurim-0.1.0/frontend/tests/fixtures/condition-mailbox-sample.replay.json +1 -0
  107. concurim-0.1.0/frontend/tests/fixtures/event-gate-sample.replay.json +1 -0
  108. concurim-0.1.0/frontend/tests/fixtures/future-control-sample.replay.json +1 -0
  109. concurim-0.1.0/frontend/tests/fixtures/lineage-pipeline-sample.replay.json +1 -0
  110. concurim-0.1.0/frontend/tests/fixtures/lock-contention-sample.replay.json +1 -0
  111. concurim-0.1.0/frontend/tests/fixtures/lock-order-sample.replay.json +1 -0
  112. concurim-0.1.0/frontend/tests/fixtures/pipeline.replay.json +1 -0
  113. concurim-0.1.0/frontend/tests/fixtures/process-pool-map-sample.replay.json +1 -0
  114. concurim-0.1.0/frontend/tests/fixtures/process-pool-sample.replay.json +1 -0
  115. concurim-0.1.0/frontend/tests/fixtures/process-share-sample.replay.json +1 -0
  116. concurim-0.1.0/frontend/tests/fixtures/semaphore-sample.replay.json +1 -0
  117. concurim-0.1.0/frontend/tests/fixtures/task-group-sample.replay.json +1 -0
  118. concurim-0.1.0/frontend/tests/fixtures/thread-pool-sample.replay.json +1 -0
  119. concurim-0.1.0/frontend/tsconfig.json +18 -0
  120. concurim-0.1.0/frontend/vite.config.ts +44 -0
  121. concurim-0.1.0/pyproject.toml +90 -0
  122. concurim-0.1.0/scripts/benchmark.py +133 -0
  123. concurim-0.1.0/scripts/build.sh +8 -0
  124. concurim-0.1.0/scripts/make_fixtures.py +66 -0
  125. concurim-0.1.0/scripts/make_samples.py +55 -0
  126. concurim-0.1.0/scripts/smoke_wheel.sh +103 -0
  127. concurim-0.1.0/src/concurim/__init__.py +72 -0
  128. concurim-0.1.0/src/concurim/__main__.py +7 -0
  129. concurim-0.1.0/src/concurim/cli.py +262 -0
  130. concurim-0.1.0/src/concurim/config.py +81 -0
  131. concurim-0.1.0/src/concurim/export/__init__.py +1 -0
  132. concurim-0.1.0/src/concurim/export/cdp.py +224 -0
  133. concurim-0.1.0/src/concurim/export/cli.py +110 -0
  134. concurim-0.1.0/src/concurim/export/options.py +99 -0
  135. concurim-0.1.0/src/concurim/export/sink.py +126 -0
  136. concurim-0.1.0/src/concurim/export/video.py +136 -0
  137. concurim-0.1.0/src/concurim/instrumentation/__init__.py +1 -0
  138. concurim-0.1.0/src/concurim/instrumentation/aio_events.py +158 -0
  139. concurim-0.1.0/src/concurim/instrumentation/aio_primitives.py +334 -0
  140. concurim-0.1.0/src/concurim/instrumentation/aio_taskgroup.py +87 -0
  141. concurim-0.1.0/src/concurim/instrumentation/aio_tasks.py +152 -0
  142. concurim-0.1.0/src/concurim/instrumentation/auto.py +251 -0
  143. concurim-0.1.0/src/concurim/instrumentation/auto_events.py +56 -0
  144. concurim-0.1.0/src/concurim/instrumentation/auto_mp.py +33 -0
  145. concurim-0.1.0/src/concurim/instrumentation/auto_support.py +46 -0
  146. concurim-0.1.0/src/concurim/instrumentation/barriers.py +213 -0
  147. concurim-0.1.0/src/concurim/instrumentation/executors.py +144 -0
  148. concurim-0.1.0/src/concurim/instrumentation/futures_support.py +132 -0
  149. concurim-0.1.0/src/concurim/instrumentation/mp_pool.py +272 -0
  150. concurim-0.1.0/src/concurim/instrumentation/mp_process.py +113 -0
  151. concurim-0.1.0/src/concurim/instrumentation/mp_support.py +268 -0
  152. concurim-0.1.0/src/concurim/instrumentation/mp_tasks.py +70 -0
  153. concurim-0.1.0/src/concurim/instrumentation/process_pool.py +164 -0
  154. concurim-0.1.0/src/concurim/instrumentation/queue_support.py +145 -0
  155. concurim-0.1.0/src/concurim/instrumentation/queues.py +263 -0
  156. concurim-0.1.0/src/concurim/instrumentation/sync.py +294 -0
  157. concurim-0.1.0/src/concurim/instrumentation/sync_events.py +179 -0
  158. concurim-0.1.0/src/concurim/instrumentation/threads.py +92 -0
  159. concurim-0.1.0/src/concurim/instrumentation/work.py +65 -0
  160. concurim-0.1.0/src/concurim/program_check.py +47 -0
  161. concurim-0.1.0/src/concurim/py.typed +0 -0
  162. concurim-0.1.0/src/concurim/recorder/__init__.py +1 -0
  163. concurim-0.1.0/src/concurim/recorder/collector.py +351 -0
  164. concurim-0.1.0/src/concurim/recorder/environment.py +110 -0
  165. concurim-0.1.0/src/concurim/recorder/events.py +246 -0
  166. concurim-0.1.0/src/concurim/recorder/flush.py +143 -0
  167. concurim-0.1.0/src/concurim/recorder/identities.py +27 -0
  168. concurim-0.1.0/src/concurim/recorder/lifecycle.py +211 -0
  169. concurim-0.1.0/src/concurim/recorder/sampler.py +192 -0
  170. concurim-0.1.0/src/concurim/recorder/serialization.py +159 -0
  171. concurim-0.1.0/src/concurim/replay/__init__.py +1 -0
  172. concurim-0.1.0/src/concurim/replay/code_view.py +71 -0
  173. concurim-0.1.0/src/concurim/replay/engine.py +218 -0
  174. concurim-0.1.0/src/concurim/replay/explanations.py +141 -0
  175. concurim-0.1.0/src/concurim/replay/models.py +124 -0
  176. concurim-0.1.0/src/concurim/replay/rules_barrier.py +83 -0
  177. concurim-0.1.0/src/concurim/replay/rules_events.py +191 -0
  178. concurim-0.1.0/src/concurim/replay/rules_exec.py +178 -0
  179. concurim-0.1.0/src/concurim/replay/rules_queues.py +200 -0
  180. concurim-0.1.0/src/concurim/replay/rules_sync.py +79 -0
  181. concurim-0.1.0/src/concurim/replay/rules_threads.py +101 -0
  182. concurim-0.1.0/src/concurim/replay/spans.py +128 -0
  183. concurim-0.1.0/src/concurim/replay/state.py +323 -0
  184. concurim-0.1.0/src/concurim/replay/state_barrier.py +79 -0
  185. concurim-0.1.0/src/concurim/replay/state_events.py +108 -0
  186. concurim-0.1.0/src/concurim/replay/state_exec.py +174 -0
  187. concurim-0.1.0/src/concurim/replay/state_sync.py +88 -0
  188. concurim-0.1.0/src/concurim/replay/state_taskgroup.py +70 -0
  189. concurim-0.1.0/src/concurim/replay/wait_for.py +206 -0
  190. concurim-0.1.0/src/concurim/runner.py +90 -0
  191. concurim-0.1.0/src/concurim/samples/__init__.py +32 -0
  192. concurim-0.1.0/src/concurim/samples/asyncio-condition.concurim +0 -0
  193. concurim-0.1.0/src/concurim/samples/asyncio-queue.concurim +0 -0
  194. concurim-0.1.0/src/concurim/samples/asyncio-tasks.concurim +0 -0
  195. concurim-0.1.0/src/concurim/samples/asyncio_condition.py +40 -0
  196. concurim-0.1.0/src/concurim/samples/asyncio_queue.py +42 -0
  197. concurim-0.1.0/src/concurim/samples/asyncio_tasks.py +25 -0
  198. concurim-0.1.0/src/concurim/samples/backpressure.concurim +0 -0
  199. concurim-0.1.0/src/concurim/samples/backpressure.py +48 -0
  200. concurim-0.1.0/src/concurim/samples/barrier.concurim +0 -0
  201. concurim-0.1.0/src/concurim/samples/barrier.py +31 -0
  202. concurim-0.1.0/src/concurim/samples/condition-mailbox.concurim +0 -0
  203. concurim-0.1.0/src/concurim/samples/condition_mailbox.py +47 -0
  204. concurim-0.1.0/src/concurim/samples/cpu-threads.concurim +0 -0
  205. concurim-0.1.0/src/concurim/samples/cpu_threads.py +33 -0
  206. concurim-0.1.0/src/concurim/samples/event-gate.concurim +0 -0
  207. concurim-0.1.0/src/concurim/samples/event_gate.py +37 -0
  208. concurim-0.1.0/src/concurim/samples/future-control.concurim +0 -0
  209. concurim-0.1.0/src/concurim/samples/future_control.py +42 -0
  210. concurim-0.1.0/src/concurim/samples/lineage-pipeline.concurim +0 -0
  211. concurim-0.1.0/src/concurim/samples/lineage_pipeline.py +70 -0
  212. concurim-0.1.0/src/concurim/samples/lock-contention.concurim +0 -0
  213. concurim-0.1.0/src/concurim/samples/lock-order.concurim +0 -0
  214. concurim-0.1.0/src/concurim/samples/lock_contention.py +40 -0
  215. concurim-0.1.0/src/concurim/samples/lock_order.py +43 -0
  216. concurim-0.1.0/src/concurim/samples/manifest.json +298 -0
  217. concurim-0.1.0/src/concurim/samples/plain-queues.concurim +0 -0
  218. concurim-0.1.0/src/concurim/samples/plain-threads.concurim +0 -0
  219. concurim-0.1.0/src/concurim/samples/plain_pipeline.py +61 -0
  220. concurim-0.1.0/src/concurim/samples/process-pool-map.concurim +0 -0
  221. concurim-0.1.0/src/concurim/samples/process-pool.concurim +0 -0
  222. concurim-0.1.0/src/concurim/samples/process-share.concurim +0 -0
  223. concurim-0.1.0/src/concurim/samples/process_pool.py +31 -0
  224. concurim-0.1.0/src/concurim/samples/process_pool_map.py +27 -0
  225. concurim-0.1.0/src/concurim/samples/process_share.py +33 -0
  226. concurim-0.1.0/src/concurim/samples/producer-consumer.concurim +0 -0
  227. concurim-0.1.0/src/concurim/samples/semaphore.concurim +0 -0
  228. concurim-0.1.0/src/concurim/samples/semaphore_limit.py +34 -0
  229. concurim-0.1.0/src/concurim/samples/shutdown.concurim +0 -0
  230. concurim-0.1.0/src/concurim/samples/shutdown.py +44 -0
  231. concurim-0.1.0/src/concurim/samples/task-group.concurim +0 -0
  232. concurim-0.1.0/src/concurim/samples/task_group.py +30 -0
  233. concurim-0.1.0/src/concurim/samples/thread-pool.concurim +0 -0
  234. concurim-0.1.0/src/concurim/samples/thread_pool.py +30 -0
  235. concurim-0.1.0/src/concurim/samples/threaded_pipeline.py +72 -0
  236. concurim-0.1.0/src/concurim/schema/__init__.py +0 -0
  237. concurim-0.1.0/src/concurim/schema/event.schema.json +42 -0
  238. concurim-0.1.0/src/concurim/viewer/__init__.py +0 -0
  239. concurim-0.1.0/src/concurim/viewer/api.py +256 -0
  240. concurim-0.1.0/src/concurim/viewer/exports.py +197 -0
  241. concurim-0.1.0/src/concurim/viewer/interpreters.py +86 -0
  242. concurim-0.1.0/src/concurim/viewer/plan.py +52 -0
  243. concurim-0.1.0/src/concurim/viewer/procs.py +62 -0
  244. concurim-0.1.0/src/concurim/viewer/runs.py +273 -0
  245. concurim-0.1.0/src/concurim/viewer/server.py +197 -0
  246. concurim-0.1.0/src/concurim/viewer/session.py +43 -0
  247. concurim-0.1.0/src/concurim/viewer/static/assets/Viewer-B5HA3A02.js +8 -0
  248. concurim-0.1.0/src/concurim/viewer/static/assets/Viewer-DLioOiRN.css +1 -0
  249. concurim-0.1.0/src/concurim/viewer/static/assets/index-BHRQ-DHX.css +1 -0
  250. concurim-0.1.0/src/concurim/viewer/static/assets/index-Ch-8w8yu.js +11 -0
  251. concurim-0.1.0/src/concurim/viewer/static/assets/rolldown-runtime-WkwZ5CU-.js +1 -0
  252. concurim-0.1.0/src/concurim/viewer/static/index.html +16 -0
  253. concurim-0.1.0/src/concurim/viewer/static/logo.png +0 -0
  254. concurim-0.1.0/src/concurim/viewer/workspaces.py +144 -0
  255. concurim-0.1.0/tests/concurrency/test_stress.py +142 -0
  256. concurim-0.1.0/tests/conftest.py +55 -0
  257. concurim-0.1.0/tests/integration/test_cli_and_viewer.py +219 -0
  258. concurim-0.1.0/tests/integration/test_export_api.py +91 -0
  259. concurim-0.1.0/tests/integration/test_queue_recording.py +215 -0
  260. concurim-0.1.0/tests/integration/test_recording_features.py +139 -0
  261. concurim-0.1.0/tests/integration/test_stuck_run.py +128 -0
  262. concurim-0.1.0/tests/integration/test_viewer_api.py +168 -0
  263. concurim-0.1.0/tests/integration/test_viewer_levels.py +119 -0
  264. concurim-0.1.0/tests/integration/test_viewer_runs.py +272 -0
  265. concurim-0.1.0/tests/integration/viewer_client.py +99 -0
  266. concurim-0.1.0/tests/unit/pool_tasks.py +51 -0
  267. concurim-0.1.0/tests/unit/test_asyncio_instrumentation.py +229 -0
  268. concurim-0.1.0/tests/unit/test_auto_instrument.py +149 -0
  269. concurim-0.1.0/tests/unit/test_barriers.py +149 -0
  270. concurim-0.1.0/tests/unit/test_code_view.py +164 -0
  271. concurim-0.1.0/tests/unit/test_events.py +50 -0
  272. concurim-0.1.0/tests/unit/test_events_conditions.py +297 -0
  273. concurim-0.1.0/tests/unit/test_export.py +141 -0
  274. concurim-0.1.0/tests/unit/test_future_events.py +141 -0
  275. concurim-0.1.0/tests/unit/test_multiprocessing.py +161 -0
  276. concurim-0.1.0/tests/unit/test_process_pool.py +128 -0
  277. concurim-0.1.0/tests/unit/test_program_check.py +34 -0
  278. concurim-0.1.0/tests/unit/test_replay_state.py +234 -0
  279. concurim-0.1.0/tests/unit/test_serialization.py +90 -0
  280. concurim-0.1.0/tests/unit/test_sources.py +47 -0
  281. concurim-0.1.0/tests/unit/test_sync_instrumentation.py +256 -0
  282. concurim-0.1.0/tests/unit/test_taskgroup.py +135 -0
  283. concurim-0.1.0/tests/unit/test_wait_for.py +77 -0
@@ -0,0 +1,14 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ *.egg-info/
6
+ .pytest_cache/
7
+ .ruff_cache/
8
+ node_modules/
9
+ frontend/test-results/
10
+ frontend/playwright-report/
11
+ *.concurim
12
+ !tests/fixtures/*.concurim
13
+ !src/concurim/samples/*.concurim
14
+ .env
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (unreleased)
4
+
5
+ First vertical slice, then the GUI-first milestone, then locks, pools and
6
+ asyncio.
7
+
8
+ - The home screen shows the logo beside the name.
9
+ - The home screen groups examples into short lists. The home screen
10
+ and the player link to the GitHub repository. The README leads with
11
+ the commands.
12
+ - A short guide in `docs/`: getting started, concepts, recording,
13
+ reading a run, and which example to open.
14
+ - Barriers (`threading` and `asyncio`): who has arrived, the release
15
+ when the party is complete, and abort or reset. The home screen includes
16
+ a three-worker example.
17
+ - A program that does not compile is refused before it starts. A run that
18
+ gets stuck still leaves a trace: the buffer is flushed about once a second
19
+ and again on SIGINT or SIGTERM, and the viewer says who is still waiting
20
+ on locks one another holds. `concurim record --open` opens that picture.
21
+ Packaging metadata is ready for PyPI; 0.1.0 has not been uploaded.
22
+ - Events and conditions (`threading` and `asyncio`), `asyncio.TaskGroup`,
23
+ `multiprocessing` `Pool` and `Process` (copied versus shared arguments),
24
+ and `Future.cancel()`, callbacks and `as_completed` for thread pools.
25
+ - Viewer: **Lineage** tab, **Resources** layout, `concurim compare` (side by
26
+ side, GIL on/off, facts only), and `concurim export` (mp4/webm/gif/PNG,
27
+ deterministic frames with smooth, reproducible motion, optional
28
+ `concurim[video]` for ffmpeg), plus an **Export…** dialog in the viewer,
29
+ with an optional logo watermark in the top-left.
30
+ - Observability levels `sync` (`threading.Lock/RLock/Semaphore/
31
+ BoundedSemaphore`, `ThreadPoolExecutor`) and `asyncio` (tasks, `Queue`,
32
+ `Lock`, `Semaphore`); managed `TracedLock`, `TracedRLock`,
33
+ `TracedSemaphore`, `TracedBoundedSemaphore`, `TracedThreadPoolExecutor`,
34
+ `TracedAsync*`. Replay explains who holds or waits for each lock; asyncio
35
+ tasks are drawn like threads; executors like queues of tasks.
36
+ - `ProcessPoolExecutor` (auto at `sync`, managed `TracedProcessPoolExecutor`):
37
+ workers appear as processes; example `process-pool`.
38
+ - Code view: a task that has just started shades its **whole function body**,
39
+ and a background sampler (`sample_interval_ms`, with source included) then
40
+ follows the line each thread, asyncio task and pool worker is on (shown `≈`,
41
+ approximate).
42
+ - Viewer: **fullscreen** animation with media-style controls (`z`), a **Code**
43
+ mode lighting each busy worker's line in its item's colour, and a layout
44
+ polish (orbs inside nodes, no overflowing text).
45
+ - Five more examples (lock contention, semaphore, thread pool, asyncio tasks,
46
+ asyncio queue); queue items now keep their identity at `queues`.
47
+ - **Group workers** and `×N` folding of repeating log events.
48
+ - Viewer split into two chunks (home ~316 kB, viewer ~223 kB).
49
+ - CI: free-threaded 3.14t, and an experimental Windows job.
50
+
51
+ - Home screen: drop a `.concurim` trace, `.py` file or project folder; bundled
52
+ examples; explicit, reviewable "Record execution" with cancel and exit codes.
53
+ - Zero-code recording levels (`--auto none|threads|queues`), reversible.
54
+ - React Flow canvas with draggable, remembered layout; Motion-animated item
55
+ tokens; Animation/Timeline views over one recording; pacing modes; jumps.
56
+ - Local server: per-launch token on all POSTs, Origin/Host checks, body
57
+ limits, private scratch workspaces.
58
+ - Examples moved to `src/concurim/samples` (recordings ship in the wheel).
59
+
60
+ - `TracedQueue` (`queue.Queue` subclass), `TracedThread`, `work()` spans,
61
+ explicit lineage, `record()` API.
62
+ - `.concurim` trace format with JSON Schema, bounded buffer, truncation flag,
63
+ optional embedded source and payload previews.
64
+ - Replay engine with per-event state, blocking/join/shutdown handling, and
65
+ rule-based explanations that separate observed from inferred.
66
+ - Local viewer: animated producer/queue/consumer canvas, timeline with
67
+ spans and occupancy, playback controls, source panel, inspector, event log.
68
+ - CLI: `record`, `view`, `demo`, `inspect`.
@@ -0,0 +1,69 @@
1
+ # Contributing
2
+
3
+ Thanks for helping! Concurim values **accuracy over spectacle**: a visualization
4
+ must follow recorded events, and anything inferred must be labelled.
5
+
6
+ ## Setup
7
+
8
+ ```bash
9
+ uv sync
10
+ uv run pytest && uv run ruff check . && uv run ruff format --check . \
11
+ && uv run ty check src
12
+ cd frontend && npm ci && npm test
13
+ ```
14
+
15
+ The compiled viewer lives in `src/concurim/viewer/static` and is committed so
16
+ the package works without Node. After frontend changes run
17
+ `scripts/build.sh` and commit the result (CI fails if it is stale).
18
+ Regenerate frontend fixtures with `uv run python scripts/make_fixtures.py`
19
+ (real capture; then refresh Playwright snapshots with
20
+ `npx playwright test --update-snapshots`).
21
+
22
+ ## Adding an instrumented primitive
23
+
24
+ 1. Add event types in `recorder/events.py` and to the JSON Schema.
25
+ 2. Wrap the primitive in `instrumentation/`. Emit state-changing events at the
26
+ point where the real state changes (under the primitive's own lock if the
27
+ primitive offers a hook); use `mark_internal(__file__)` for source
28
+ attribution; never take locks that could invert with the collector's.
29
+ 3. Add a reducer handler in `replay/state.py` and a rule in `replay/rules_*.py`.
30
+ 4. Test with real threads. Never assert one incidental interleaving; assert
31
+ invariants and use events/barriers to force the situation you test.
32
+ 5. Update `docs/limitations.md`.
33
+
34
+ ## Style
35
+
36
+ Google style, 80 columns (hard limit 100), a short docstring per function,
37
+ files under ~300 lines (split before they grow), no duplicated logic.
38
+ Prefer the simplest design; do not add dependencies lightly (runtime
39
+ dependencies are currently zero).
40
+
41
+ ## Publishing
42
+
43
+ 0.1.0 is not on PyPI yet. The `pyproject.toml` metadata (SPDX license,
44
+ readme, classifiers, project URLs, the `concurim` script) is what the
45
+ upload will use. From a clean tree, after `scripts/build.sh`:
46
+
47
+ ```bash
48
+ uv build
49
+ uvx twine check dist/*
50
+ ```
51
+
52
+ Upload only when cutting the release. Do not upload from a dirty tree, and
53
+ do not upload over an existing version: PyPI rejects a repeated version.
54
+
55
+ ```bash
56
+ uvx twine upload dist/*
57
+ ```
58
+
59
+ Install paths once that version is on PyPI: `uvx concurim` (home screen;
60
+ the server copies itself onto the interpreter you pick) and
61
+ `uv add --dev concurim` when `concurim record` must import the project.
62
+
63
+ ## Frontend development note
64
+
65
+ `npm run dev` proxies `/api` to the server, but POST features (opening
66
+ examples, dropping files, running programs) fail there: they need the
67
+ per-launch token the real server embeds only in the HTML it serves. For those, run `npm run build` and use `uv run concurim`.
68
+ Playwright starts two servers (`concurim demo` on 8799, the home screen on
69
+ 8798) via `uv run`.
concurim-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Concurim contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.5
2
+ Name: concurim
3
+ Version: 0.1.0
4
+ Summary: See how Python runs together: record and replay concurrent execution.
5
+ Project-URL: Homepage, https://github.com/ej-sanmartin/concurim
6
+ Project-URL: Documentation, https://github.com/ej-sanmartin/concurim/blob/main/docs/index.md
7
+ Project-URL: Source, https://github.com/ej-sanmartin/concurim
8
+ Project-URL: Issues, https://github.com/ej-sanmartin/concurim/issues
9
+ Project-URL: Changelog, https://github.com/ej-sanmartin/concurim/blob/main/CHANGELOG.md
10
+ Author: Concurim contributors
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: asyncio,concurrency,debugging,observability,queue,threading,visualization
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Environment :: Console
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Education
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: MacOS
20
+ Classifier: Operating System :: Microsoft :: Windows
21
+ Classifier: Operating System :: POSIX :: Linux
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Software Development :: Debuggers
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.13
27
+ Provides-Extra: video
28
+ Requires-Dist: imageio-ffmpeg>=0.5; extra == 'video'
29
+ Description-Content-Type: text/markdown
30
+
31
+ <p align="center">
32
+ <img src="docs/images/logo.png" alt="Concurim" width="200"/>
33
+ </p>
34
+
35
+ # Concurim
36
+
37
+ **See how Python runs together.** Concurim records what your threads and
38
+ asyncio tasks actually do, then replays that one run as an animation.
39
+
40
+ ## Setup
41
+
42
+ From a checkout. After 0.1.0 is on PyPI, `uvx concurim` opens the same
43
+ home screen.
44
+
45
+ ```bash
46
+ git clone https://github.com/ej-sanmartin/concurim.git
47
+ cd concurim
48
+ uv sync
49
+
50
+ uv run concurim
51
+ uv run concurim demo
52
+ uv run concurim record --open --auto sync my_app.py
53
+ uv run concurim record --open --include-source -o run.concurim my_app.py arg1
54
+ uv run concurim view run.concurim
55
+ uv run concurim inspect run.concurim
56
+ uv run concurim compare one.concurim two.concurim
57
+ uv run concurim export run.concurim -o clip.mp4 --events 5:40
58
+ ```
59
+
60
+ `uv run concurim` opens the page. Drop a `.py` file or a folder, read the
61
+ plan, then click **Record execution**. Drop a `.concurim` file, or open
62
+ an example, and nothing runs. Options on `record` come before the script.
63
+ `uvx concurim record` cannot import a project's packages; install it
64
+ there with `uv add --dev concurim` and use `uv run`.
65
+
66
+ ![Concurim viewer, dark theme](docs/images/viewer-dark.png)
67
+
68
+ *A recording of [`threaded_pipeline.py`](src/concurim/samples/threaded_pipeline.py)
69
+ (event 41 of 120): two producers and two consumers share a queue of
70
+ capacity 3.*
71
+
72
+ > **Status: 0.1 (alpha).** Not on PyPI yet. Threads, queues, locks,
73
+ > events, barriers, pools, and asyncio are included.
74
+ > [What is and is not observed](docs/limitations.md).
75
+
76
+ ## Guide
77
+
78
+ | | |
79
+ | --- | --- |
80
+ | [Getting started](docs/getting-started.md) | The page, the commands, a stuck run |
81
+ | [Concepts](docs/concepts.md) | One schedule, observed versus inferred, what you can trust |
82
+ | [Recording](docs/recording.md) | Home screen, `--auto`, `record()`, `Traced*` |
83
+ | [Reading a run](docs/reading-a-run.md) | Animation, Code, Timeline, compare, export |
84
+ | [Teaching](docs/teaching.md) | Which example to open |
85
+ | [Cost](docs/cost.md) | Overhead of recording |
86
+ | [Limitations](docs/limitations.md) | Caveats |
87
+ | [Trace format](docs/trace-format.md) | The `.concurim` file |
88
+ | [Security](SECURITY.md) | Local only. Review a trace before sharing it |
89
+ | [Contributing](CONTRIBUTING.md) | Tests, style, publishing |
90
+ | [Changelog](CHANGELOG.md) | What 0.1.0 contains |
91
+
92
+ The viewer binds `127.0.0.1` and has no telemetry. Source and payload
93
+ previews are off unless you ask for them.
94
+
95
+ MIT licensed.
@@ -0,0 +1,65 @@
1
+ <p align="center">
2
+ <img src="docs/images/logo.png" alt="Concurim" width="200"/>
3
+ </p>
4
+
5
+ # Concurim
6
+
7
+ **See how Python runs together.** Concurim records what your threads and
8
+ asyncio tasks actually do, then replays that one run as an animation.
9
+
10
+ ## Setup
11
+
12
+ From a checkout. After 0.1.0 is on PyPI, `uvx concurim` opens the same
13
+ home screen.
14
+
15
+ ```bash
16
+ git clone https://github.com/ej-sanmartin/concurim.git
17
+ cd concurim
18
+ uv sync
19
+
20
+ uv run concurim
21
+ uv run concurim demo
22
+ uv run concurim record --open --auto sync my_app.py
23
+ uv run concurim record --open --include-source -o run.concurim my_app.py arg1
24
+ uv run concurim view run.concurim
25
+ uv run concurim inspect run.concurim
26
+ uv run concurim compare one.concurim two.concurim
27
+ uv run concurim export run.concurim -o clip.mp4 --events 5:40
28
+ ```
29
+
30
+ `uv run concurim` opens the page. Drop a `.py` file or a folder, read the
31
+ plan, then click **Record execution**. Drop a `.concurim` file, or open
32
+ an example, and nothing runs. Options on `record` come before the script.
33
+ `uvx concurim record` cannot import a project's packages; install it
34
+ there with `uv add --dev concurim` and use `uv run`.
35
+
36
+ ![Concurim viewer, dark theme](docs/images/viewer-dark.png)
37
+
38
+ *A recording of [`threaded_pipeline.py`](src/concurim/samples/threaded_pipeline.py)
39
+ (event 41 of 120): two producers and two consumers share a queue of
40
+ capacity 3.*
41
+
42
+ > **Status: 0.1 (alpha).** Not on PyPI yet. Threads, queues, locks,
43
+ > events, barriers, pools, and asyncio are included.
44
+ > [What is and is not observed](docs/limitations.md).
45
+
46
+ ## Guide
47
+
48
+ | | |
49
+ | --- | --- |
50
+ | [Getting started](docs/getting-started.md) | The page, the commands, a stuck run |
51
+ | [Concepts](docs/concepts.md) | One schedule, observed versus inferred, what you can trust |
52
+ | [Recording](docs/recording.md) | Home screen, `--auto`, `record()`, `Traced*` |
53
+ | [Reading a run](docs/reading-a-run.md) | Animation, Code, Timeline, compare, export |
54
+ | [Teaching](docs/teaching.md) | Which example to open |
55
+ | [Cost](docs/cost.md) | Overhead of recording |
56
+ | [Limitations](docs/limitations.md) | Caveats |
57
+ | [Trace format](docs/trace-format.md) | The `.concurim` file |
58
+ | [Security](SECURITY.md) | Local only. Review a trace before sharing it |
59
+ | [Contributing](CONTRIBUTING.md) | Tests, style, publishing |
60
+ | [Changelog](CHANGELOG.md) | What 0.1.0 contains |
61
+
62
+ The viewer binds `127.0.0.1` and has no telemetry. Source and payload
63
+ previews are off unless you ask for them.
64
+
65
+ MIT licensed.
@@ -0,0 +1,37 @@
1
+ # Security policy
2
+
3
+ ## Model
4
+
5
+ - The viewer is local-only: it binds `127.0.0.1`, rejects other `Host`
6
+ headers, sends a strict CSP, serves static files
7
+ with path containment, and serves source text only from the loaded trace.
8
+ - The viewer has a small write surface: opening a trace, uploading a program to
9
+ a private scratch directory, and starting/cancelling a run. Every `POST`
10
+ needs a per-launch token (embedded only in the served page), a same-origin
11
+ `Origin`, and is size-limited. Scratch files use sanitized paths and caps.
12
+ - **Recording runs your Python code with your user's permissions. There is no
13
+ sandbox.** A run starts only after the plan (command, working directory,
14
+ arguments, interpreter) is shown and "Record execution" is clicked. Dropping
15
+ a `.concurim` trace never executes anything.
16
+ - Traces are data: they are parsed with `json` and validated; size limits
17
+ apply; nothing is unpickled or executed. Strings are rendered as text.
18
+ - No telemetry, uploads, accounts, or network use.
19
+ - Auto-instrumentation (`--auto sync|asyncio`) temporarily replaces
20
+ standard-library factories in the recorded process only, and restores them
21
+ on exit. It never touches the viewer or other processes.
22
+ - Recording can expose information. Source embedding and payload previews are
23
+ opt-in; previews cover only built-in primitives, are redacted by configured
24
+ patterns *before* truncation, and never call `repr()` on user objects.
25
+
26
+ ## Reporting
27
+
28
+ Please report vulnerabilities privately through GitHub Security Advisories on
29
+ the repository (or by email to the maintainers once published) instead of a
30
+ public issue. Include a trace or steps to reproduce. We aim to acknowledge
31
+ within a few days.
32
+
33
+ ## Sharing traces
34
+
35
+ A `.concurim` file may contain file paths, thread names, queue names and, if
36
+ enabled, source code or data previews. Review with `concurim inspect` and the
37
+ viewer's **Trace** tab before sharing.
@@ -0,0 +1,37 @@
1
+ # Concepts
2
+
3
+ Concurim records one schedule: the one this process actually ran. It
4
+ does not search for other interleavings, and it does not show who held
5
+ the GIL.
6
+
7
+ **Observed** facts come from an event. A solid border, a queue's
8
+ contents, and "blocked on this lock" are observed. **Inferred** facts
9
+ are a guess the viewer labels as such. A dashed border means the actor
10
+ is treated as running because nothing has said it is blocked. The
11
+ explanation card says when a sentence is inferred.
12
+
13
+ Recording changes timing. The picture is faithful to the instrumented
14
+ run, which is a bit slower than the same program with Concurim absent.
15
+
16
+ A file that does not compile is not a run. A program that starts and
17
+ then raises is a run, and the exception is in the trace. A program that
18
+ gets stuck is a run that ends at the last observed event. If threads are
19
+ waiting on locks one another holds, the viewer says so. That cycle is
20
+ read off this run. Queues and events do not name who has to move next,
21
+ so they are not drawn as a cycle.
22
+
23
+ Anything the chosen observability level cannot see is listed as not
24
+ observed. It is not filled in. The full list is in
25
+ [limitations](limitations.md).
26
+
27
+ ## What you can trust
28
+
29
+ - Queue contents change only on recorded events, in true mutation order.
30
+ - Queued and unfinished are different numbers. A dequeued item leaves
31
+ the queue and stays unfinished until `task_done()`.
32
+ - `task_done()` does not say which item finished. Concurim attributes it
33
+ only when you pass `object_id=`. Otherwise the pairing is labeled
34
+ inferred.
35
+ - A truncated recording, an object that was not instrumented, and
36
+ anything unobservable (CPU scheduling, the GIL) is stated in the trace
37
+ and in the UI.
@@ -0,0 +1,16 @@
1
+ # Cost
2
+
3
+ Measured on an Apple-silicon laptop, CPython 3.13
4
+ (`scripts/benchmark.py`):
5
+
6
+ | | |
7
+ | --- | --- |
8
+ | `TracedQueue` while not recording | ~2× a bare `queue.Queue` call (≈0.5 µs more) |
9
+ | Recording, detailed or light | ≈10 µs per put+get pair (≈20× a bare, work-free loop) |
10
+ | Memory while recording | ≈550 B/event (in-memory buffer) |
11
+ | Trace size | ≈19 B/event on disk (zip) |
12
+ | Load + replay | ≈1.5 s for 80k events |
13
+
14
+ The overhead matters when threads do no real work between instrumented
15
+ calls. With real work in between, it is usually small. Re-measure on
16
+ the machine you care about.
@@ -0,0 +1,68 @@
1
+ # Getting started
2
+
3
+ Open the home screen and drop a program, or record from the terminal.
4
+ Either way you get one picture of the run that actually happened.
5
+
6
+ Commands below use `uv run concurim` from a checkout of this repo.
7
+ After 0.1.0 is on PyPI, `uvx concurim` is the same home screen, and
8
+ `uv add --dev concurim` is how `concurim record` can import a project's
9
+ own packages.
10
+
11
+ ## Open the page
12
+
13
+ ```bash
14
+ git clone https://github.com/ej-sanmartin/concurim.git
15
+ cd concurim
16
+ uv sync
17
+ uv run concurim
18
+ ```
19
+
20
+ The browser opens on this computer. From there:
21
+
22
+ - Drop a `.concurim` file, or open an example. Nothing runs.
23
+ - Drop a `.py` file or a project folder. Pick the entry point,
24
+ interpreter, arguments, and observability level, read the plan, then
25
+ click **Record execution**. The program does not run until that click.
26
+ When it finishes, the picture opens.
27
+ - **Watch so far**, while a run is still going, opens the latest saved
28
+ moment. That picture stays where you opened it.
29
+
30
+ ```bash
31
+ uv run concurim --port 8794 --no-browser # page only, you open the URL
32
+ ```
33
+
34
+ ## Record from the terminal
35
+
36
+ Options come before the script. Everything after the script path is
37
+ passed to your program.
38
+
39
+ ```bash
40
+ uv run concurim record --open my_app.py
41
+ uv run concurim record --open --auto sync my_app.py
42
+ uv run concurim record --open --include-source -o run.concurim my_app.py arg1
43
+ uv run concurim view run.concurim
44
+ uv run concurim inspect run.concurim
45
+ ```
46
+
47
+ `--auto` defaults to `threads`. Use `queues`, `sync`, or `asyncio` when
48
+ the program uses those primitives and does not import Concurim itself.
49
+ `--include-source` is what the Code view needs. A file that does not
50
+ compile is refused, and no trace is written.
51
+
52
+ ```bash
53
+ uv run concurim demo
54
+ uv run concurim compare one.concurim two.concurim
55
+ uv run concurim export run.concurim -o clip.mp4 --events 5:40
56
+ ```
57
+
58
+ `demo` records the bundled pipeline and opens it. `export` needs a
59
+ Chrome-family browser and ffmpeg (`uv run --with imageio-ffmpeg`, or
60
+ `concurim[video]` once the package is published). It only reads the
61
+ trace.
62
+
63
+ Ctrl-C during `record` still leaves a trace. Playback ends at the last
64
+ observed event. See [limitations](limitations.md) for what a stuck run
65
+ can and cannot show.
66
+
67
+ Next: [concepts](concepts.md), [recording](recording.md),
68
+ [reading a run](reading-a-run.md).
Binary file
@@ -0,0 +1,23 @@
1
+ # Concurim documentation
2
+
3
+ The [README](../README.md) is the tour. These pages are the short guide.
4
+
5
+ ## Read these
6
+
7
+ | | |
8
+ | --- | --- |
9
+ | [Getting started](getting-started.md) | Open the page, or record from the terminal |
10
+ | [Concepts](concepts.md) | One schedule, observed versus inferred |
11
+ | [Recording](recording.md) | Home screen, CLI, `record()`, `Traced*` |
12
+ | [Reading a run](reading-a-run.md) | Animation, Code, Timeline, compare, export |
13
+ | [Teaching](teaching.md) | Which bundled example shows which idea |
14
+ | [Cost](cost.md) | Overhead of recording |
15
+ | [Limitations](limitations.md) | What is observed, stuck programs, what is refused |
16
+ | [Trace format](trace-format.md) | The `.concurim` zip, for anyone building on the file |
17
+ | [Security](../SECURITY.md) | Local viewer, no telemetry, what a trace can contain |
18
+ | [Contributing](../CONTRIBUTING.md) | Tests, style, and how a PyPI upload will work |
19
+ | [Changelog](../CHANGELOG.md) | What 0.1.0 contains |
20
+
21
+ Video captions, a live picture that keeps updating, and a single HTML
22
+ file you can send someone are listed in [limitations](limitations.md)
23
+ and are not in 0.1.0.