explainer-video 0.0.0-stage → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +12 -0
- package/LICENSE +21 -0
- package/README.md +154 -2
- package/cordis.patch.yml +7 -0
- package/lib/index.js +35 -0
- package/package.json +56 -4
- package/plugin.json +13 -0
- package/skills/coursework-author/.synced-from +5 -0
- package/skills/coursework-author/SKILL.md +209 -0
- package/skills/craft-codeflow-flows/SKILL.md +116 -0
- package/skills/craft-codeflow-mine/SKILL.md +207 -0
- package/skills/craft-codeflow-plan/SKILL.md +65 -0
- package/skills/craft-codeflow-scene/SKILL.md +168 -0
- package/skills/craft-video-align/SKILL.md +109 -0
- package/skills/craft-video-assemble/SKILL.md +70 -0
- package/skills/craft-video-audio/SKILL.md +59 -0
- package/skills/craft-video-enrich/SKILL.md +46 -0
- package/skills/craft-video-plan/SKILL.md +57 -0
- package/skills/craft-video-publish/SKILL.md +79 -0
- package/skills/craft-video-render/SKILL.md +68 -0
- package/skills/craft-video-scene/SKILL.md +112 -0
- package/skills/craft-video-script/SKILL.md +80 -0
- package/skills/craft-video-thumbnail/SKILL.md +168 -0
- package/skills/explainer-video-from-codebase/FLOWS.md +577 -0
- package/skills/explainer-video-from-codebase/SKILL.md +698 -0
- package/skills/explainer-video-from-codebase/tools/apply-flows.mjs +531 -0
- package/skills/explainer-video-from-codebase/tools/approve-flows.mjs +56 -0
- package/skills/explainer-video-from-codebase/tools/codemap-md.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/config/dotenv.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/json.mjs +14 -0
- package/skills/explainer-video-from-codebase/tools/config/redact.mjs +16 -0
- package/skills/explainer-video-from-codebase/tools/config/toml-lite.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/yaml-lite.mjs +18 -0
- package/skills/explainer-video-from-codebase/tools/excerpt-check.mjs +186 -0
- package/skills/explainer-video-from-codebase/tools/excerpt.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/flow-diagram.mjs +168 -0
- package/skills/explainer-video-from-codebase/tools/flow-plan.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/flows-md.mjs +87 -0
- package/skills/explainer-video-from-codebase/tools/lang/c.mjs +684 -0
- package/skills/explainer-video-from-codebase/tools/lang/go.mjs +673 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-config.mjs +143 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-deps.mjs +62 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-stdlib.mjs +24 -0
- package/skills/explainer-video-from-codebase/tools/lang/python.mjs +609 -0
- package/skills/explainer-video-from-codebase/tools/lang/typescript.mjs +963 -0
- package/skills/explainer-video-from-codebase/tools/lib/decompose.mjs +102 -0
- package/skills/explainer-video-from-codebase/tools/lib/groups.mjs +77 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-coverage.mjs +68 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-v8.mjs +232 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe.mjs +162 -0
- package/skills/explainer-video-from-codebase/tools/lib/redact.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/sandbox.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/lib/test-links.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/toml.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-c.mjs +95 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-go.mjs +101 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-js.mjs +136 -0
- package/skills/explainer-video-from-codebase/tools/lib/tree-sitter.mjs +19 -0
- package/skills/explainer-video-from-codebase/tools/lib/walk.mjs +138 -0
- package/skills/explainer-video-from-codebase/tools/mine.mjs +409 -0
- package/skills/explainer-video-from-codebase/tools/propose.mjs +127 -0
- package/skills/explainer-video-from-codebase/tools/rank.mjs +119 -0
- package/skills/explainer-video-from-codebase/tools/ranking-md.mjs +76 -0
- package/skills/explainer-video-from-codebase/tools/rules/c-handlers.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/celery-taskiq.mjs +48 -0
- package/skills/explainer-video-from-codebase/tools/rules/click-typer.mjs +6 -0
- package/skills/explainer-video-from-codebase/tools/rules/express.mjs +72 -0
- package/skills/explainer-video-from-codebase/tools/rules/fastapi.mjs +99 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-http.mjs +173 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-test.mjs +108 -0
- package/skills/explainer-video-from-codebase/tools/rules/grpc.mjs +57 -0
- package/skills/explainer-video-from-codebase/tools/rules/gtest.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/rules/haystack.mjs +55 -0
- package/skills/explainer-video-from-codebase/tools/rules/jest-vitest.mjs +100 -0
- package/skills/explainer-video-from-codebase/tools/rules/js-http-client.mjs +111 -0
- package/skills/explainer-video-from-codebase/tools/rules/nextjs.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/process-spawn.mjs +49 -0
- package/skills/explainer-video-from-codebase/tools/rules/py-http-client.mjs +26 -0
- package/skills/explainer-video-from-codebase/tools/rules/pytest.mjs +107 -0
- package/skills/explainer-video-from-codebase/tools/rules/react-router.mjs +17 -0
- package/skills/explainer-video-from-codebase/tools/rules/registry.mjs +31 -0
- package/skills/explainer-video-from-codebase/tools/trace/c/fmtrace-sh +14 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-net.cjs +33 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-srcmap.cjs +41 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/fmtrace_plugin.py +87 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/sitecustomize.py +330 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/linux-bwrap.sh +73 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/macos-nonet.sb +41 -0
- package/skills/explainer-video-from-codebase/tools/trace.mjs +577 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-c.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-cpp.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-go.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-javascript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-python.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-typescript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/web-tree-sitter.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/MANIFEST.json +172 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-c.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-cpp.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-go.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-python.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.js +4081 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.wasm +0 -0
- package/skills/explainer-video-from-coursework/ARCHITECTURE.md +61 -0
- package/skills/explainer-video-from-coursework/PIPELINE.md +369 -0
- package/skills/explainer-video-from-coursework/ROADMAP.md +727 -0
- package/skills/explainer-video-from-coursework/SERIES.md +243 -0
- package/skills/explainer-video-from-coursework/SETUP.md +105 -0
- package/skills/explainer-video-from-coursework/SKILL.md +998 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-chart.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-equation.js +10 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-hubspoke.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-loopflow.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-sequence.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-timeline.js +11 -0
- package/skills/explainer-video-from-coursework/src/scene-code.html +127 -0
- package/skills/explainer-video-from-coursework/src/scene-code.js +379 -0
- package/skills/explainer-video-from-coursework/src/scene-core.js +1250 -0
- package/skills/explainer-video-from-coursework/src/scene-diagram.html +50 -0
- package/skills/explainer-video-from-coursework/src/scene-svg.html +50 -0
- package/skills/explainer-video-from-coursework/src/thumbnail.html +119 -0
- package/skills/explainer-video-from-coursework/src/timeline.js +297 -0
- package/skills/explainer-video-from-coursework/src/timing-chart.js +160 -0
- package/skills/explainer-video-from-coursework/tools/align.mjs +419 -0
- package/skills/explainer-video-from-coursework/tools/apply-enrichment.mjs +206 -0
- package/skills/explainer-video-from-coursework/tools/assemble.mjs +223 -0
- package/skills/explainer-video-from-coursework/tools/build-audio.mjs +234 -0
- package/skills/explainer-video-from-coursework/tools/chromium.mjs +73 -0
- package/skills/explainer-video-from-coursework/tools/debug-page.mjs +75 -0
- package/skills/explainer-video-from-coursework/tools/env.mjs +30 -0
- package/skills/explainer-video-from-coursework/tools/frame-dedup.mjs +131 -0
- package/skills/explainer-video-from-coursework/tools/key-source.mjs +151 -0
- package/skills/explainer-video-from-coursework/tools/model.mjs +21 -0
- package/skills/explainer-video-from-coursework/tools/naming.mjs +38 -0
- package/skills/explainer-video-from-coursework/tools/onset-audit.mjs +51 -0
- package/skills/explainer-video-from-coursework/tools/pacing-check.mjs +350 -0
- package/skills/explainer-video-from-coursework/tools/planner.mjs +616 -0
- package/skills/explainer-video-from-coursework/tools/publish.mjs +230 -0
- package/skills/explainer-video-from-coursework/tools/render.mjs +205 -0
- package/skills/explainer-video-from-coursework/tools/resolve-beats.mjs +239 -0
- package/skills/explainer-video-from-coursework/tools/script-check.mjs +174 -0
- package/skills/explainer-video-from-coursework/tools/stamp.mjs +71 -0
- package/skills/explainer-video-from-coursework/tools/thumbnail.mjs +224 -0
- package/skills/explainer-video-from-coursework/tools/timing-report.mjs +299 -0
- package/skills/explainer-video-from-coursework/tools/transcribe-gemini.mjs +96 -0
- package/skills/explainer-video-from-coursework/tools/tts-gemini.mjs +394 -0
- package/skills/explainer-video-from-coursework/tools/verify-audio.mjs +88 -0
- package/skills/explainer-video-from-coursework/tools/workdir.mjs +124 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js +8 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js.LICENSE.txt +16 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
# FLOWS.md — reference for the code-flow artifacts
|
|
2
|
+
|
|
3
|
+
This file grows with the skill. Today it describes the stage 1 artifacts: `codemap.json`, written
|
|
4
|
+
by `tools/mine.mjs`, with its readable twin `codemap.md`; `candidates.json`, written from it by
|
|
5
|
+
`tools/propose.mjs`; and `ranking.json` and `ranking.md`, written from that by `tools/rank.mjs` (each schema 1).
|
|
6
|
+
Then the stage 1b–1c artifacts: `flows.json`, the agent's series proposal; `flows.validated.json` and `flows.md`,
|
|
7
|
+
written by `tools/apply-flows.mjs`; `flows.approved.json`, written by `tools/approve-flows.mjs`; and `plan.json`
|
|
8
|
+
and `plan.md`, written by `tools/flow-plan.mjs`. Then the on-screen code for a video's scenes: code panels from
|
|
9
|
+
`tools/excerpt.mjs`, verified by `tools/excerpt-check.mjs`, and diagram skeletons from `tools/flow-diagram.mjs`.
|
|
10
|
+
|
|
11
|
+
`mine`, `propose` and `rank` are pure functions of their input and the tool versions: they never write
|
|
12
|
+
inside the repo, never use the network and never run repo code. Two runs over the same input produce
|
|
13
|
+
byte-identical output (timings go to stdout only). The opt-in `trace.json` (stage 1t, `tools/trace.mjs`) is the one
|
|
14
|
+
artifact made by running repo code: the repo's own test suite, sandboxed.
|
|
15
|
+
|
|
16
|
+
## Top level
|
|
17
|
+
|
|
18
|
+
| Field | What it holds |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `schema`, `tool.versions` | `1`; the vendored tree-sitter runtime and grammar versions |
|
|
21
|
+
| `root`, `commit`, `treeHash` | the mined directory, its `git HEAD` (or `null`), a sha256 over every mined and config file |
|
|
22
|
+
| `languages`, `unsupported` | Python, TypeScript, JavaScript, Go, C and C++ files and parse errors (`lang` on each file and symbol is `python`, `typescript`, `tsx`, `javascript`, `go`, `c` or `cpp`; a `.h` header holding C++ is `cpp`); files a `--lang` left out, by extension |
|
|
23
|
+
| `groups` | the ordered group rules, the `--group` overrides, and file / symbol / entry counts per group |
|
|
24
|
+
| `files[]` | every source file: `path`, `group`, the `rule` that placed it, `lang`, `sha256`, `parseError?`, `unsupported?` |
|
|
25
|
+
| `symbols[]` | `id`, `group`, `kind` (`function`, `method`, `class`, `main`), `file`, `start`, `end`, `name`, `module`, `cls`, `decos`, `params`, `async?`, `sha256` of its source span |
|
|
26
|
+
| `classes[]` | `id`, resolved `bases`, raw `baseExprs`, `methods` (name → symbol id), `decos` |
|
|
27
|
+
| `edges[]`, `entries[]` | below |
|
|
28
|
+
| `tests[]`, `fixtures[]`, `fixtureBindings[]`, `mocks[]`, `testSummary` | below |
|
|
29
|
+
| `rules` | per-pack records: Depends links, queue producers, registry tables (with dropped constructors), Haystack pipelines, HTTP client calls (linked or not), spawns, callback count; with TS/JS files also `jsHttp` (every fetch / axios / ky call: `client`, `method`, folded `url`, `handler` or null, `unlinked` (why, when the method is unknown and the path serves more than one handler), `folded` consts, `env` keys, `match: "suffix"`), `uiRoutes`, Express `mounts`, `nextRoots`, `routeAliases` (a second route to a handler that already has an entry) and `setupFiles` |
|
|
30
|
+
| `dispatchSites[]`, `factories` | every narrowed or CHA dispatch point; every function that can return two or more classes, with branch guards |
|
|
31
|
+
| `config` | git-tracked config files (`group`, `reader`, fact count), the number of `untracked` ones not read, and `notGitWorkTree` (the mined or `--config-root` directories outside a git work tree, whose config is not read at all), settings fields and properties, every fact (`key`, `file:line`, and `value` only for a key a dispatch guard reads), profiles and classified dispatch sites (below) |
|
|
32
|
+
| `frameworksWithoutPack[]` | known frameworks the repo imports that no rule pack covers: their wiring is not linked |
|
|
33
|
+
| `selfCheck` | below |
|
|
34
|
+
|
|
35
|
+
Symbol ids: `pkg.module:func`, `pkg.module:Class.method`, `pkg.module:Outer.Inner` for a nested class,
|
|
36
|
+
`…:outer.<locals>.inner` for a function or class defined in a function, `pkg.module:<main>`
|
|
37
|
+
for an `if __name__ == "__main__":` block. Modules are named from the repo root (`backend/app/x.py` is
|
|
38
|
+
`backend.app.x`). An absolute import resolves to the module of that name at the repo root; else, for a file in a
|
|
39
|
+
directory with no `__init__.py`, to a module beside it; else through a package root: a file's package root is the
|
|
40
|
+
top of the contiguous run of ancestor directories holding an `__init__.py`, and its dotted name there is that run
|
|
41
|
+
plus the file, so with `backend/app/__init__.py` (and no `backend/__init__.py`) `from app.x import f` finds
|
|
42
|
+
`backend/app/x.py`; the longest claimed prefix carries the rest (`app.api.routes` when `api/` has no `__init__.py`).
|
|
43
|
+
When several roots claim a name, the one holding the importing file wins; otherwise the name stays external. A
|
|
44
|
+
claim under a root that does not hold the importing file resolves across roots, unless the repo declares the
|
|
45
|
+
top-level name as a dependency (requirements*.txt, pyproject.toml `[project]` / Poetry dependencies, setup.cfg
|
|
46
|
+
`install_requires` / `extras_require`, Pipfile; case, `-` and `_` normalised; a pyproject.toml or Pipfile that is
|
|
47
|
+
not valid TOML is read line by line instead and listed in `selfCheck.dependencyFilesNotParsed`): `integrations/stripe/` never
|
|
48
|
+
captures a declared `stripe`, and a declared name several roots claim is a library import, never reported as
|
|
49
|
+
ambiguous. A top-level name of the standard library never
|
|
50
|
+
resolves to an in-repo loose module, namespace directory or nested package (`app/logging/` never captures
|
|
51
|
+
`import logging`); only a repo-root package `logging/__init__.py` shadows it. `selfCheck.packageRoots` lists the
|
|
52
|
+
package roots below the repo root. A call made at module level has `from: pkg.module:<module>`.
|
|
53
|
+
A library target is `ext:<dotted.name>`.
|
|
54
|
+
|
|
55
|
+
TypeScript / JavaScript ids name the module by its repo-relative path with its extension (`web/src/api.ts:searchBooks`,
|
|
56
|
+
`web/src/store.ts:Store.load`, `…:Page.<locals>.go`, `…:<main>` for an `if (require.main === module)` block), so they
|
|
57
|
+
never meet a Python id. Imports resolve the way graphify resolves them: relative specifiers (a `.js` naming a `.ts`
|
|
58
|
+
file, extensions probed, directory `index` files), the nearest tsconfig.json / jsconfig.json `paths` and `baseUrl`
|
|
59
|
+
(through `extends`), in-repo workspace packages by their package.json `name`, and `@/` as `src/` when no tsconfig
|
|
60
|
+
exists; ESM default, named and namespace imports, re-exports and `export *`, and CommonJS `require` /
|
|
61
|
+
`module.exports` / `exports.x`. A function or class assigned to a variable is named by it, an object literal's
|
|
62
|
+
functions by `obj.member`, an inline route handler by its route (`GET /health`), a test by its describe path
|
|
63
|
+
(`Cart > adds an item`). `selfCheck.typescript` holds its counts, and `unresolvedImports` the relative imports that
|
|
64
|
+
name no file.
|
|
65
|
+
|
|
66
|
+
Go ids name the file too (`api/server.go:Server.getStock`, `stock/store.go:Store` for a type, `…:Run.<locals>.helper`
|
|
67
|
+
for a function literal bound to a local, `…:NewMux.<locals>.GET /x` for an inline route handler). The files of one
|
|
68
|
+
directory with one `package` clause share a namespace; an import path names a repo directory through the nearest
|
|
69
|
+
`go.mod`'s `module` line, as graphify resolves it (an import that maps to no directory is a dependency), and is known
|
|
70
|
+
by its alias, else its package's own name. Methods join their receiver type from any file of the package; a type
|
|
71
|
+
implements an interface whose methods its own and promoted methods cover (`classes[].bases` lists the types it embeds
|
|
72
|
+
and the interfaces it implements; an interface's methods are `abstractmethod`). `selfCheck.go` holds its counts and the
|
|
73
|
+
`modules` found.
|
|
74
|
+
|
|
75
|
+
C and C++ ids name the file that holds the definition, with namespaces and classes joined by `.`
|
|
76
|
+
(`src/cart.c:cart_total`, `src/cart.cpp:shop.Cart.add` for an out-of-line `Cart::add`, `include/shapes.h:geo.Shape`,
|
|
77
|
+
`tests/cart_test.cpp:CartTest.TotalsPrices` for a gtest `TEST_F`, `tests/t.cpp:cart adds` for a Catch2 `TEST_CASE`,
|
|
78
|
+
`src/router.cpp:routes["GET /x"]` for a lambda in a handler table). C linkage is global: a call names the definition in
|
|
79
|
+
its own file, else the one non-static one, preferring the files its includes name; a quoted `#include` resolves beside
|
|
80
|
+
the including file (graphify's rule), else to the one repo file ending with it. A pure virtual method is
|
|
81
|
+
`abstractmethod`; a gmock `MOCK_METHOD` member is `mock`. `selfCheck.c` holds its counts and `unresolvedIncludes`.
|
|
82
|
+
|
|
83
|
+
## Groups
|
|
84
|
+
|
|
85
|
+
Every file is in exactly one group. Ordered path rules, first match wins: **test** (a `tests/`,
|
|
86
|
+
`test/` or `__tests__/` dir; `test_*.py`, `*_test.py`, `conftest.py`; `*.test.*` / `*.spec.*` for JS/TS,
|
|
87
|
+
`setupTests.*`, `jest.setup.*`, `vitest.setup.*`; `*_test.go`; `test_*.c|cc|cpp|cxx`, `*_test.*`, `*_unittest.*`) → **dev** by file name (`debug_*`, `diagnose_*`,
|
|
88
|
+
`*migration_controller*`, `dev.py`; `debug*.[jt]s`, `*.dev.[jt]s`, jest/vitest/vite/webpack/… `.config.[jt]s`) → **eval** (`evals/`, `eval/`, `*_evals/`,
|
|
89
|
+
`evals_*/`, `evaluation(s)/`, `benchmark(s)/`, `bench/`) → **dev** by directory (`migrations/`, `alembic/`,
|
|
90
|
+
`dev_setup/`, `dev/`, `scripts/`, `verification/`, `debug/`) → **prod**. A file the path rules leave in prod
|
|
91
|
+
is moved by content: it imports pytest/unittest and defines tests → test; it imports an eval framework
|
|
92
|
+
(ragas, deepeval, promptfoo, lm_eval, inspect_ai, mteb, beir, trulens, langsmith `evaluate`) or has
|
|
93
|
+
training-loop markers → eval; a JS/TS file that imports vitest, `@jest/globals`, `node:test` or mocha and defines
|
|
94
|
+
tests → test, one that imports promptfoo, evalite, autoevals or braintrust → eval: in every language, an
|
|
95
|
+
eval-framework import puts a file in the eval group (design rev3 §3.15); a Go file generated by MockGen or holding a
|
|
96
|
+
testify `mock.Mock` → test (a test double wherever it lives); a C / C++ file that includes gtest, gmock, Catch2, Unity,
|
|
97
|
+
cmocka or check and defines tests or mocks → test. `--group 'glob=group'` always wins.
|
|
98
|
+
|
|
99
|
+
## Entries
|
|
100
|
+
|
|
101
|
+
`kind` ∈ `http` (`method`, `path` with every `include_router` prefix up the chain and the router's own; only a
|
|
102
|
+
decorator whose receiver is an `APIRouter` or `FastAPI` app makes one), `queue-consumer` / `scheduled` (`key`),
|
|
103
|
+
`cli`, `main`, `registered-callback` (`via`, e.g. `FastAPI(lifespan=…)`), and from TS/JS: `http` from Express
|
|
104
|
+
(`app`/`router` methods and `.route()` chains, every `app.use(prefix, router)` mount up the chain, `:id` written
|
|
105
|
+
`{id}`) and Next.js route handlers and API routes, and `ui-route` (`path`) from React Router (route objects and
|
|
106
|
+
`<Route>` elements, nested) and Next.js pages, at the component rendered; a handler or component a second route also
|
|
107
|
+
reaches keeps one entry and lists the other routes in `alsoAt`. From Go: `http` from net/http (`Handle` /
|
|
108
|
+
`HandleFunc`, the Go 1.22 `"METHOD /path"` pattern, else any method `*`), gin, echo and chi (prefixes from `Group`,
|
|
109
|
+
`Route`, `Mount`, `StripPrefix` and a router handed to a function; `:id`, `*rest`, `{id:re}`, `{rest...}` written
|
|
110
|
+
`{id}`), `rpc` (`path` `/<pkg>.<Service>/<Method>`, `key`, `service`) for each method a `Register<Svc>Server` call
|
|
111
|
+
serves, and `main` for `func main()` in `package main`. From C / C++: `command` (`key`, `table`) for each row of a
|
|
112
|
+
string-to-handler table (a C array of `{ "name", fn }` rows, designated or positional; a C++ map initialised or
|
|
113
|
+
assigned with such pairs), `signal` (`key`: the signal) for a `signal()` / `sa_handler` handler, and `main` (not a
|
|
114
|
+
test file's: that is its test runner). Each carries `group`, `file`,
|
|
115
|
+
`line` and the `rule` that made it. An entry in the test group is not a flow; `codemap.md` suggests the
|
|
116
|
+
override that moves it.
|
|
117
|
+
|
|
118
|
+
## Edges
|
|
119
|
+
|
|
120
|
+
`{ from, to, kind, line, … }`. Kinds: `static`, `ctor` (to the class and its `__init__`), `virtual` (a
|
|
121
|
+
subclass override reachable by CHA), `dispatch` (one of several narrowed targets), `framework`, `queue`,
|
|
122
|
+
`http`, `spawn`. Optional fields:
|
|
123
|
+
|
|
124
|
+
| Field | Meaning |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `rule` | the pack that produced it: `fastapi.Depends`, `taskiq.literal-key`, `taskiq.kiq` / `.delay` / `.apply_async`, `registry`, `haystack.pipeline`, `py.http-client`, `process-spawn`, `pytest.fixture[.autouse]`, `py.callback`; from TS/JS `js.http-client` (a frontend call to a route in any language), `express.middleware`, `js.test-hook`, `js.callback` (a function handed to a call or a JSX event prop runs from there); from Go `go.middleware`, `grpc.client` (an `http` crossing to the rpc handler: generated `cc.Invoke` with the method path, or an external client by service and method name), `go.test-main`, `go.callback` (a function or method value handed to a call); from C / C++ `c.handler-table` (`dispatch` from the function that calls through the table to every row's function, one `site`, each with its `key`), `c.fn-pointer` (a call through a struct field to every function assigned to that field), `gtest.fixture`, `unity.setUp`, `c.callback`; `evidence` says why, with `file:line` |
|
|
127
|
+
| `groups` | `[fromGroup, toGroup]` when the edge crosses groups (labelled, never cut) |
|
|
128
|
+
| `group` | on `registry` and `py.http-client` links: the producer's group (a test that enqueues is test evidence, not a production crossing) |
|
|
129
|
+
| `recvClass`, `site`, `narrowedBy`, `self` | receiver typing: the concrete class, the call site, the factory or `class-attr` that narrowed it, a `self.m()` call |
|
|
130
|
+
| `super`, `passCtx` | a `super().m()` call; the receiver passes through |
|
|
131
|
+
| `selectors`, `order`, `component` | Haystack: the factory guards that select a component, its connect order |
|
|
132
|
+
| `cfg` | the config-reading guards around the call (`settings.…`, `getattr(settings, …)`, `is_prod`, `env_mode`, or a local assigned from one; TS/JS `process.env`; Go `os.Getenv`, `cfg.` / `config.`, flags; C `getenv` and every `#ifdef X` / `#if X` build guard) |
|
|
133
|
+
| `goroutine`, `deferred` | Go: the call is a `go` or `defer` statement |
|
|
134
|
+
| `onFailure` | the call sits in an `except` clause: it runs only when the guarded code raised, e.g. `except TimeoutError` (TS/JS: `catch (e)`) |
|
|
135
|
+
| `jsx` | the edge is a rendered component (`<Page />`): to the function, or to a class's constructor and `render` |
|
|
136
|
+
| `passedBy`, `passedAt` | `py.callback`: the callee runs a callable or coroutine that `passedBy` handed it at `passedAt`. The link is only true for that caller: a walk that reached the callee some other way must not follow it |
|
|
137
|
+
|
|
138
|
+
## Tests as evidence
|
|
139
|
+
|
|
140
|
+
- `tests[]`: `id`, `file`, `line`, `end`, `asserts`, `assertLines`, and per test `direct[]` (`to`, `via` ∈
|
|
141
|
+
`body` / `fixture` / `helper`, `viaId`, `kind`, `rule?`, `line`) and `mocked[]`. A direct link is an edge
|
|
142
|
+
from the test's own side (its body, the fixtures it binds, test-side helpers, its test class) into
|
|
143
|
+
non-test code that the test does not mock. It is the only link narration may call "tested".
|
|
144
|
+
- `fixtures[]` (`autouse`), `fixtureBindings[]` (parameter → fixture; class scope, then module, then the
|
|
145
|
+
nearest `conftest.py`; autouse binds every test in scope).
|
|
146
|
+
- `mocks[]`: `fn`, `raw` target, resolved `target` (the name in the importing module, followed to its
|
|
147
|
+
definition), `how`, and `scope: "session"` plus `conftest` for a patch started in `pytest_configure`,
|
|
148
|
+
`pytest_sessionstart` or at conftest module level: it applies to every test under that conftest.
|
|
149
|
+
- `testSummary`: counts, including production functions with at least one direct link.
|
|
150
|
+
- TS/JS (jest, vitest, node:test, mocha in test-group files): a test is an `it` / `test` call (with `.each` titles kept
|
|
151
|
+
as written), named by its describe path; `beforeEach` / `beforeAll` / `afterEach` / `afterAll` are autouse
|
|
152
|
+
fixtures for the tests under their `describe`, and a hook in a setup file (`setupTests.*`, `jest.setup.*`,
|
|
153
|
+
`vitest.setup.*`, or one a jest / vitest config lists in `setupFiles`) binds every test below it (`covers`: the
|
|
154
|
+
config's directory for a listed one). `jest.mock('mod')` / `vi.mock('mod')` replace every export of the module the
|
|
155
|
+
string resolves to, or only the factory's keys when the factory spreads the real module; they are hoisted, so their
|
|
156
|
+
`fn` is the test file's `<module>`, and in a setup file they are `scope: "session"` mocks. `jest.spyOn` /
|
|
157
|
+
`vi.spyOn` replaces a method only when a `.mockImplementation` / `.mockReturnValue` / `.mockResolvedValue` /
|
|
158
|
+
`.mockRejectedValue` follows. One mock record per replaced symbol.
|
|
159
|
+
- Go (`go test` in `_test.go` files): a test is `TestXxx(t *testing.T)` or `FuzzXxx(f *testing.F)`; assertion lines
|
|
160
|
+
are `t.Error*` / `t.Fatal*` / `t.Fail*`, testify `assert.*` / `require.*` (or an `assert.New(t)` value's) and a
|
|
161
|
+
testify mock's `AssertExpectations` / `AssertCalled`; a subtest's body (a function literal handed to `t.Run`) is its
|
|
162
|
+
test's, `subtests[]` names them (`<tc.name>` when the name is a table field) and `table: {over, line, cases}` counts
|
|
163
|
+
a table test's composite-literal cases. `TestMain` is an autouse fixture for every test of its directory. A
|
|
164
|
+
test-group type the test side constructs (`&fake{}`, `new(T)`, a gomock `NewMockX`) that implements a production
|
|
165
|
+
interface is a mock (`how`: `go.fake`, `testify.mock`, `gomock`) of every production implementation of that
|
|
166
|
+
interface's methods, in that test.
|
|
167
|
+
- C / C++: a test is a gtest `TEST` / `TEST_F` / `TEST_P` / `TYPED_TEST` (`Suite.Name`), a Catch2 `TEST_CASE` /
|
|
168
|
+
`SCENARIO` / `TEST_CASE_METHOD` (its `SECTION`s in `subtests[]`), or a `test_*` function in a test-group file
|
|
169
|
+
(Unity, cmocka, a hand-rolled runner), with `framework` (`gtest`, `catch2`, `c`); assertion lines are `EXPECT_*` /
|
|
170
|
+
`ASSERT_*` (gmock's `EXPECT_CALL` included), `REQUIRE*` / `CHECK*`, `TEST_ASSERT*`, `assert_*`, `ck_assert*` and
|
|
171
|
+
`assert`. A `TEST_F` fixture's constructor, `SetUp` and `TearDown`, and a Unity file's `setUp` / `tearDown`, are
|
|
172
|
+
autouse fixtures; a `TEST_F` body resolves members through its fixture. A test-group class deriving from a
|
|
173
|
+
production class (a gmock mock or a fake) that the test side constructs, or that a fixture holds as a field, mocks
|
|
174
|
+
every production implementation of the methods it overrides (`how`: `gmock`, `cpp.fake`).
|
|
175
|
+
|
|
176
|
+
## Config-aware dispatch (`config.profiles`, `config.sites`)
|
|
177
|
+
|
|
178
|
+
A profile is code defaults, or one in-repo config file (`.env`, YAML incl. Helm env lists, TOML, JSON;
|
|
179
|
+
plus `--config-root` dirs) that sets a key a dispatch guard reads; each carries its file's `group`.
|
|
180
|
+
Production and dev sites are decided under code defaults and prod/dev profiles only; eval and test
|
|
181
|
+
profiles decide only eval and test sites. Each site lists its alternatives (with their group, so an
|
|
182
|
+
eval-group mock is visibly an alternative), the guards, a yes / no / ? per profile, the settings read,
|
|
183
|
+
the files that set them, and a classification: `config-narrowed` (every profile decides one class),
|
|
184
|
+
`config-dependent (runtime input)` (a guard reads a parameter) or `config-dependent (value not in repo)`.
|
|
185
|
+
`getattr(settings, "x", d)` reads a declared field `x` exactly as `settings.x` does (a profile's value, else the
|
|
186
|
+
field's default); when no field declares `x`, `d` decides, since a settings object ignores undeclared env vars.
|
|
187
|
+
|
|
188
|
+
Config is read only from git-tracked files (a local, untracked `.env` never is; a tracked `sample.env` or
|
|
189
|
+
`.env.example` is) and never from an `--exclude`d path. A value is recorded only for a key a dispatch guard
|
|
190
|
+
reads; any other fact records its key and file only. A secret-looking key (containing KEY, TOKEN, SECRET,
|
|
191
|
+
PASSWORD, CREDENTIAL, PRIVATE, AUTH, …) or a credential-shaped value (a URL with a password, a private key, a
|
|
192
|
+
JWT, a provider token, a long random string) is written as `set (redacted)` everywhere in `codemap.json` and
|
|
193
|
+
`codemap.md`, settings defaults included; dispatch is still decided on the value as read.
|
|
194
|
+
|
|
195
|
+
## Self-check
|
|
196
|
+
|
|
197
|
+
`ok` is false — and `mine` exits 2 — when the parser saw a definition the extractor did not record
|
|
198
|
+
(`defsExtracted` < `defNodes`), or when two definitions in different scopes collapsed into one symbol
|
|
199
|
+
(fewer non-`main` symbols than `defScopes`, the distinct qualified names the parser saw; a definition repeated in
|
|
200
|
+
both branches of an `if` / `try` is one). Also reported: files with syntax errors, dependency files that are not
|
|
201
|
+
valid TOML (`dependencyFilesNotParsed`, above), resolved calls, unresolved calls
|
|
202
|
+
by where their head name comes from (`inRepo`, `external`, `builtin`, `local`), and the in-repo miss rate:
|
|
203
|
+
calls whose head is an in-repo name but whose target could not be resolved. `packageRoots` lists the package roots
|
|
204
|
+
below the repo root (above); `crossRootImports` the calls resolved through a root that does not hold the importing
|
|
205
|
+
file (`name`, `root`, `calls`: right only when that root is on its sys.path) and `ambiguousImports` the calls lost
|
|
206
|
+
because several roots claim their import and none holds the importer (`name`, `roots`, `calls`). `externalLikeRepoDirs` counts calls into imports that resolved as external although an
|
|
207
|
+
in-repo directory with no `__init__.py` (a namespace package) has their name, with their `rate` among in-repo calls:
|
|
208
|
+
a high rate means those in-repo calls are not linked.
|
|
209
|
+
|
|
210
|
+
## `candidates.json` (`propose.mjs`)
|
|
211
|
+
|
|
212
|
+
One candidate per prod, eval and dev entry (test-group entries are not walked). A candidate is the tree of paths from its entry, walked over `static`, `ctor`, `framework`,
|
|
213
|
+
`dispatch`, `virtual`, `queue`, `http` and `spawn` edges with the receiver class carried, so a `self.m()` call
|
|
214
|
+
resolves under the class that was constructed. No candidate is chosen here.
|
|
215
|
+
|
|
216
|
+
The walk's rules:
|
|
217
|
+
|
|
218
|
+
- **A non-test walk never enters test-group code.** A test subclass or fake that production CHA reaches is a
|
|
219
|
+
*test double*: listed in `testDoubles`, never walked.
|
|
220
|
+
- **A production walk expands any non-test code** it reaches; a node from another group is listed in `crossGroup`.
|
|
221
|
+
- **An eval or dev walk folds every production symbol** into a `uses-prod` leaf, and its crossings into production
|
|
222
|
+
are `exercises` links: an eval flow is the harness's own story plus the production flows it drives.
|
|
223
|
+
- **Helpers fold by same-group fan-in.** A symbol with at least `--fanin` (6) callers from its own group and an
|
|
224
|
+
in-repo subtree of at most `--leafy` (5) symbols is a `helper` leaf. Test callers never count, so a production
|
|
225
|
+
function many tests call is not folded.
|
|
226
|
+
- **Collaborators fold at the interface.** An abstract target is an `interface` leaf; two or more concrete targets
|
|
227
|
+
of one call site are `alternative` leaves, with the config site that selects them. `--no-iface-fold` expands them.
|
|
228
|
+
- **Data-model constructors** (dataclasses, pydantic models, exceptions, enums) are listed in `constructs`, not walked.
|
|
229
|
+
- **A `py.callback` link is followed only from the caller that handed the callable over**, as a child of the callee
|
|
230
|
+
that runs it (`via: callback`).
|
|
231
|
+
|
|
232
|
+
| Field | What it holds |
|
|
233
|
+
|---|---|
|
|
234
|
+
| `params` | `fanin`, `leafy`, `ifaceFold` |
|
|
235
|
+
| `totals` | entries per group, candidates and top-level candidates per group, `continuations`, `helpers` (symbols that fold, all groups), `testDoublesRefused`, production functions with a direct test link and reached from tests |
|
|
236
|
+
| `unwalked[]` | entries whose symbol is not in the code map (`propose` exits 2) |
|
|
237
|
+
| `candidates{entry id}` | `group`, the entry's `kind` / `method` / `path` / `key` / `file` / `line`; `continues` (below); `symbols`, `own`, `folded`, `modules`, `maxDepth`; `ownIds[]`; `nodes[]` (`id`, `depth`, `via`, `ctx` receiver, `parent`, `folded` ∈ `helper` / `interface` / `alternative` / `uses-prod`); `crossings[]` (`from`, `to`, `kind`, `evidence`, `intoGroup`); `exercises[]` (the crossings into another group); `dispatch[]` (`site`, `how` ∈ `receiver` / `cha` / `factory`, `before` → `after` target counts, `targets`, `narrowedBy`, `config` = the factory's classification and alternatives with their guards, Haystack `selectors`); `branches[]` (followed calls under a config guard: `cfg`, `onFailure`); `constructs`, `testDoubles`, `usesProd`, `crossGroup`; `segments[]` |
|
|
238
|
+
| `testLinks[]` | per test: `asserts`, `assertLines`, `mocked`, `direct[]` (mine's direct links) and `exercised` / `exercisedIds` |
|
|
239
|
+
| `bySymbol{id}` | `direct[]` (the tests with a direct link to it) and `exercised` (how many tests reach it) |
|
|
240
|
+
| `symbols{id}` | `group`, `kind`, `file`, `start`, `end`, `module` of every symbol a candidate or test link touches |
|
|
241
|
+
|
|
242
|
+
**Segments and continuations.** A candidate is cut into segments at each crossing that stays in its group: the
|
|
243
|
+
entry, then each crossing target (`root`, the `from` that crosses, `kind`, its own and folded counts, `ownIds`).
|
|
244
|
+
A crossing into another group is an `exercises` link, not a segment. An entry reached through another candidate's
|
|
245
|
+
same-group crossing is a **continuation**: `continues` lists the candidates that reach it, and it is not ranked
|
|
246
|
+
on its own.
|
|
247
|
+
|
|
248
|
+
**Test reach is reach, not proof.** From each direct link's target, with the call site's receiver, the walk runs
|
|
249
|
+
until it meets a symbol the test mocks (`mocked`, session-wide mocks included), a crossing or test code. Only
|
|
250
|
+
expanded symbols and helpers count. Narration calls a hop "tested" only for a direct link.
|
|
251
|
+
|
|
252
|
+
## `ranking.json` and `ranking.md` (`rank.mjs`)
|
|
253
|
+
|
|
254
|
+
Each group's top-level candidates (prod, eval, dev; continuations excluded), ordered by greedy marginal coverage of
|
|
255
|
+
that group's own symbols, ties broken by more crossings. **There is no cut line and no series**: no row is marked
|
|
256
|
+
chosen, and `--min-new`, `--min-share` and `--series` do not exist. The agent proposes the series from the full
|
|
257
|
+
tables.
|
|
258
|
+
|
|
259
|
+
| Field | What it holds |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `params` | `explainTop` (10) and `explainTopEval` (5): how many prod and eval rows `ranking.md` explains in full |
|
|
262
|
+
| `totals` | top-level rows and universe (distinct own symbols) per group, continuations |
|
|
263
|
+
| `ranked.{prod,eval,dev}[]` | per row: `rank`, the entry and its route or key, `gain` (new own symbols), `cumulative`, `coverage`, `own`, `folded`, `modules`, `crossings`; `nearest` (the most-overlapping higher row) and `jaccard`; `pinned` (own functions with a direct test link), `tests` (distinct tests pinning own code), `testsNew` (tests pinning this row's new code); `cfg` (distinct dispatch points, one per site and target set, that a config read narrows, that have two or more concrete targets, or that Haystack selectors pick: the same points the explained rows list); `evals` (top-level eval rows whose `uses-prod` leaves or crossings reach this row's own code). Eval and dev rows add `exercises[]` and `usesProd[]`, each with `flowRank`, the first production row that owns it |
|
|
264
|
+
| `merged[]` | prod and eval top-level rows on one list, evidence only |
|
|
265
|
+
| `shared[]` | production modules whose functions appear in at least 3 production rows and that at least 5 tests pin directly: `module`, `candidates`, `firstOwner` (rank) and `firstOwnerEntry`, `functions`, `directTests`. Cross-cutting code (auth, retry, tokens) has no entry point of its own, so this table is where it shows |
|
|
266
|
+
| `continuations[]` | every continuation with its parents |
|
|
267
|
+
|
|
268
|
+
`ranking.md` prints every row of every group with these columns, what each eval row exercises, the dev group as
|
|
269
|
+
filtered by default (promotable in `flows.json`), the shared table, the continuations, the merged order, and for the
|
|
270
|
+
top prod and eval rows their segment table, dispatch points with the config that selects them, and config-guarded
|
|
271
|
+
branches.
|
|
272
|
+
|
|
273
|
+
## `trace.json` (`trace.mjs`, opt-in)
|
|
274
|
+
|
|
275
|
+
Written only after the user said yes to one exact test command (`--i-ran-this-by-the-user`). `trace.mjs` refuses
|
|
276
|
+
(exit 1) without that flag; refuses (exit 3) anything but `pytest`, `py.test`, `python -m pytest` or `python -m
|
|
277
|
+
unittest` (python, python3 or python3.N, by name or path; a bare name is found in the repo's `.venv/bin` or
|
|
278
|
+
`venv/bin`, then on PATH), `node --test` (only `--test-*` and type-stripping flags), `npx jest` or `npx vitest [run]`
|
|
279
|
+
(no npx flags, no watch mode, none of the flags the trace sets itself: reporter, coverage, pool; a flag's value as
|
|
280
|
+
`-t=name` or `-t name`, split only for the jest and vitest flags that take a value, so a word after any other flag is
|
|
281
|
+
a test path or filter), or `jest` / `vitest`
|
|
282
|
+
from the repo's `node_modules/.bin`; `go test` or `go -C <dir> test` (packages `.`, `./dir`, `./dir/...` in the
|
|
283
|
+
module; only `-run`, `-skip`, `-short`, `-v`, `-count`, `-tags`, `-timeout`, `-failfast`, `-p`, `-parallel`, `-cpu`:
|
|
284
|
+
never `-exec`, `-toolexec`, `-json`, `-cover*`, `-o`, `-c`, profiles or `-race`); `make test`, `make check` or
|
|
285
|
+
`make -C <dir> test|check` (no variables, no other target or flag); refuses (exit 1) a jest or vitest the repo has not installed (nothing is ever
|
|
286
|
+
installed, and `npx` itself is never run). jest and vitest run from the package that holds them: the repo root, else
|
|
287
|
+
the one package below it (a `package.json` beside a `node_modules/.bin` holding the runner, such as `frontend/`) that
|
|
288
|
+
holds the code map's test files, whose test files and the paths given are read relative to it (`cwd` in `trace.json`);
|
|
289
|
+
two such packages holding test files are refused (exit 1). It refuses (exit 3), too, a command with any shell syntax (`;`, `&&`, `|`, redirects, `$`, backticks) or a wrapper (`uv run`,
|
|
290
|
+
`env`); and refuses (exit 4) on a platform with no OS sandbox, or where this platform's sandbox does not start
|
|
291
|
+
(`sandbox-exec` on macOS, `bwrap` on Linux, always chosen by platform; there is no option to pick or skip one). It
|
|
292
|
+
never runs unsandboxed.
|
|
293
|
+
|
|
294
|
+
The run: the repo's git-tracked and unignored files (every file but `.git` and virtualenvs, outside a git work tree;
|
|
295
|
+
a submodule or a nested repo by its own git view, never whole, so nothing its own `.gitignore` ignores) are copied to a scratch dir, and the command runs there with the repo's own interpreter, in place and read-only,
|
|
296
|
+
inside the sandbox. The sandbox hides the user's home folder and the original repo: the run reads only the copy, the
|
|
297
|
+
interpreter's prefix, its virtualenv (even one inside the repo) and the prefix that virtualenv was made from; a
|
|
298
|
+
prefix that would expose a hidden path whole (an interpreter installed straight into the home folder) is not exposed,
|
|
299
|
+
and the run says so.
|
|
300
|
+
|
|
301
|
+
- **macOS**: `sandbox-exec` with `tools/trace/sandbox/macos-nonet.sb` plus read rules `lib/sandbox.mjs` adds per run:
|
|
302
|
+
IP traffic denied (localhost included), unix socket connects denied (so no DNS resolver), every write outside
|
|
303
|
+
scratch denied, every read under the home folder and the original repo denied except the exposed paths (and the
|
|
304
|
+
metadata, not the contents, of the dirs above them); no program outside the sandbox acting for the run: opening a
|
|
305
|
+
URL, file or app through LaunchServices, Apple Events and launchd job submission denied (the services behind them
|
|
306
|
+
too), and `open`, `osascript` and `launchctl` not executable;
|
|
307
|
+
- **Linux**: `bwrap` through `tools/trace/sandbox/linux-bwrap.sh`: new network, user, pid, ipc and uts namespaces;
|
|
308
|
+
`/` read-only with `/run`, `/tmp`, `/var/tmp`, home dirs, the user's home folder and the original repo replaced by
|
|
309
|
+
empty tmpfs mounts (the exposed paths mounted back read-only); scratch the only writable path; a seccomp filter
|
|
310
|
+
failing `socket(AF_UNIX)` with EPERM. A new network namespace has its own loopback, so a test may reach a server it
|
|
311
|
+
started itself, which the macOS profile denies;
|
|
312
|
+
- an empty environment: `HOME` and `TMPDIR` in scratch, `PYTHONDONTWRITEBYTECODE=1`, pytest's `-p no:cacheprovider`;
|
|
313
|
+
- an editable install that points into the original repo (a `.pth` path line, a setuptools finder's mapping, an
|
|
314
|
+
`editables` redirect) is sent to the copy by the collector, since the original is hidden;
|
|
315
|
+
- credential-shaped strings (private-key blocks, AWS access key ids, bearer and basic auth values, known token
|
|
316
|
+
formats, JWTs, a URL's password, the value of a key that names a secret) are redacted from `trace.log`, the output
|
|
317
|
+
tail trace.mjs prints and the failure messages in `trace.json`;
|
|
318
|
+
- a time limit (`--timeout`, 900 s): the process group gets SIGTERM, then SIGKILL; what the collector wrote by then
|
|
319
|
+
is kept, `timedOut: true`, exit 5.
|
|
320
|
+
|
|
321
|
+
The collector (`tools/trace/python/sitecustomize.py`, on `PYTHONPATH`; it runs the interpreter's own
|
|
322
|
+
`sitecustomize` it shadows) records every Python call as (nearest in-repo caller, in-repo callee, current test):
|
|
323
|
+
library frames between the two are skipped; code objects are keyed by identity, since twin bodies in two files
|
|
324
|
+
compare equal; lambdas, comprehensions and generator bodies count as the function holding them; an interpreter or
|
|
325
|
+
virtualenv under the repo is library code. The current test is set around each test's setup, call and teardown by
|
|
326
|
+
`fmtrace_plugin.py` (pytest, via `PYTEST_PLUGINS`) or a `TestCase.run` wrapper (unittest), so a call a framework makes
|
|
327
|
+
(a fixture, a DI provider) is still attributed to its test. Only the repo's own tests count: a run a test starts
|
|
328
|
+
inside itself over tests outside the copy (pytester's tmp dir) or an inner unittest suite records no outcome and leaves
|
|
329
|
+
the outer test current; a pytest process over the copy's own tests (an xdist worker, or a child a test starts) records
|
|
330
|
+
them, and `collected` is never less than the tests recorded. Audit hooks record each test's network attempts. Method
|
|
331
|
+
names come from `co_qualname` (Python 3.11+; older interpreters report bare function names, which miss methods). With
|
|
332
|
+
`HOME` in scratch, packages installed only in the user's site dir (`pip install --user`) are not found: use the repo's
|
|
333
|
+
virtualenv.
|
|
334
|
+
|
|
335
|
+
A JavaScript / TypeScript runner is run once per test file (the code map's test files that match the runner's own
|
|
336
|
+
default pattern, selected by the command's positionals as the runner reads them: a path or directory for node, a
|
|
337
|
+
case-insensitive regex over the path for jest, as is jest's `--testPathPattern` (never passed on, since
|
|
338
|
+
`--runTestsByPath` ignores it), a case-insensitive substring of it for vitest), each in its own
|
|
339
|
+
sandboxed process under V8 precise coverage (`NODE_V8_COVERAGE`), on the `node` the command names by path, else the one on PATH, with the repo's
|
|
340
|
+
`node_modules` linked into the copy entry by entry and exposed read-only (all but vite's caches, `.vite`, `.vite-temp`
|
|
341
|
+
and `.vitest`, which the run writes into, so it makes its own in the copy); a workspace package's link (a symlink into
|
|
342
|
+
the repo, such as `node_modules/@app/shared -> ../../packages/shared`) points at its copy, never at the hidden repo.
|
|
343
|
+
Reports: node --test's TAP on stdout; jest `--json --outputFile --runInBand --watchman=false --passWithNoTests`;
|
|
344
|
+
vitest `run --reporter=json --outputFile --pool=threads --passWithNoTests` (a forked worker is killed before it
|
|
345
|
+
writes its coverage). A file whose run ends cleanly with no test reported (the repo's own runner config does not
|
|
346
|
+
collect it, or it defines none) is listed in `notCollected` (`{file, tests}`: the code map's tests in it), never
|
|
347
|
+
counted as an error; those tests never ran, so they are not in `byTest` or `directLinksPerTest`. A covered
|
|
348
|
+
function is matched to a symbol through the script's source map when there is one (Node's own, or the inline map of a
|
|
349
|
+
file jest or vitest compiled through `node:vm`, which `tools/trace/js/fmtrace-srcmap.cjs` records), else by position
|
|
350
|
+
when the script V8 ran is the file (plain JavaScript, or TypeScript with its types stripped in place and a comment
|
|
351
|
+
appended), and otherwise by a name only one symbol of that file has. `tools/trace/js/fmtrace-net.cjs` (`NODE_OPTIONS=--require`) records each process's
|
|
352
|
+
and worker thread's connects and DNS lookups as they happen (a worker its runner terminates never reaches its exit);
|
|
353
|
+
where it cannot, the target is read from the error. A run in which no test file reported a test is exit 2. Attribution is per
|
|
354
|
+
test file and there are no caller → callee pairs, so `pairs`, `edges`, `noStaticEdge` and `callbacks` are empty;
|
|
355
|
+
`granularity: "test-file"`, `files` / `ranFiles` (the test files, and those run before the time limit), `ran`
|
|
356
|
+
(symbol → the test files it ran under), `byFile` (what ran in each) and `byTest` (`file`, `status`, `direct` counted
|
|
357
|
+
against what ran in that file) take their place, and `summary` has `functionsRan`, `unmappedFunctions` (named
|
|
358
|
+
functions that matched no symbol), `anonymousCallbacks`, `directLinksPerTest` and `fixtureLinks` (hooks that ran).
|
|
359
|
+
|
|
360
|
+
| Field | What it holds |
|
|
361
|
+
|---|---|
|
|
362
|
+
| `optIn` | `{by: "user", at, command}`: who opted in, when, and the exact command |
|
|
363
|
+
| `granularity`, `byUnit`, `ran` | coverage runners only: `test-file` (JS), `test` (Go) or `binary` (make); what ran in each unit, and per symbol the units it ran in |
|
|
364
|
+
| `sandbox`, `runner`, `argv` | `macos-sandbox-exec:macos-nonet.sb` or `linux-bwrap:linux-bwrap.sh`; `pytest`, `unittest`, `node-test`, `jest`, `vitest`, `go-test` or `make`; the command as run (for a JS runner, one file's command with `<test file>`; for Go, one test's with `<TestName>`) |
|
|
365
|
+
| `timeoutSec`, `timedOut`, `exitCode`, `durationMs` | the limit and how the run ended |
|
|
366
|
+
| `codemap` | the code map's file, sha256, `commit`, `treeHash`, and `changedFiles`: mined files that differ in the copy (mine again first) |
|
|
367
|
+
| `outcome` | `collected`, `passed`, `failed`, `errors` (collection and setup errors, failed teardowns, a failing unittest class or module fixture, a test the time limit cut off), `skipped`, `xfailed`, `xpassed` |
|
|
368
|
+
| `failures[]` | `test` (code-map id, or null: a collection error, a unittest class or module fixture such as `unittest::setUpClass (pkg.mod.Class)`), `nodeid`, `status`, `phase` (`collect`, `setup`, `call`, `teardown`), `message` |
|
|
369
|
+
| `deniedBySandbox[]` | failing tests that attempted a network connect, or failed on a permission error or on a hidden path not found (bwrap hides a path by mounting an empty dir over it): `network` (the attempts), `permission`. They need a service or a file the sandbox refused; never retried |
|
|
370
|
+
| `pairs[]` | `{from, to, n, tests}`: distinct in-repo caller → callee pairs, with calls and how many tests they ran under |
|
|
371
|
+
| `edges[]` | the code-map edges that fired as a pair: `from`, `to`, `kind`, `rule?`, `passedBy?`, `n`, `tests` (ids). The overlay: an edge not listed is not observed, never removed |
|
|
372
|
+
| `noStaticEdge[]` | pairs no code-map edge of any kind explains, with a `category`: `test-side`, `validator`, `property`, `dunder`, `init`, `other` |
|
|
373
|
+
| `callbacks[]` | the `py.callback` links that fired |
|
|
374
|
+
| `byTest{id}` | per test (parametrized cases merged): `nodeids`, `status`, `ran` (every in-repo function that ran while it was current), `direct` (`total`, `observed`, `missing`: its direct links that did not run under it) |
|
|
375
|
+
| `summary` | records, distinct pairs, functions that ran per group; static edges observed by kind for all edges, production → production and test → non-test; direct links observed per test (and production only); fixture links observed by attribution; no-static-edge counts by category; unmapped records |
|
|
376
|
+
|
|
377
|
+
`go test` runs once per code-map test (`-run ^TestXxx$`, intersected with a `-run` the command gave) in the module
|
|
378
|
+
`-C` names, else the repo root's, else the code map's one module (two or more without `-C` are refused, exit 1), with
|
|
379
|
+
`-json -count=1 -coverprofile -coverpkg=./...`, in the sandbox, with the go the command names, run from inside its
|
|
380
|
+
`GOROOT`. The run may read exactly two dirs the home folder would otherwise hide, read-only: the `GOROOT` and
|
|
381
|
+
`GOMODCACHE` that go's `go env` reports (the default module cache is `$HOME/go/pkg/mod`), never anything else of the
|
|
382
|
+
home folder; a `GOROOT` or `GOMODCACHE` that is the home folder or holds it, or a go in the home folder outside its
|
|
383
|
+
`GOROOT`, is refused (exit 1). `GOCACHE` and `GOPATH` are in scratch; `GOPROXY=off`, `GOTOOLCHAIN=local`,
|
|
384
|
+
`CGO_ENABLED=0`, so nothing is downloaded: a dependency comes from the module cache or the build fails. A test in a Go
|
|
385
|
+
module nested inside the one the run uses (a `go.mod` further down) is that module's, and is left out, as `go test
|
|
386
|
+
./...` leaves it. A symbol ran under a test when a counted block of its profile sits inside
|
|
387
|
+
it (the innermost symbol holding the block): `granularity: "test"`, one unit per test. go test does not instrument
|
|
388
|
+
`_test.go` files, so test-side code (helpers, fakes, `TestMain`) is never observed. A failure whose output shows a
|
|
389
|
+
dial, socket or lookup error is in `deniedBySandbox`.
|
|
390
|
+
|
|
391
|
+
`make` builds and runs its target once with `CC="cc --coverage" CXX="c++ --coverage"` (a command-line variable wins
|
|
392
|
+
over the Makefile's own and reaches compiling and linking; a Makefile that calls a compiler by another name writes no
|
|
393
|
+
counters, exit 2) and `SHELL` set to `tools/trace/c/fmtrace-sh`, which runs each recipe line, with the line's
|
|
394
|
+
`.SHELLFLAGS`, under the shell the Makefile's own top-level `SHELL =` names (else `/bin/sh`), with `GCOV_PREFIX`
|
|
395
|
+
pointing at a dir of its own, so the counters each test binary writes land apart, beside the line's exit status and
|
|
396
|
+
output. Each recipe line that wrote counters is one unit, keyed by the line (`./tests/test_cart`):
|
|
397
|
+
`granularity: "binary"`. After the run, inside the same sandbox, each `.gcda` and the `.gcno` beside its object are
|
|
398
|
+
copied into a fresh dir and read there with `gcov -t`; nothing outside the sandbox opens a file the run wrote except
|
|
399
|
+
through a no-symlink read of a plain file, and the run's process group is killed when it ends. A binary that aborts
|
|
400
|
+
writes no counters. A test belongs to the first unit its own lines ran in, and its direct links are judged against that
|
|
401
|
+
unit's coverage; its status comes from GoogleTest (`[ OK ]`, `[ FAILED ]`) or Unity (`file:line:test:PASS`) output,
|
|
402
|
+
else from its recipe line's exit status, only when every command of the line (one simple command, or each part of an
|
|
403
|
+
all-`&&` chain) has at its head a native executable (ELF or Mach-O) that the build produced in the copy, a relative
|
|
404
|
+
head read from the dir the line ran in (a sub-make's own): one such binary is `passed` on 0 and `failed` otherwise; an
|
|
405
|
+
`&&` chain of them is `passed` on 0 and `unknown` otherwise (a binary that aborted wrote no counters, so which one
|
|
406
|
+
failed cannot be told). Every other line is `unknown` either way:
|
|
407
|
+
a script or a shell or interpreter at its head (`sh tests/run.sh`, a `./run.sh` starting with `#!`, `env`, `python`),
|
|
408
|
+
a command that runs others (`find -exec`, `xargs`, `cd`, `time`), `;`, `||`, a pipe, a loop or a subshell; `not run`
|
|
409
|
+
when its lines never ran. Network errors in a line's output (`connect: Operation not permitted`) put its failing test
|
|
410
|
+
in `deniedBySandbox`. Neither runner records a caller → callee pair, so `pairs`, `edges`, `noStaticEdge` and
|
|
411
|
+
`callbacks` are empty, and `outcome` adds `unknown` and `notRun`.
|
|
412
|
+
|
|
413
|
+
A fixture, a FastAPI `Depends` provider or a queue consumer has no in-repo caller frame, so those links are never
|
|
414
|
+
observed as pairs; a fixture link is observed by attribution (the fixture ran under its test). The trace is one
|
|
415
|
+
configuration with one set of mocks: narration says "observed in the test suite", naming the test configuration.
|
|
416
|
+
|
|
417
|
+
## `flows.json` (written by the agent)
|
|
418
|
+
|
|
419
|
+
The series proposal. Every reason (`why`, `say`, `claim`) is at least three words and not a placeholder (`tbd`,
|
|
420
|
+
`todo`, `…`). An unknown field is a problem, so a typo never passes silently.
|
|
421
|
+
|
|
422
|
+
| Field | What it holds |
|
|
423
|
+
|---|---|
|
|
424
|
+
| `series[]` | `{flow \| core, why}` in video order: every flow and core defined below, once each |
|
|
425
|
+
| `skipped[]` | `{entry, rank?, why}`: one entry each, never a glob; `rank` ("prod #6"), when given, must match the ranking |
|
|
426
|
+
| `promote[]` | `{entry, why}`: a dev-group row brought into consideration; it then needs a decision like any other row |
|
|
427
|
+
| `flows[]` | `{id, title, from, group?, hops, segments?, variants?, teach?, links?}`: `from` is an entry with a candidate; `group`, when given, is the entry's |
|
|
428
|
+
| `cores[]` | `{id, title, hops, pinnedBy?, variants?, teach?, links?}`: shared code with no entry of its own; `pinnedBy` here is `{symbol, test, say, lines?, strength?}` |
|
|
429
|
+
| `triage.excluded[]` | `{match, why}`: a glob (`*`, `?`) over entry ids and segment roots; it must match something |
|
|
430
|
+
| `outOfRepo[]` | `{claim, evidence, selects?}`: context outside the repo, checked for shape only and printed as **unverified**; `selects` names the config sites the claim decides |
|
|
431
|
+
| `codemap`, `candidates`, `trace` | accepted for the reader; the inputs are always the files beside `--candidates` |
|
|
432
|
+
|
|
433
|
+
A hop is a symbol id or `{symbol, lines?, pinnedBy?, exercises?, uses?, break?}`: `lines` ("54-65,70") narrows the
|
|
434
|
+
narrated lines inside the symbol's span; `pinnedBy[]` is `{test, say, lines?, strength?}`; `exercises` names the
|
|
435
|
+
production flow an eval or dev hop drives; `uses` names the core a hop hands over to; `break` starts a new video.
|
|
436
|
+
A flow's first hop is its `from`. `segments` is `{keep: [root], mention: [{root, say}], exclude: [{root, why}]}`.
|
|
437
|
+
A variant is `{entry, why}`. `teach[]` is `{dispatch, say}` (a config site, a factory or a recorded dispatch site)
|
|
438
|
+
or `{guard, say}` (`<symbol>@<line>` of a call under a config read). A declared link is `{from, to, kind, evidence}`
|
|
439
|
+
whose evidence starts with `file:line` or `file:a-b` that exists in the repo.
|
|
440
|
+
|
|
441
|
+
## `flows.validated.json` and `flows.md` (`apply-flows.mjs`)
|
|
442
|
+
|
|
443
|
+
`apply-flows` exits 2 on any problem, writes `flows.md` with every problem, and removes a stale
|
|
444
|
+
`flows.validated.json`, so nothing can approve or plan an invalid list. The checks:
|
|
445
|
+
|
|
446
|
+
| Check | Rule |
|
|
447
|
+
|---|---|
|
|
448
|
+
| reasons | every series item, skip, variant, promotion, exclusion, mention and citation carries a real reason |
|
|
449
|
+
| C1′ high-ranked rows | in each group, a row ranked at most 10 (prod) / 5 (eval) / 0 (dev), or the ranking's `explainTop` / `explainTopEval` when larger (a smaller one never lowers the floor), or at or above the lowest-ranked row the series includes, is a series flow's `from`, a variant or a skip, each with a reason; an exclusion glob does not count |
|
|
450
|
+
| C1 entry triage | every non-test entry is a flow's `from`, a continuation of one, a variant, skipped, excluded, or a dev row (filtered unless promoted; a dev continuation is filtered with its parent). Eval rows are never implicit |
|
|
451
|
+
| C2 segment triage | every segment of a kept candidate (a series flow's `from`) is kept, mentioned with a `say`, or excluded (in `segments.exclude` or by a glob) |
|
|
452
|
+
| C3 hop fidelity | each hop is within 4 calls of the previous hop, or of the nearest earlier hop that reaches it (back up the narrated stack): a path in the flow's candidate walk (or a variant's; for a core, every series flow's), returning up the tree then calling down, never down then up; or a directed path over recorded edges of any kind, never into test code; or a declared link. A hop that is a variant's entry starts that variant's chain |
|
|
453
|
+
| dispatch | a hop on a config alternative that no profile selects is refused unless an `outOfRepo` claim's `selects` names its site (then printed as selected only by an unverified claim); a hop reached through a CHA or dispatch edge must be in a walk tree, where the receiver resolved it |
|
|
454
|
+
| C3′ test fidelity | a cited test has a direct link to the hop and does not mock it; a test that only reaches the hop may be cited only with `"strength": "reachable"` |
|
|
455
|
+
| groups | no hop is test-group code; an eval or dev flow's hops are not production code, and its `exercises` hop must call, cross into or declare a link into that flow's code |
|
|
456
|
+
| shape | no unknown field, unique ids, real titles (not slugs, ellipses or "Part N"), a flow's `group`, when given, is its entry's, `uses` names a core with no cycle, a `lines` range inside its span, `teach` names a recorded point |
|
|
457
|
+
|
|
458
|
+
`flows.validated.json` (schema 1, `ok: true`) holds everything `flow-plan` needs: `inputs` (the flows, candidates,
|
|
459
|
+
code map and ranking files with their sha256), `params` (`explainTop`, `explainTopEval`: the effective K, never below 10 and 5; `maxPath`), `series[]`
|
|
460
|
+
(`kind`, `id`, `title`, `why`, `rank`), `flows[]` and `cores[]` with every hop resolved (`symbol`, `name`, `file`,
|
|
461
|
+
`start`, `end`, `sha256`, `lines`, `connection` (`how`: entry, start, variant, candidate, edges or link; `path`; `steps[{from, to, kind, rule, evidence, down}]`),
|
|
462
|
+
`crossing`, `break`, `uses`, `exercises`, `drive` (an `exercises` hop's `{kind, to, evidence}`: the walk's crossing into that flow's code, else a declared link, else a direct call, `kind: "call"`), `selectedBy`,
|
|
463
|
+
`pinnedBy[{test, file, line, end, asserts, assertLines, lines, say, strength, via, observed}]`), segments with their
|
|
464
|
+
decision, variants with their rank, `teach[{dispatch, guard, say, hop, alternatives}]` (the hop that holds the
|
|
465
|
+
point, else its nearest narrated ancestor), `links`; `skipped`, `promote`, `excluded` (with what each rule matched);
|
|
466
|
+
`triage` (every entry's status and the high-ranked rows per group); `outOfRepo` (`verified: false`); and `tests`
|
|
467
|
+
(`sessionMocks`, `autouseMocks`: mocks every test under a conftest runs with).
|
|
468
|
+
|
|
469
|
+
`flows.md` opens with the **approval table**: the series with group, rank, size and reason; the skipped rows; the
|
|
470
|
+
variants; the promoted and dev rows; and the estimate from `flow-plan`'s cost model. Then the C1′ and C1
|
|
471
|
+
accounting, every flow and core with every hop, how it is reached, the rule and evidence of each edge and the cited
|
|
472
|
+
tests with their assertion lines, the segments, taught branches and declared links, the hops kept by two or more
|
|
473
|
+
flows (core candidates), the out-of-repo claims and the test configuration. Two runs give byte-identical outputs.
|
|
474
|
+
|
|
475
|
+
## `flows.approved.json` (`approve-flows.mjs`)
|
|
476
|
+
|
|
477
|
+
`{schema, tool, flowsFile, flowsHash, series[{kind, id, title, why}], approvedBy, at, note}`: `flowsHash` is the
|
|
478
|
+
sha256 of `flows.validated.json`. It is written only after the user's explicit yes (the skill's rule: code cannot
|
|
479
|
+
prove a human said yes). `approve-flows` exits 2 when the validated list is missing, did not pass, or `flows.json`
|
|
480
|
+
changed since it was validated.
|
|
481
|
+
|
|
482
|
+
## `plan.json` and `plan.md` (`flow-plan.mjs`)
|
|
483
|
+
|
|
484
|
+
`flow-plan` refuses (exit 5) unless `flows.approved.json` carries the current `flows.validated.json`'s sha256 and
|
|
485
|
+
`flows.json` still matches what was validated: no plan, so no script, exists before the user approves the list.
|
|
486
|
+
|
|
487
|
+
The plan has the coursework plan's shape, so `apply-enrichment`, `script-check`, `timing-report` and the per-video
|
|
488
|
+
stages read it unchanged: `generatedFrom`, `kind: "codebase"`, `model` (below), `forms: ["overview", "flow",
|
|
489
|
+
"core"]` (what `apply-enrichment` accepts), `approval` (`by`, `at`, `note`, `flowsHash`), `inputs`, `totals`
|
|
490
|
+
(`videos`, `seriesMinutes`, `longestVideoMin`, `capMinutes`, `hops`, `flows`, `cores`), `coverage[]` (every hop:
|
|
491
|
+
`item`, `hop`, `symbol`, `section`, `video`), `orphans`, `capViolations`, `outOfRepo`, `tests`, and `videos[]`:
|
|
492
|
+
`index`, `title` (a placeholder for `/enrich`), `form`, `group` (the flow or core id; `timing-report` groups by it),
|
|
493
|
+
`flowGroup` (prod or eval), `words`, `seconds`, `minutes`, `formWhy` (the series reason), `sources` (the hops'
|
|
494
|
+
files), `artefacts` (the cited tests' files), `evidence`, `teach`, `sections` (`"<file> :: <symbol> @L<start>"`,
|
|
495
|
+
what a script's `covers` names) and `sectionSpans` (each hop's span, sha256, narrated lines and `pinnedBy`).
|
|
496
|
+
|
|
497
|
+
Videos: one `overview` first (its sections are the series items), then each series item in order. A hop with
|
|
498
|
+
`break` starts a new video; hops pack greedily under `--max-min` (25); on overflow a video splits at its latest
|
|
499
|
+
crossing, else at the overflowing hop; a hop is never split and two items are never glued; a piece under 2.5
|
|
500
|
+
minutes merges with a neighbour of the same item when the two fit (never across a `break`). C4: every kept hop is
|
|
501
|
+
assigned to exactly one video; an orphan or a video over the cap exits 2. Re-planning when `plan.enriched.json`
|
|
502
|
+
exists and the tree, candidates, flows, approval or cap changed exits 4 unless `--force`.
|
|
503
|
+
|
|
504
|
+
The cost model (`lib/decompose.mjs`). WPM and EXPANSION are coursework's measured `model.mjs`; FRAME, HOP_BASE,
|
|
505
|
+
WPL, BRANCH and TEST are calibrated on one narrated pilot code video (8 hops, 96 narrated lines, 2 taught branches,
|
|
506
|
+
3 cited tests, 1032 words; `CALIBRATION` holds the counts). BOUNDARY and the overview's words are still estimates:
|
|
507
|
+
the pilot crossed no boundary and produced no overview.
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
words = FRAME 150 + Σ hops (HOP_BASE 5 + WPL 6 × narrated) + crossings × BOUNDARY 60
|
|
511
|
+
+ taught branches × BRANCH 70 + min(3, cited tests) × TEST 45 (the overview: 1000 words)
|
|
512
|
+
narrated = count(hop.lines) or min(22, ceil(0.5 × span lines))
|
|
513
|
+
minutes = words / 153 × 1.10
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
A crossing is a hop reached through a queue, HTTP or spawn edge, or an eval hop that `exercises` a production flow.
|
|
517
|
+
|
|
518
|
+
## Code panels (`excerpt.mjs`)
|
|
519
|
+
|
|
520
|
+
A panel fragment for a `code` scene, printed to stdout, cut from the file as mined (the file's sha256 must still be
|
|
521
|
+
the code map's, and a symbol's span its own; else exit 2: re-mine):
|
|
522
|
+
|
|
523
|
+
| Field | What it holds |
|
|
524
|
+
|---|---|
|
|
525
|
+
| `file`, `lang` | the file's path (the panel's tab) and its language |
|
|
526
|
+
| `lines[]` | the shown lines, byte for byte, and the elision markers between them |
|
|
527
|
+
| `numbers[]` | each line's real line number; `null` on an elision marker |
|
|
528
|
+
| `elided[]` | `{at, from, to}`: the marker at index `at` stands for lines `from`–`to` |
|
|
529
|
+
| `source` | `{file, symbol \| test + pins + via, start, end, sha256, fileSha256, commit}`: the span shown, its sha256, the file's sha256 when mined and the mined commit; what `excerpt-check` verifies |
|
|
530
|
+
|
|
531
|
+
`--lines` ("54-65,70", inside the span) picks the narrated lines; each unshown run inside the span becomes one
|
|
532
|
+
marker, `⋯ N lines`, at the run's outer indent (a run of blank lines is shown instead). Without it the whole span is
|
|
533
|
+
shown. More than 22 shown lines exits 2. With `--test <id>` the span is the test's: its def line, the line
|
|
534
|
+
where it calls `--symbol` (when that call is in its own body; a fixture or helper calls from elsewhere, recorded in
|
|
535
|
+
`via`) and its assertion lines. The test must have a direct link to the symbol and must not mock it (C3′), else
|
|
536
|
+
exit 2.
|
|
537
|
+
|
|
538
|
+
Scene-code reads `numbers[]` for the number column and styles the `elided[]` lines apart. A panel with
|
|
539
|
+
`numbers[]` takes one `focus` act per sentence (`{op: "focus", panel, lines: [1-based panel lines], from, to,
|
|
540
|
+
narration}`), and a window that starts as the previous one ends keeps the dim; a coursework panel keeps its single
|
|
541
|
+
focus act. A `{op: "pause", from, to, text?}` act shows the pause badge. A scene with `segment: "<key>"` is
|
|
542
|
+
timed from that segment's window: the host (opened with `?work=`) reads the window from `timeline.js` and plays
|
|
543
|
+
the scene at video time minus the window's start, and draws no progress bar; a named
|
|
544
|
+
segment with no window throws. A coursework scene, with no `segment`, keeps the video clock and its own `total`.
|
|
545
|
+
|
|
546
|
+
## `excerpt-check.mjs` (C6)
|
|
547
|
+
|
|
548
|
+
`--work out/<name>/<NN> --repo <root> --script …` reads the video's code scenes (the manifest's `code` segments),
|
|
549
|
+
`lines.json` and the video's script (exit 1 without any of them):
|
|
550
|
+
|
|
551
|
+
- **drift, exit 2**: every panel has an excerpt `source`; the file it names is the mined one (the working tree's
|
|
552
|
+
file when its sha256 matches, else `git show <commit>:<file>`); the span's sha256 matches; each numbered line
|
|
553
|
+
equals that line of the file, in order, inside the span, with no line hidden except behind a marker that names
|
|
554
|
+
exactly its gap; at most 22 shown lines.
|
|
555
|
+
- **focus, exit 3**: every shown non-blank line is in a focus act whose `narration` names lines of the segment in
|
|
556
|
+
`lines.json` and whose window starts at the first line's onset and ends between the last line's end and the next
|
|
557
|
+
line's onset (scene time = line time + `lead`, ±0.06 s). A pause segment (its script segment carries `pause`; it must be a code scene with panels) is exempt, must carry a `pause` act, and a video has at most 3;
|
|
558
|
+
a `pause` act on any other segment is an error. Every code scene names its own segment (`segment` equal to its
|
|
559
|
+
manifest key), since its acts are timed from that window.
|
|
560
|
+
|
|
561
|
+
A pause segment's script carries `"pause": {"dwell": <s>}` with 0 < dwell ≤ 2, said out loud ("pause" in its
|
|
562
|
+
text), with `pad` equal to the dwell; `script-check` adds the dwell to its estimate and errors on a `pad` that
|
|
563
|
+
differs from the dwell and on a fourth pause segment.
|
|
564
|
+
|
|
565
|
+
## Diagram skeletons (`flow-diagram.mjs`)
|
|
566
|
+
|
|
567
|
+
`--flows out/<name>/flows.validated.json --as map|sequence|exercises [--flow <id>]` prints JSON; exit 1 on bad
|
|
568
|
+
flags, an unknown flow, or `exercises` on a flow with no `exercises` hop.
|
|
569
|
+
|
|
570
|
+
| `--as` | Output |
|
|
571
|
+
|---|---|
|
|
572
|
+
| `map` | `{host: "diagram", items, config: {layout, nodes, edges}, crossings}`: one `box` per module the hops run through (every series item, or `--flow`'s), members = its hops, an arrow per call between modules (queue amber, HTTP green, spawn red); `crossings` lists every queue, HTTP and spawn hand-off and every eval hop's drive into production, with evidence |
|
|
573
|
+
| `sequence` | `{host: "svg", primitive: "sequence", params: {participants, messages}}` for `SceneCore.primitives.sequence`: the caller (an HTTP entry's route, a queue key, a command), then each module in order; one message per hop from the module that calls it (`kind` call / http / queue / spawn; `via` counts the unnarrated calls between) |
|
|
574
|
+
| `exercises` | `{host: "diagram", exercises, config}` for an eval or dev flow: its hops as the harness lane, each production flow it drives as one box with `see video N`, and its `drive` between (`kind`, `to`, `evidence`) |
|
|
575
|
+
|
|
576
|
+
The video number comes from `plan.json` beside the validated flows when it was planned from that exact file
|
|
577
|
+
(`videoFrom: "plan.json"`), else from the planner at the default cap (`videoFrom: "estimate …"`).
|