Phase 3 keeps the generic Agent state machine unchanged. The Host owns Root delivery,
publication, recovery, authorization, and the legacy HTTP projection. The implementation
baseline is main@1495d063; migration 0003_phase_three_publication is the first schema
revision that permits final publication.
Use MySQL 8.0.16 or newer. Verify migrations/CHECKSUMS.sha256, back up the legacy schema,
and run migrations only with the deployment administrator:
SCRIPT_BUILD_WRITE_DATABASE_URL='mysql+asyncmy://migration_admin:...@db/script_build' \
python -m alembic upgrade head
The 0003 preflight refuses missing or duplicate script_build_record.reson_trace_id values and
duplicate publication identities. The revision adds DATETIME(6) ownership fencing, the Root
manifest artifact kind, the HTTP command journal, one final publication identity per build, and
candidate/runtime views with WITH CASCADED CHECK OPTION. Its revision identifier deliberately
fits Alembic's MySQL version_num VARCHAR(32).
Production must use three different database principals:
SELECT on authorized legacy/Host tables and views only;status=success;SqlAlchemyFinalPublicationUnitOfWork.Grant names are deployment-specific and therefore are not embedded in Alembic. Revoke the old
unrestricted writer before enabling Phase 3. Configure SCRIPT_BUILD_READ_DATABASE_URL,
SCRIPT_BUILD_WRITE_DATABASE_URL, and SCRIPT_BUILD_FINAL_DATABASE_URL; production startup
rejects reused usernames. MySQL sessions are explicitly opened at READ COMMITTED.
Use migrations/mysql_phase_three_grants.sql.example as the reviewed least-privilege template.
Publication is allowed only after one frozen Manifest, passed Root Validation, and explicit Root
ACCEPT. A single final AsyncSession replaces branch 0, reads the canonical legacy projection back,
publishes the Manifest and StructuredScript, sets the accepted pointer, records the publication,
and changes the build to success. Any pre-commit error rolls back all of those writes. A lost
commit acknowledgement is classified by the unique final publication identity and is read back;
it is never blindly replayed.
Use per-build endpoints only; startup does not scan the database:
POST .../{id}/phase-three/advance enters the frozen Root delivery continuation;POST .../{id}/resume classifies durable state and performs only a safe continuation;POST .../{id}/finalize retries the same immutable final identity;POST .../{id}/reconcile is inspection-only unless explicitly retrying finalize;GET .../{id}/publication?type=direction|final reports publication state.If recovery reports manual reconciliation, do not edit an Artifact, digest, Decision, or accepted pointer. Preserve the task ledger and database facts for inspection.
Run all of the following before release:
python -m pytest
SCRIPT_BUILD_TEST_MYSQL_DSN='mysql+asyncmy://.../disposable_db' python -m pytest -m mysql
python -m ruff check src migrations tests
python -m ruff format --check src migrations tests
python -m mypy src/script_build_host
python -m compileall -q src
git diff --check
The MySQL database must first contain the legacy tables and be upgraded from 0001 through 0003.
The gate checks MySQL version, READ COMMITTED, DATETIME(6), enforced CHECK constraints,
candidate/runtime view check options, the Alembic head, and a real atomic final publication.
tests/fixtures/legacy_http_goldens_manifest.json records the two legacy Run 454 responses that
were actually captured, including route, status, redaction policy, comparison mode, and SHA-256.
Uncaptured legacy routes are listed explicitly rather than represented by invented fixtures.