diff --git a/.github/workflows/compiler-execution.yml b/.github/workflows/compiler-execution.yml index c5b9ba2..423e412 100644 --- a/.github/workflows/compiler-execution.yml +++ b/.github/workflows/compiler-execution.yml @@ -21,7 +21,7 @@ jobs: - runtime: release-0.4.2 worker-version: '0.4.2' - runtime: current - worker-version: '0.4.3' + worker-version: '0.4.4' steps: - name: Check out reviewed validation scenarios uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 1d37a84..61721d3 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -34,7 +34,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 with: - python-version: '3.12' + python-version: '3.14.10' - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 with: node-version: '24' diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index 976a599..f84a115 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -66,7 +66,7 @@ jobs: persist-credentials: false - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 with: - python-version: '3.12' + python-version: '3.14.10' - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 with: node-version: '24' diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3ca40b4..426c3f3 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -93,7 +93,7 @@ jobs: with: python-version: '3.12' - run: pip install -e '.[dev]' - - run: docker build -t code-flow-worker:0.4.3 runner + - run: docker build -t code-flow-worker:0.4.4 runner - run: python -m scripts.worker_checks - run: python -m scripts.private_worker_checks - run: python -m scripts.push_analysis diff --git a/.gitignore b/.gitignore index 82c3c42..0753ab4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ test-results/ *.sqlite-shm *.sqlite-wal *.cfsvault + +# Local acceptance/developer state remains private and outside release inputs. +.runtime/ diff --git a/README.md b/README.md index 500f9c9..d5faed9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An independently written successor to a group AI-in-SDLC capstone: a local-first development workbench for isolated polyglot checks, configurable AI proposals, and replayable revision evidence. -**Source version 0.4.3: exact instrumented C# exits.** The worker now separates the program's real exit status from the coverage collector, so approved nonzero returns can have fully measured coverage. Read the [fix and validation](docs/csharp-coverage.md). Create an account, unlock your encrypted source/history workspace, and use the same interface in the desktop app or your browser. Filter diagnostics by file or unused-code category, select an exact line, inspect its read-only evidence snapshot, and open the editable draft only when its SHA-256 still matches. No public backend, shared login, analytics, ads or cloud AI calls are enabled by default. By [Jason Stys](https://github.com/JasonStys); [contact via LinkedIn](https://www.linkedin.com/in/jason-stys-31b30b351/). +**Source version 0.4.4: reviewed desktop runtimes and exact instrumented C# exits.** The desktop service uses Python 3.14.10 and Electron 44.11.0; its frozen entry refuses an unreviewed interpreter before opening accounts. The separately isolated worker uses a digest-pinned Python 3.12.16 base. The 0.4.3 C# fix is retained: the worker separates the program's real exit status from the coverage collector, so approved nonzero returns can have fully measured coverage. Read the [release notes](docs/releases/v0.4.4.md) and [C# fix and validation](docs/csharp-coverage.md). Create an account, unlock your encrypted source/history workspace, and use the same interface in the desktop app or your browser. Filter diagnostics by file or unused-code category, select an exact line, inspect its read-only evidence snapshot, and open the editable draft only when its SHA-256 still matches. No public backend, shared login, analytics, ads or cloud AI calls are enabled by default. By [Jason Stys](https://github.com/JasonStys); [contact via LinkedIn](https://www.linkedin.com/in/jason-stys-31b30b351/). Download OS-specific installers/portable packages from [Releases](https://github.com/JasonStys/code-flow-studio/releases). Read [private-account operation](docs/private-accounts.md), [current validation](docs/validation-polyglot.md), [historical Python-location validation](docs/validation-locations.md) and [security boundaries](docs/security.md). Packages are unsigned. This is a local development tool, not a public hostile-code SaaS sandbox or an autonomous correctness oracle. @@ -16,7 +16,7 @@ Download OS-specific installers/portable packages from [Releases](https://github ## Hosted compiler acceptance -The [realistic compiler suite](docs/compiler-execution.md) covers all twelve executable adapters with 72 independently authored input/output cases, deliberate compiler/runtime/wrong-answer controls, exact unused/uncovered anchors, and encrypted private-report replay. Its historical lane preserves the published 0.4.2 baseline, including its C# limitation; the current 0.4.3 lane requires the corrected target/collector protocol and fully measured approved C# cases. Read the [0.4.2 observations](docs/reports/hosted-compilers-2026-11-10.md) and [0.4.3 validation](docs/reports/csharp-coverage-2026-11-10.md). Published 0.4.2 packages are unchanged; this source fix neither installs an app nor alters Docker on an end user's PC. +The [realistic compiler suite](docs/compiler-execution.md) covers all twelve executable adapters with 72 independently authored input/output cases, deliberate compiler/runtime/wrong-answer controls, exact unused/uncovered anchors, and encrypted private-report replay. Its historical lane preserves the published 0.4.2 baseline, including its C# limitation; the current 0.4.4 lane retains the corrected target/collector protocol and fully measured approved C# cases. Read the [0.4.2 observations](docs/reports/hosted-compilers-2026-11-10.md) and [0.4.3 C# validation](docs/reports/csharp-coverage-2026-11-10.md). Those are dated observations; a new release must pass its own exact-source compiler and native gates. Published historical packages are unchanged. Building or installing this desktop application does not install, start or repair Docker on an end user's PC. ## Features and limits @@ -42,9 +42,10 @@ The [source inspection workflow](docs/source-navigation.md) explains stale/legac ## Run and install -Version 0.4.2 retains bounded worker-report reads, verified source review and safe +Version 0.4.4 retains bounded worker-report reads, verified source review and safe local shutdown, and corrects the final draft-verification job's required metadata -visibility. Its [release notes](docs/releases/v0.4.2.md) +visibility. The [current runtime validation](docs/reports/desktop-runtimes-2026-11-10.md), +[historical 0.4.2 release notes](docs/releases/v0.4.2.md) and [full-main finding/validation report](docs/reports/codeql-main-fixes-2026-11-09.md) describe the bounded worker reader and the fresh native/download checks required before release promotion. The 0.4.1 draft passed native builds but its final diff --git a/docs/code-index.md b/docs/code-index.md index 7b864f9..b97aafa 100644 --- a/docs/code-index.md +++ b/docs/code-index.md @@ -2224,203 +2224,203 @@ Generated declaration locations. Python uses AST; JS/TS uses declaration-line ma | Kind | Declaration | Line | |---|---|---| -| variable | `CSP` | 38 | -| variable | `HISTORY_STOP_BYTES` | 39 | -| class | `Boundary` | 42 | -| function | `__init__(self, app, manager, port)` | 45 | -| parameter | `app` | 45 | -| parameter | `manager` | 45 | -| parameter | `port` | 45 | -| parameter | `self` | 45 | -| function | `__call__(self, scope, receive, send)` | 51 | -| parameter | `receive` | 51 | -| parameter | `scope` | 51 | -| parameter | `self` | 51 | -| parameter | `send` | 51 | -| variable | `raw` | 55 | -| variable | `headers` | 56 | -| variable | `k` | 56 | -| variable | `v` | 56 | -| function | `guarded(message)` | 58 | -| parameter | `message` | 58 | -| variable | `k` | 63 | -| variable | `v` | 63 | -| function | `deny(status, detail)` | 79 | -| parameter | `detail` | 79 | -| parameter | `status` | 79 | -| variable | `_` | 85 | -| variable | `k` | 85 | -| variable | `cookies` | 103 | -| variable | `token` | 108 | -| variable | `account` | 109 | -| variable | `chunks` | 117 | -| variable | `length` | 117 | -| variable | `message` | 119 | -| variable | `data` | 122 | -| variable | `length` | 123 | -| variable | `delivered` | 129 | -| function | `replay()` | 131 | -| variable | `delivered` | 135 | -| class | `Closed` | 142 | -| variable | `model_config` | 145 | -| class | `Credentials` | 148 | -| variable | `alias` | 151 | -| variable | `password` | 152 | -| class | `Recovery` | 155 | -| variable | `recovery` | 158 | -| class | `Unlock` | 161 | -| variable | `key` | 164 | -| variable | `salt` | 165 | -| class | `Activity` | 168 | -| variable | `idle_seconds` | 171 | -| class | `Delete` | 174 | -| variable | `password` | 177 | -| variable | `confirm` | 178 | -| class | `Provider` | 181 | -| variable | `id` | 184 | -| variable | `kind` | 185 | -| variable | `url` | 186 | -| variable | `model` | 187 | -| variable | `key` | 188 | -| variable | `retain_key` | 189 | -| variable | `token_parameter` | 190 | -| variable | `temperature` | 191 | -| class | `Settings` | 194 | -| variable | `providers` | 197 | -| variable | `routes` | 198 | -| function | `coherent(self)` | 203 | -| parameter | `self` | 203 | -| variable | `ids` | 205 | -| variable | `p` | 205 | -| variable | `value` | 206 | -| variable | `p` | 209 | -| function | `public_settings(manager)` | 215 | -| parameter | `manager` | 215 | -| variable | `keys` | 217 | -| variable | `settings` | 217 | -| variable | `data` | 218 | -| variable | `p` | 220 | -| class | `Proxy` | 228 | -| function | `__init__(self, manager)` | 231 | -| parameter | `manager` | 231 | -| parameter | `self` | 231 | -| function | `__call__(self, scope, receive, send)` | 235 | -| parameter | `receive` | 235 | -| parameter | `scope` | 235 | -| parameter | `self` | 235 | -| parameter | `send` | 235 | -| variable | `manager` | 239 | -| variable | `app` | 242 | -| variable | `path` | 247 | -| variable | `db` | 253 | -| variable | `count` | 254 | -| variable | `headers` | 266 | -| variable | `forwarded` | 275 | -| variable | `k` | 276 | -| variable | `v` | 276 | -| function | `create_private_app(root, port, ui, launcher_token, nonce)` | 286 | -| parameter | `launcher_token` | 287 | -| parameter | `nonce` | 287 | -| parameter | `port` | 287 | -| parameter | `root` | 287 | -| parameter | `ui` | 287 | -| variable | `manager` | 290 | -| function | `lifespan(app)` | 294 | -| parameter | `app` | 294 | -| function | `idle()` | 297 | -| variable | `task` | 311 | -| variable | `app` | 325 | -| function | `invalid(request, error)` | 337 | -| parameter | `error` | 337 | -| parameter | `request` | 337 | -| function | `bad_value(request, error)` | 342 | -| parameter | `error` | 342 | -| parameter | `request` | 342 | -| function | `bad_disk(request, error)` | 347 | -| parameter | `error` | 347 | -| parameter | `request` | 347 | -| function | `identify(request)` | 353 | -| parameter | `request` | 353 | -| function | `signed(owner, data)` | 357 | -| parameter | `data` | 357 | -| parameter | `owner` | 357 | -| variable | `response` | 359 | -| function | `auth_limit(alias)` | 370 | -| parameter | `alias` | 370 | -| function | `health()` | 378 | -| function | `shutdown(request)` | 389 | -| parameter | `request` | 389 | -| variable | `callback` | 395 | -| function | `register(data)` | 402 | -| parameter | `data` | 402 | -| variable | `owner` | 406 | -| variable | `recovery` | 406 | -| function | `login(data)` | 412 | -| parameter | `data` | 412 | -| variable | `owner` | 415 | -| function | `reset(data)` | 421 | -| parameter | `data` | 421 | -| variable | `db` | 425 | -| variable | `row` | 426 | -| function | `commit_reset()` | 431 | -| variable | `fresh` | 436 | -| function | `account(request)` | 442 | -| parameter | `request` | 442 | -| variable | `owner` | 444 | -| variable | `session` | 444 | -| function | `unlock(request, data)` | 452 | -| parameter | `data` | 452 | -| parameter | `request` | 452 | -| variable | `owner` | 454 | -| variable | `session` | 454 | -| function | `activity(request, data)` | 463 | -| parameter | `data` | 463 | -| parameter | `request` | 463 | -| function | `save(request)` | 471 | -| parameter | `request` | 471 | -| function | `lock(request)` | 478 | -| parameter | `request` | 478 | -| variable | `owner` | 481 | -| variable | `session` | 481 | -| function | `logout(request)` | 486 | -| parameter | `request` | 486 | -| variable | `owner` | 489 | -| variable | `session` | 489 | -| function | `commit_logout()` | 490 | -| variable | `response` | 497 | -| function | `delete(request, data)` | 502 | -| parameter | `data` | 502 | -| parameter | `request` | 502 | -| variable | `_` | 505 | -| variable | `owner` | 505 | -| variable | `db` | 506 | -| variable | `row` | 507 | -| function | `commit_delete()` | 510 | -| variable | `response` | 517 | -| function | `backup(request)` | 522 | -| parameter | `request` | 522 | -| variable | `_` | 524 | -| variable | `owner` | 524 | -| variable | `path` | 525 | -| function | `get_providers(request)` | 533 | -| parameter | `request` | 533 | -| function | `providers(request, data)` | 540 | -| parameter | `data` | 540 | -| parameter | `request` | 540 | -| variable | `keys` | 544 | -| variable | `old` | 544 | -| variable | `rows` | 545 | -| variable | `provider` | 546 | -| variable | `row` | 547 | -| variable | `p` | 549 | -| variable | `previous` | 549 | -| variable | `config` | 554 | -| variable | `router` | 555 | -| variable | `k` | 557 | -| variable | `p` | 557 | -| variable | `v` | 557 | -| variable | `p` | 560 | -| function | `commit_providers()` | 562 | +| variable | `CSP` | 39 | +| variable | `HISTORY_STOP_BYTES` | 40 | +| class | `Boundary` | 43 | +| function | `__init__(self, app, manager, port)` | 46 | +| parameter | `app` | 46 | +| parameter | `manager` | 46 | +| parameter | `port` | 46 | +| parameter | `self` | 46 | +| function | `__call__(self, scope, receive, send)` | 52 | +| parameter | `receive` | 52 | +| parameter | `scope` | 52 | +| parameter | `self` | 52 | +| parameter | `send` | 52 | +| variable | `raw` | 56 | +| variable | `headers` | 57 | +| variable | `k` | 57 | +| variable | `v` | 57 | +| function | `guarded(message)` | 59 | +| parameter | `message` | 59 | +| variable | `k` | 64 | +| variable | `v` | 64 | +| function | `deny(status, detail)` | 80 | +| parameter | `detail` | 80 | +| parameter | `status` | 80 | +| variable | `_` | 86 | +| variable | `k` | 86 | +| variable | `cookies` | 104 | +| variable | `token` | 109 | +| variable | `account` | 110 | +| variable | `chunks` | 118 | +| variable | `length` | 118 | +| variable | `message` | 120 | +| variable | `data` | 123 | +| variable | `length` | 124 | +| variable | `delivered` | 130 | +| function | `replay()` | 132 | +| variable | `delivered` | 136 | +| class | `Closed` | 143 | +| variable | `model_config` | 146 | +| class | `Credentials` | 149 | +| variable | `alias` | 152 | +| variable | `password` | 153 | +| class | `Recovery` | 156 | +| variable | `recovery` | 159 | +| class | `Unlock` | 162 | +| variable | `key` | 165 | +| variable | `salt` | 166 | +| class | `Activity` | 169 | +| variable | `idle_seconds` | 172 | +| class | `Delete` | 175 | +| variable | `password` | 178 | +| variable | `confirm` | 179 | +| class | `Provider` | 182 | +| variable | `id` | 185 | +| variable | `kind` | 186 | +| variable | `url` | 187 | +| variable | `model` | 188 | +| variable | `key` | 189 | +| variable | `retain_key` | 190 | +| variable | `token_parameter` | 191 | +| variable | `temperature` | 192 | +| class | `Settings` | 195 | +| variable | `providers` | 198 | +| variable | `routes` | 199 | +| function | `coherent(self)` | 204 | +| parameter | `self` | 204 | +| variable | `ids` | 206 | +| variable | `p` | 206 | +| variable | `value` | 207 | +| variable | `p` | 210 | +| function | `public_settings(manager)` | 216 | +| parameter | `manager` | 216 | +| variable | `keys` | 218 | +| variable | `settings` | 218 | +| variable | `data` | 219 | +| variable | `p` | 221 | +| class | `Proxy` | 229 | +| function | `__init__(self, manager)` | 232 | +| parameter | `manager` | 232 | +| parameter | `self` | 232 | +| function | `__call__(self, scope, receive, send)` | 236 | +| parameter | `receive` | 236 | +| parameter | `scope` | 236 | +| parameter | `self` | 236 | +| parameter | `send` | 236 | +| variable | `manager` | 240 | +| variable | `app` | 243 | +| variable | `path` | 248 | +| variable | `db` | 254 | +| variable | `count` | 255 | +| variable | `headers` | 267 | +| variable | `forwarded` | 276 | +| variable | `k` | 277 | +| variable | `v` | 277 | +| function | `create_private_app(root, port, ui, launcher_token, nonce)` | 287 | +| parameter | `launcher_token` | 288 | +| parameter | `nonce` | 288 | +| parameter | `port` | 288 | +| parameter | `root` | 288 | +| parameter | `ui` | 288 | +| variable | `manager` | 291 | +| function | `lifespan(app)` | 295 | +| parameter | `app` | 295 | +| function | `idle()` | 298 | +| variable | `task` | 312 | +| variable | `app` | 326 | +| function | `invalid(request, error)` | 338 | +| parameter | `error` | 338 | +| parameter | `request` | 338 | +| function | `bad_value(request, error)` | 343 | +| parameter | `error` | 343 | +| parameter | `request` | 343 | +| function | `bad_disk(request, error)` | 348 | +| parameter | `error` | 348 | +| parameter | `request` | 348 | +| function | `identify(request)` | 354 | +| parameter | `request` | 354 | +| function | `signed(owner, data)` | 358 | +| parameter | `data` | 358 | +| parameter | `owner` | 358 | +| variable | `response` | 360 | +| function | `auth_limit(alias)` | 371 | +| parameter | `alias` | 371 | +| function | `health()` | 379 | +| function | `shutdown(request)` | 391 | +| parameter | `request` | 391 | +| variable | `callback` | 397 | +| function | `register(data)` | 404 | +| parameter | `data` | 404 | +| variable | `owner` | 408 | +| variable | `recovery` | 408 | +| function | `login(data)` | 414 | +| parameter | `data` | 414 | +| variable | `owner` | 417 | +| function | `reset(data)` | 423 | +| parameter | `data` | 423 | +| variable | `db` | 427 | +| variable | `row` | 428 | +| function | `commit_reset()` | 433 | +| variable | `fresh` | 438 | +| function | `account(request)` | 444 | +| parameter | `request` | 444 | +| variable | `owner` | 446 | +| variable | `session` | 446 | +| function | `unlock(request, data)` | 454 | +| parameter | `data` | 454 | +| parameter | `request` | 454 | +| variable | `owner` | 456 | +| variable | `session` | 456 | +| function | `activity(request, data)` | 465 | +| parameter | `data` | 465 | +| parameter | `request` | 465 | +| function | `save(request)` | 473 | +| parameter | `request` | 473 | +| function | `lock(request)` | 480 | +| parameter | `request` | 480 | +| variable | `owner` | 483 | +| variable | `session` | 483 | +| function | `logout(request)` | 488 | +| parameter | `request` | 488 | +| variable | `owner` | 491 | +| variable | `session` | 491 | +| function | `commit_logout()` | 492 | +| variable | `response` | 499 | +| function | `delete(request, data)` | 504 | +| parameter | `data` | 504 | +| parameter | `request` | 504 | +| variable | `_` | 507 | +| variable | `owner` | 507 | +| variable | `db` | 508 | +| variable | `row` | 509 | +| function | `commit_delete()` | 512 | +| variable | `response` | 519 | +| function | `backup(request)` | 524 | +| parameter | `request` | 524 | +| variable | `_` | 526 | +| variable | `owner` | 526 | +| variable | `path` | 527 | +| function | `get_providers(request)` | 535 | +| parameter | `request` | 535 | +| function | `providers(request, data)` | 542 | +| parameter | `data` | 542 | +| parameter | `request` | 542 | +| variable | `keys` | 546 | +| variable | `old` | 546 | +| variable | `rows` | 547 | +| variable | `provider` | 548 | +| variable | `row` | 549 | +| variable | `p` | 551 | +| variable | `previous` | 551 | +| variable | `config` | 556 | +| variable | `router` | 557 | +| variable | `k` | 559 | +| variable | `p` | 559 | +| variable | `v` | 559 | +| variable | `p` | 562 | +| function | `commit_providers()` | 564 | ## studio/private_cli.py @@ -2893,22 +2893,23 @@ Generated declaration locations. Python uses AST; JS/TS uses declaration-line ma | variable | `route` | 141 | | variable | `browserErrors` | 153 | | variable | `health` | 157 | -| variable | `state` | 165 | -| variable | `directory` | 196 | -| variable | `serviceLog` | 199 | -| variable | `statuses` | 200 | -| variable | `sourceInspector` | 223 | -| variable | `lifecycle` | 273 | -| variable | `records` | 274 | -| variable | `launcher` | 281 | -| variable | `line` | 284 | -| variable | `service` | 289 | -| variable | `line` | 290 | -| variable | `diagnostic` | 297 | -| variable | `records` | 301 | -| variable | `stage` | 302 | -| variable | `record` | 314 | -| variable | `pid` | 327 | +| variable | `healthData` | 158 | +| variable | `state` | 167 | +| variable | `directory` | 198 | +| variable | `serviceLog` | 201 | +| variable | `statuses` | 202 | +| variable | `sourceInspector` | 225 | +| variable | `lifecycle` | 275 | +| variable | `records` | 276 | +| variable | `launcher` | 283 | +| variable | `line` | 286 | +| variable | `service` | 291 | +| variable | `line` | 292 | +| variable | `diagnostic` | 299 | +| variable | `records` | 303 | +| variable | `stage` | 304 | +| variable | `record` | 316 | +| variable | `pid` | 329 | ## tests/electron-readiness.test.cjs @@ -3598,6 +3599,22 @@ Generated declaration locations. Python uses AST; JS/TS uses declaration-line ma | parameter | `argv` | 98 | | parameter | `kwargs` | 98 | +## tests/test_frozen_runtime.py + +| Kind | Declaration | Line | +|---|---|---| +| function | `test_unreviewed_frozen_runtime_refuses_before_service_import(monkeypatch, version)` | 24 | +| parameter | `monkeypatch` | 24 | +| parameter | `version` | 24 | +| variable | `events` | 26 | +| variable | `service` | 30 | +| function | `test_reviewed_frozen_and_supported_source_dispatch(monkeypatch, frozen, version)` | 39 | +| parameter | `frozen` | 39 | +| parameter | `monkeypatch` | 39 | +| parameter | `version` | 39 | +| variable | `events` | 41 | +| variable | `service` | 45 | + ## tests/test_locations.py | Kind | Declaration | Line | diff --git a/docs/compiler-execution.md b/docs/compiler-execution.md index 13419f2..aa8c68b 100644 --- a/docs/compiler-execution.md +++ b/docs/compiler-execution.md @@ -119,7 +119,7 @@ The [observed report snapshots](../tests/fixtures/compiler-reports-042.json) ret these actual first-run results, including their original failed-acceptance labels; replay unit tests are not a new compiler run. -Current 0.4.3 workers use a [separate exact target-exit bridge](csharp-coverage.md) +Current 0.4.4 workers retain the 0.4.3 [exact target-exit bridge](csharp-coverage.md) and must report measured completion for approved nonzero exits. The old partial allowance is available only to the independently pinned 0.4.2 baseline verifier; it cannot make a current worker pass acceptance. diff --git a/docs/releases/v0.4.4.md b/docs/releases/v0.4.4.md new file mode 100644 index 0000000..a3d68eb --- /dev/null +++ b/docs/releases/v0.4.4.md @@ -0,0 +1,26 @@ +# Code Flow Studio 0.4.4 — reviewed desktop runtimes + +By Jason Stys. This maintenance release retains the 0.4.3 C# coverage correction, +exact source locations, unused-code checks and isolated compiler workflows. It +updates the desktop service to Python 3.14.10 and the shell to Electron 44.11.0. +Frozen services refuse a different Python build before opening private account +storage; health and native acceptance report the interpreter actually running. +Source development continues to support the Python versions in `pyproject.toml`. + +The worker remains on its separately tested Python 3.12 compatibility line and +uses the Python 3.12.16 Bookworm image pinned by digest. Product and worker-image +identities advance together to 0.4.4. Build the matching worker in a supported +isolated environment; compilation remains blocked when that worker is unavailable. +There is no host-execution fallback. Docker on the development PC is unchanged. + +Account and encrypted-history formats do not change. Quit the existing app cleanly +and back up its private data before upgrading. The installer preserves data outside +the installation directory and retains the existing application identity. Package +checks, source checks and independently downloaded installer acceptance must pass +before publication. Packages remain unsigned; no signing or notarization is claimed. + +Python 3.14.10 is the expedited security release described in the +[official release notice](https://www.python.org/downloads/release/python-31410/). +Historical 0.4.3 compiler measurements and installer reports remain dated evidence, +not measurements of this release. The current validation report records exact +source commits, actual runtime versions, results and remaining environment limits. diff --git a/docs/reports/desktop-runtimes-2026-11-10.md b/docs/reports/desktop-runtimes-2026-11-10.md new file mode 100644 index 0000000..dc6a41b --- /dev/null +++ b/docs/reports/desktop-runtimes-2026-11-10.md @@ -0,0 +1,54 @@ +# Desktop runtime maintenance — 10 November 2026 + +Author: Jason Stys. Scope: the 0.4.4 source candidate and a locally built unsigned +Windows application image. Published installer and hosted acceptance are separate +gates; this report does not label an uninstalled candidate as the installed app. + +## Changes and reason + +The existing 0.4.3 one-file service contains Python 3.12.10. That historical Windows +installer line no longer receives new official binary installers. The new desktop +build uses the official full Python 3.14.10 SDK and Electron 44.11.0, preserving +the same account/vault formats and application identity. The frozen entry refuses +another interpreter before private-service dispatch. `/health` reports the actual +interpreter, and native acceptance requires that value rather than trusting only +a package label. The compiler worker stays on its compatibility line with a +digest-pinned Python 3.12.16 Bookworm base and a matching 0.4.4 worker identity. + +The 0.4.3 C# target-exit bridge and located-diagnostic contracts are retained. +The existing local `.runtime` directory is ignored; no contents are packaged, +deleted, inspected for account data or added to source control. + +## Observed candidate validation + +| Check | Observed result | +| --- | --- | +| Complete source suite on Python 3.14.10 | 422 passed, two Windows platform-specific skips and 28 subtests passed | +| Existing controller coverage scope | 85.13%; unchanged 80% floor, `studio` plus `training.dataset` | +| Frozen-runtime regressions | Unexpected interpreter versions refuse before account-service dispatch; reviewed frozen and supported source modes dispatch correctly | +| Ruff and exact source maps | Passed; 98 source maps checked | +| TypeScript and production UI build | Passed | +| Electron readiness/lifecycle regressions | Passed | +| Actual packaged Windows journey | Passed: authenticate, encrypted history, sandboxed offline editor, source inspector and test catalog; service drained and exited normally | +| Dependency advisories | npm: zero known findings; pip-audit: no known findings in auditable build dependencies; this unpublished local project is not a PyPI package | +| Docker on this PC | Unchanged; no local compiler container was started or built | + +The native pilot used a dirty, explicitly labeled development candidate based on +`3825eecda6f991a8ddbd21873bbcfc7a6734a48b`. It is evidence about the actual built +candidate, not clean release provenance. Exact-source hosted packages and compiler +acceptance must pass again before release promotion. Independent download, +installer-upgrade, private-data preservation and existing-shortcut checks remain +required before replacing the installed 0.4.3 application. + +## Test environment diagnosis + +Initial source-test attempts stalled before application dispatch in Python's +internal asyncio loopback socket pair inside the restricted tool sandbox. A +30-second thread traceback identified `socket.accept` during event-loop setup. +The same six API tests passed in normal local context, followed by the full suite +above. The stalled owned test processes were stopped after that diagnosis; their +logs remain private. No product workaround or weakened test was introduced. + +Runtime sources: [Python 3.14.10 release notice](https://www.python.org/downloads/release/python-31410/), +[official Windows distribution manifest](https://www.python.org/ftp/python/3.14.10/windows-3.14.10.json). +Dependency scans are dated advisory observations, not guarantees of future safety. diff --git a/package-lock.json b/package-lock.json index 5dc42db..6b8e77e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-flow-studio", - "version": "0.4.3", + "version": "0.4.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-flow-studio", - "version": "0.4.3", + "version": "0.4.4", "license": "MIT", "dependencies": { "@monaco-editor/react": "4.7.0", @@ -20,7 +20,7 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "6.1.1", - "electron": "44.10.2", + "electron": "44.11.0", "electron-builder": "26.15.3", "typescript": "5.9.3", "vite": "8.3.5" @@ -1828,9 +1828,9 @@ } }, "node_modules/electron": { - "version": "44.10.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-44.10.2.tgz", - "integrity": "sha512-Bz+GV+9Xj++3RW95NYfzp5Wgivf61BgWTPBxYTOUETytVB1uLUAOdSM39Dn/V3PGYeGTE6LYeCTR5SCiu3BR2A==", + "version": "44.11.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-44.11.0.tgz", + "integrity": "sha512-CQxe5prFylBroRmKQcWJtM6kiUE3sJP2OrSiPZNJxcZRLI2OKlkbcRfooqrEzPV8dMjTFqvkswHIfwiEhVGlEg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 48f7639..9313b61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-flow-studio", - "version": "0.4.3", + "version": "0.4.4", "description": "Isolated polyglot testing and evidence-first AI development studio", "author": "Jason Stys", "license": "MIT", @@ -38,7 +38,7 @@ "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "6.1.1", - "electron": "44.10.2", + "electron": "44.11.0", "electron-builder": "26.15.3", "typescript": "5.9.3", "vite": "8.3.5" diff --git a/pyproject.toml b/pyproject.toml index d5b80f2..61caf7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "code-flow-studio" -version = "0.4.3" +version = "0.4.4" description = "Evidence-first, isolated polyglot testing and AI development workbench" requires-python = ">=3.12" dependencies = ["fastapi==0.142.1", "uvicorn==0.38.0", "httpx==0.28.1", "pydantic==2.13.2", "cryptography==50.0.2", "psutil==7.2.2", "certifi==2026.10.19"] diff --git a/runner/Dockerfile b/runner/Dockerfile index 52034f9..5780ff5 100644 --- a/runner/Dockerfile +++ b/runner/Dockerfile @@ -4,7 +4,7 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim AS dotnet FROM node:24-bookworm-slim AS node FROM rust:1.99.1-slim-bookworm AS rust RUN rustup component add llvm-tools-preview -FROM python:3.12.12-slim-bookworm +FROM python:3.12.16-slim-bookworm@sha256:935c3c5b12357a7e10caa9c1e6ca5692344d97cf229832ef584be2cec9fc03df COPY --from=dotnet /usr/share/dotnet /usr/share/dotnet COPY --from=node /usr/local/bin/node /usr/local/bin/node COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules diff --git a/runner/tools/package-lock.json b/runner/tools/package-lock.json index 0ce0115..f11e667 100644 --- a/runner/tools/package-lock.json +++ b/runner/tools/package-lock.json @@ -1,12 +1,12 @@ { "name": "code-flow-worker-analyzers", - "version": "0.4.3", + "version": "0.4.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-flow-worker-analyzers", - "version": "0.4.3", + "version": "0.4.4", "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "8.72.0", diff --git a/runner/tools/package.json b/runner/tools/package.json index 24a5bdc..b33c104 100644 --- a/runner/tools/package.json +++ b/runner/tools/package.json @@ -1,6 +1,6 @@ { "name": "code-flow-worker-analyzers", - "version": "0.4.3", + "version": "0.4.4", "private": true, "description": "Fixed, offline worker analyzers; never load target configuration or plugins", "license": "MIT", diff --git a/scripts/compiler_acceptance.py b/scripts/compiler_acceptance.py index 74f1ff1..c4ee725 100644 --- a/scripts/compiler_acceptance.py +++ b/scripts/compiler_acceptance.py @@ -593,7 +593,7 @@ def provenance(runtime: Path, expected_source: str, lane: str) -> dict: ).strip() require(not dirty, "Runtime tracked source is dirty") require( - studio.__version__ == ("0.4.2" if lane == "release-0.4.2" else "0.4.3") + studio.__version__ == ("0.4.2" if lane == "release-0.4.2" else "0.4.4") and execution.IMAGE == f"code-flow-worker:{studio.__version__}", "Wrong product/worker version", ) diff --git a/scripts/frozen_entry.py b/scripts/frozen_entry.py index 5f75f33..7cb4c0d 100644 --- a/scripts/frozen_entry.py +++ b/scripts/frozen_entry.py @@ -7,10 +7,13 @@ Symbol locations: docs/code-index.md. Calls the same audited CLI as source mode. """ +import sys from multiprocessing import freeze_support -from studio.private_cli import main - if __name__ == "__main__": freeze_support() + if getattr(sys, "frozen", False) and sys.version_info[:3] != (3, 14, 10): + raise SystemExit("This application requires its reviewed Python 3.14.10 runtime.") + from studio.private_cli import main + main() diff --git a/studio/__init__.py b/studio/__init__.py index 689cf18..78aab18 100644 --- a/studio/__init__.py +++ b/studio/__init__.py @@ -6,4 +6,4 @@ """Code Flow Studio orchestration package. Symbol locations: docs/code-index.md.""" -__version__ = "0.4.3" +__version__ = "0.4.4" diff --git a/studio/execution.py b/studio/execution.py index 2f5798e..ec91690 100644 --- a/studio/execution.py +++ b/studio/execution.py @@ -25,7 +25,7 @@ from studio.locations import tool_locations from studio.models import Project -IMAGE = "code-flow-worker:0.4.3" +IMAGE = "code-flow-worker:0.4.4" DOCKER = os.environ.get("STUDIO_DOCKER", shutil.which("docker") or "docker") diff --git a/studio/private_api.py b/studio/private_api.py index 98c1495..5c20817 100644 --- a/studio/private_api.py +++ b/studio/private_api.py @@ -1,10 +1,10 @@ # # Generated definition map; @L is the exact declaration line. # Function/class docs explain behavior; variables/parameters are scoped inputs, outputs or state. -# classes: Boundary@L42; Closed@L142; Credentials@L148; Recovery@L155; Unlock@L161; Activity@L168; Delete@L174; Provider@L181; Settings@L194; Proxy@L228 -# functions: __init__(self, app, manager, port)@L45; __call__(self, scope, receive, send)@L51; guarded(message)@L58; deny(status, detail)@L79; replay()@L131; coherent(self)@L203; public_settings(manager)@L215; __init__(self, manager)@L231; __call__(self, scope, receive, send)@L235; create_private_app(root, port, ui, launcher_token, nonce)@L286; lifespan(app)@L294; idle()@L297; invalid(request, error)@L337; bad_value(request, error)@L342; bad_disk(request, error)@L347; identify(request)@L353; signed(owner, data)@L357; auth_limit(alias)@L370; health()@L378; shutdown(request)@L389; register(data)@L402; login(data)@L412; reset(data)@L421; commit_reset()@L431; account(request)@L442; unlock(request, data)@L452; activity(request, data)@L463; save(request)@L471; lock(request)@L478; logout(request)@L486; commit_logout()@L490; delete(request, data)@L502; commit_delete()@L510; backup(request)@L522; get_providers(request)@L533; providers(request, data)@L540; commit_providers()@L562 -# variables: CSP@L38; HISTORY_STOP_BYTES@L39; raw@L55; headers@L56; k@L56; v@L56; k@L63; v@L63; _@L85; k@L85; cookies@L103; token@L108; account@L109; chunks@L117; length@L117; message@L119; data@L122; length@L123; delivered@L129; delivered@L135; model_config@L145; alias@L151; password@L152; recovery@L158; key@L164; salt@L165; idle_seconds@L171; password@L177; confirm@L178; id@L184; kind@L185; url@L186; model@L187; key@L188; retain_key@L189; token_parameter@L190; temperature@L191; providers@L197; routes@L198; ids@L205; p@L205; value@L206; p@L209; keys@L217; settings@L217; data@L218; p@L220; manager@L239; app@L242; path@L247; db@L253; count@L254; headers@L266; forwarded@L275; k@L276; v@L276; manager@L290; task@L311; app@L325; response@L359; callback@L395; owner@L406; recovery@L406; owner@L415; db@L425; row@L426; fresh@L436; owner@L444; session@L444; owner@L454; session@L454; owner@L481; session@L481; owner@L489; session@L489; response@L497; _@L505; owner@L505; db@L506; row@L507; response@L517; _@L524; owner@L524; path@L525; keys@L544; old@L544; rows@L545; provider@L546; row@L547; p@L549; previous@L549; config@L554; router@L555; k@L557; p@L557; v@L557; p@L560 -# parameters: app@L45; manager@L45; port@L45; self@L45; receive@L51; scope@L51; self@L51; send@L51; message@L58; detail@L79; status@L79; self@L203; manager@L215; manager@L231; self@L231; receive@L235; scope@L235; self@L235; send@L235; launcher_token@L287; nonce@L287; port@L287; root@L287; ui@L287; app@L294; error@L337; request@L337; error@L342; request@L342; error@L347; request@L347; request@L353; data@L357; owner@L357; alias@L370; request@L389; data@L402; data@L412; data@L421; request@L442; data@L452; request@L452; data@L463; request@L463; request@L471; request@L478; request@L486; data@L502; request@L502; request@L522; request@L533; data@L540; request@L540 +# classes: Boundary@L43; Closed@L143; Credentials@L149; Recovery@L156; Unlock@L162; Activity@L169; Delete@L175; Provider@L182; Settings@L195; Proxy@L229 +# functions: __init__(self, app, manager, port)@L46; __call__(self, scope, receive, send)@L52; guarded(message)@L59; deny(status, detail)@L80; replay()@L132; coherent(self)@L204; public_settings(manager)@L216; __init__(self, manager)@L232; __call__(self, scope, receive, send)@L236; create_private_app(root, port, ui, launcher_token, nonce)@L287; lifespan(app)@L295; idle()@L298; invalid(request, error)@L338; bad_value(request, error)@L343; bad_disk(request, error)@L348; identify(request)@L354; signed(owner, data)@L358; auth_limit(alias)@L371; health()@L379; shutdown(request)@L391; register(data)@L404; login(data)@L414; reset(data)@L423; commit_reset()@L433; account(request)@L444; unlock(request, data)@L454; activity(request, data)@L465; save(request)@L473; lock(request)@L480; logout(request)@L488; commit_logout()@L492; delete(request, data)@L504; commit_delete()@L512; backup(request)@L524; get_providers(request)@L535; providers(request, data)@L542; commit_providers()@L564 +# variables: CSP@L39; HISTORY_STOP_BYTES@L40; raw@L56; headers@L57; k@L57; v@L57; k@L64; v@L64; _@L86; k@L86; cookies@L104; token@L109; account@L110; chunks@L118; length@L118; message@L120; data@L123; length@L124; delivered@L130; delivered@L136; model_config@L146; alias@L152; password@L153; recovery@L159; key@L165; salt@L166; idle_seconds@L172; password@L178; confirm@L179; id@L185; kind@L186; url@L187; model@L188; key@L189; retain_key@L190; token_parameter@L191; temperature@L192; providers@L198; routes@L199; ids@L206; p@L206; value@L207; p@L210; keys@L218; settings@L218; data@L219; p@L221; manager@L240; app@L243; path@L248; db@L254; count@L255; headers@L267; forwarded@L276; k@L277; v@L277; manager@L291; task@L312; app@L326; response@L360; callback@L397; owner@L408; recovery@L408; owner@L417; db@L427; row@L428; fresh@L438; owner@L446; session@L446; owner@L456; session@L456; owner@L483; session@L483; owner@L491; session@L491; response@L499; _@L507; owner@L507; db@L508; row@L509; response@L519; _@L526; owner@L526; path@L527; keys@L546; old@L546; rows@L547; provider@L548; row@L549; p@L551; previous@L551; config@L556; router@L557; k@L559; p@L559; v@L559; p@L562 +# parameters: app@L46; manager@L46; port@L46; self@L46; receive@L52; scope@L52; self@L52; send@L52; message@L59; detail@L80; status@L80; self@L204; manager@L216; manager@L232; self@L232; receive@L236; scope@L236; self@L236; send@L236; launcher_token@L288; nonce@L288; port@L288; root@L288; ui@L288; app@L295; error@L338; request@L338; error@L343; request@L343; error@L348; request@L348; request@L354; data@L358; owner@L358; alias@L371; request@L391; data@L404; data@L414; data@L423; request@L444; data@L454; request@L454; data@L465; request@L465; request@L473; request@L480; request@L488; data@L504; request@L504; request@L524; request@L535; data@L542; request@L542 # """Jason Stys: loopback-only real accounts, session-proven vaults, private AI settings and scoped controller. @@ -16,6 +16,7 @@ import contextlib import secrets import sqlite3 +import sys import time from contextlib import asynccontextmanager from http.cookies import CookieError, SimpleCookie @@ -381,6 +382,7 @@ def health(): "status": "ready", "mode": "private-local-accounts", "version": __version__, + "runtime_python": ".".join(map(str, sys.version_info[:3])), "author": "Jason Stys", "launcher_nonce": nonce, } diff --git a/tests/desktop.spec.ts b/tests/desktop.spec.ts index 9f31a94..069cb54 100644 --- a/tests/desktop.spec.ts +++ b/tests/desktop.spec.ts @@ -1,7 +1,7 @@ /* * Generated definition map; @L is the exact declaration line. * Function/class docs explain behavior; variables/parameters are scoped inputs, outputs or state. - * variables: { verifyElectron }@L24; { readDiagnosticLog }@L27; executable@L33; app@L36; launcherDirectory@L54; acceptedPackage@L57; resources@L64; file@L65; file@L91; metadata@L103; page@L123; accountStartedAt@L126; accountRoutes@L127; route@L129; route@L141; browserErrors@L153; health@L157; state@L165; directory@L196; serviceLog@L199; statuses@L200; sourceInspector@L223; lifecycle@L273; records@L274; launcher@L281; line@L284; service@L289; line@L290; diagnostic@L297; records@L301; stage@L302; record@L314; pid@L327 + * variables: { verifyElectron }@L24; { readDiagnosticLog }@L27; executable@L33; app@L36; launcherDirectory@L54; acceptedPackage@L57; resources@L64; file@L65; file@L91; metadata@L103; page@L123; accountStartedAt@L126; accountRoutes@L127; route@L129; route@L141; browserErrors@L153; health@L157; healthData@L158; state@L167; directory@L198; serviceLog@L201; statuses@L202; sourceInspector@L225; lifecycle@L275; records@L276; launcher@L283; line@L286; service@L291; line@L292; diagnostic@L299; records@L303; stage@L304; record@L316; pid@L329 * */ /** Verify the actual Electron shell and bundled Python service, without faking readiness. @@ -108,7 +108,7 @@ test("bundled desktop authenticates, seals history and provides a sandboxed edit ); expect(metadata.schema).toBe(1); expect(metadata.product).toBe("code-flow-studio"); - expect(metadata.version).toBe("0.4.3"); + expect(metadata.version).toBe("0.4.4"); expect(metadata.source).toMatch(/^[a-f0-9]{40}$/); expect(typeof metadata.source_dirty).toBe("boolean"); expect(metadata.worker_sha256).toBe( @@ -155,7 +155,9 @@ test("bundled desktop authenticates, seals history and provides a sandboxed edit if (browserErrors.length < 8) browserErrors.push(error.name); }); const health = await page.request.get(new URL("/health", page.url()).href); - expect((await health.json()).version).toBe("0.4.3"); + const healthData = await health.json(); + expect(healthData.version).toBe("0.4.4"); + expect(healthData.runtime_python).toBe("3.14.10"); try { console.info("NATIVE_JOURNEY_STAGE", "account:start"); await createSession(page, "native_fictional"); @@ -345,7 +347,7 @@ test("bundled desktop authenticates, seals history and provides a sandboxed edit "artifacts/native-resource-acceptance.json", JSON.stringify({ schema: 1, - version: "0.4.3", + version: "0.4.4", source: acceptedPackage.source, worker_sha256: acceptedPackage.worker_sha256, compiled_app: true, diff --git a/tests/test_frozen_runtime.py b/tests/test_frozen_runtime.py new file mode 100644 index 0000000..dc5fcb2 --- /dev/null +++ b/tests/test_frozen_runtime.py @@ -0,0 +1,49 @@ +# +# Generated definition map; @L is the exact declaration line. +# Function/class docs explain behavior; variables/parameters are scoped inputs, outputs or state. +# functions: test_unreviewed_frozen_runtime_refuses_before_service_import(monkeypatch, version)@L24; test_reviewed_frozen_and_supported_source_dispatch(monkeypatch, frozen, version)@L39 +# variables: events@L26; service@L30; events@L41; service@L45 +# parameters: monkeypatch@L24; version@L24; frozen@L39; monkeypatch@L39; version@L39 +# + +"""Jason Stys: refuse an unreviewed frozen interpreter before account-service imports. + +The synthetic module records dispatch without opening accounts or running a server. +Definition and variable locations are generated in the repository code index. +""" + +import runpy +import sys +import types +from pathlib import Path + +import pytest + + +@pytest.mark.parametrize("version", [(3, 12, 10), (3, 14, 6), (3, 14, 11)]) +def test_unreviewed_frozen_runtime_refuses_before_service_import(monkeypatch, version): + """A stale or unexpected frozen SDK must not reach private account setup.""" + events = [] + monkeypatch.setattr("multiprocessing.freeze_support", lambda: events.append("freeze")) + monkeypatch.setattr(sys, "frozen", True, raising=False) + monkeypatch.setattr(sys, "version_info", version) + service = types.ModuleType("studio.private_cli") + service.main = lambda: events.append("service") + monkeypatch.setitem(sys.modules, "studio.private_cli", service) + with pytest.raises(SystemExit, match="reviewed Python 3.14.10"): + runpy.run_path(str(Path(__file__).parents[1] / "scripts/frozen_entry.py"), run_name="__main__") + assert events == ["freeze"] + + +@pytest.mark.parametrize("frozen,version", [(True, (3, 14, 10)), (False, (3, 12, 10))]) +def test_reviewed_frozen_and_supported_source_dispatch(monkeypatch, frozen, version): + """The reviewed binary and source-development modes retain their normal CLI.""" + events = [] + monkeypatch.setattr("multiprocessing.freeze_support", lambda: events.append("freeze")) + monkeypatch.setattr(sys, "frozen", frozen, raising=False) + monkeypatch.setattr(sys, "version_info", version) + service = types.ModuleType("studio.private_cli") + service.main = lambda: events.append("service") + monkeypatch.setitem(sys.modules, "studio.private_cli", service) + runpy.run_path(str(Path(__file__).parents[1] / "scripts/frozen_entry.py"), run_name="__main__") + assert events == ["freeze", "service"] diff --git a/tests/test_release.py b/tests/test_release.py index 5871d68..0e7a82c 100644 --- a/tests/test_release.py +++ b/tests/test_release.py @@ -23,10 +23,10 @@ @pytest.mark.parametrize( "name", [ - "Code Flow Studio Setup 0.4.3.exe", - "Code Flow Studio-0.4.3-arm64.dmg", - "Code Flow Studio-0.4.3.AppImage", - "CodeFlowStudio-0.4.3-windows-x64.zip", + "Code Flow Studio Setup 0.4.4.exe", + "Code Flow Studio-0.4.4-arm64.dmg", + "Code Flow Studio-0.4.4.AppImage", + "CodeFlowStudio-0.4.4-windows-x64.zip", ], ) def test_checksum_download_filename(tmp_path, name): diff --git a/tests/test_release_gate.py b/tests/test_release_gate.py index ac2f015..dcc30b0 100644 --- a/tests/test_release_gate.py +++ b/tests/test_release_gate.py @@ -735,7 +735,7 @@ def test_dirty_source_and_publish_operation_are_refused_before_network(tmp_path, def test_actual_checked_in_product_version_and_worker_metadata(): """Exercise the production data-only coherence reader independently of prepared-context fixtures.""" - assert gate.product_version() == "0.4.3" + assert gate.product_version() == "0.4.4" def test_api_structured_body_and_fixed_namespace(monkeypatch): diff --git a/ui/App.tsx b/ui/App.tsx index 380577a..64cacae 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -439,7 +439,7 @@ export default function App({ - ENGINEERING WORKBENCH / v0.4.3 + ENGINEERING WORKBENCH / v0.4.4 {tab === "workspace" ? "Make your next iteration count."