feat(validation): auto build-gate on terminal freestyle stage (Gate 4 floor)

Closes the COMPLETED-lie where a freestyle stage passes by producing a
schema-valid artifact regardless of whether its build actually succeeded:
a verify stage could run 'npm run build', watch it fail, then self-attest a
build_verified artifact and transition to done. Tool failures never failed
the stage.

The compiler now flags the terminal stage autoBuildGate=true when no stage
declares an explicit build_expectation. Code-ness is decided at runtime, not
compile time (the declared kind is always the generic file_written; only the
written paths reveal code): runExecutionGate folds the FileWritten manifest via
sessionProducedBuildTarget() and promotes to a PROJECT build when a build
manifest (package_json/gradle_module) or an imports_resolve-bearing path was
written, then runs the real command from .correx/project.toml [commands]. A
non-clean exit fails the stage retryably; a docs-only plan is left untouched.

Live-proven (run 3, session 28812b44): gate fired on the real
'npm --prefix frontend run build', exit 2 on a bad tsconfig, RetryAttempted
instead of a false COMPLETED.

Modules green: transitions+workflow 67, kernel 60.
This commit is contained in:
2026-07-07 01:01:12 +04:00
parent 79e2c38a88
commit 597a26d551
6 changed files with 185 additions and 11 deletions
+7
View File
@@ -13,3 +13,10 @@ test-all = "./gradlew check --rerun-tasks"
test-module = "./gradlew :core:<module>:test --rerun-tasks"
context-lookup = "python scripts/ctx.py <query>"
epic-status = "bash scripts/epic-status.sh"
# Build-gate aliases (BuildExpectation → commandAlias): MODULE→typecheck, PROJECT→build, TESTS→test.
# The gate runner is whitespace-split / no-shell and runs at workspace_root (the repo), so `--prefix
# frontend` targets the Node subproject without a `cd`. `npm run build` = `tsc && vite build` (npm's
# own shell handles the &&), which fails on a dangling asset import — exactly the COMPLETED-lie hole.
typecheck = "npm --prefix frontend run build"
build = "npm --prefix frontend run build"
test = "npm --prefix frontend test"