aiml
SearchMy learning
Menu

Lesson 35 of 36Module 12

Release the RAG application

Stage a sealed artifact and refuse a failed task gate

Stage the existing retrieval service locally, bind its files to a retained release identity, block failed task checks, and test artifact-pointer rollback.

Prerequisites

Python, the local FastAPI retrieval service and the preceding scripted retrieval-task evaluator.

By the end, you can

  • Bind a staged runtime to a separately retained artifact identity.
  • Keep HTTP smoke checks separate from retrieval-task release policy.
  • Refuse promotion and revalidate the previous artifact before rollback.
Runtime
Python 3.12+
Dependencies
FastAPI, Uvicorn, HTTPX and Pydantic; pinned in requirements.lock

Lab code, tests and fixtures (ZIP, 169 KB)Quiz check your understanding

Progress is saved in this browser.

A service can start and return valid HTTP while still missing its retrieval targets. Build a local release controller that checks the exact candidate files, starts that candidate in staging, then consults the preceding task report before changing its artifact registry. The recorded task failures block promotion.

When you need this. Use distinct release checks when a successful startup must not override a failed application task. This lab changes a temporary local artifact pointer; site integration, production traffic and deployment remain outside its boundary.

Sealed runtime bundleRetained release identityVerify file closure+ exact file hashesStart staged serviceLocal HTTP smokePinned archived task reportFrozen fixture policyTask gate failsRefuse promotionExisting artifact pointerUnchanged bytesno pointer write
The runtime smoke passes in the recorded local staging run. The separate task check fails, preventing promotion. The pointer names an artifact directory; it does not route live traffic.

Retain an identity outside the candidate

A bundle is a directory containing the explicit runtime file closure, pinned source snapshot, dependency lock and archived task report. A manifest lists each file’s SHA-256 digest. The release ID is a digest of that manifest, retained separately by the controller.

Checking a candidate only against its own manifest would allow changed code and changed hashes to agree. The separately retained expected ID must still match. Verification also checks the exact file set and refuses symbolic links. These hashes establish consistency with trusted input; they do not authenticate an untrusted publisher.

Changed artifact. Editing code fails file verification. Replaced manifest. Replacing a listed hash also changes the release identity. Extra file. An undeclared file or symbolic link is refused rather than included implicitly.

Start the actual staged copy before considering promotion

The controller starts the sealed directory’s Uvicorn service on a temporary loopback socket. HTTPX checks readiness, a normal retrieval, a strict invalid request and the exact serialized response digest. The response also confirms the unchanged model-free mode. The child is shut down and reaped after these checks; its raw signal exit status remains in the record.

The recorded runtime checks pass: true. That result concerns the HTTP boundary and the supplied request. It does not establish that every supported task succeeds.

The archived report is an exact copy of the preceding task evaluator’s actual run. The controller checks its corpus binding and recomputes the frozen scripted-policy release threshold with the copied checker. It does not execute another evaluation, alter target labels or generate an answer.

Let the failed task check stop the write

The archived scripted task gate returns FAIL. Promotion is refused with task_gate_not_accepted, and the existing artifact pointer stays byte-identical: true.

Failed task gate. Passing HTTP smoke cannot waive the recorded task misses. Wrong smoke binding. A result for another release ID, a failed check or an incomplete check set is refused before the pointer changes. Smoke records are trusted controller values, not authenticated remote attestations.

The positive-control unit test explicitly stubs a passing task gate to exercise promotion’s success branch. It is a test fixture, not an observed task-quality pass. The actual CLI run retains the failed gate and refused promotion.

Restore a previous artifact without claiming traffic rollback

An artifact pointer is a JSON record naming the current directory and its retained release ID, plus the previous entry. Before replacing it, the writer flushes a temporary file in the same directory and uses os.replace. This assumes one writer; it is not a distributed transaction or a guarantee against every power-loss case.

The rollback drill begins with an explicitly synthetic two-entry registry. It revalidates the prior bundle, then restores that entry. The recorded restoration check is true. Neither seeded entry is claimed as a prior production deployment, and changing this file does not switch running service traffic.

Changed rollback target. If the prior bundle changed, rollback refuses it and keeps the pointer intact. Failed replacement. A controlled replace error preserves the old pointer and removes the temporary candidate file. A real traffic rollback would additionally need routing, process health, compatible state and an authorized deployment controller.

Execute the local release drill

Download the complete local release lab. It contains the unchanged retrieval runtime, pinned packages, original summary corpus, archived task report, release controller and failure tests. Create a virtual environment and install requirements.lock before running the drill.

Prepare the local labTerminal · bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.lock
release.pySource · pythonDownload
"""Sealed local artifacts and runtime smoke checks, not a production deployer."""
from pathlib import Path
import hashlib
import importlib.util
import json
import os
import shutil
import tempfile

FILES = ('api.py','app.py','service.py','tools.py','refine.py','limits.py',
         'bm25.py','version_documents.py','load_documents.py','loopback.py',
         'serve.py','requirements.txt','requirements.lock','LICENSE',
         'fixtures/snapshot.json','fixtures/development-cases.json',
         'evaluator.py','compare.py','fixtures/task-evaluation.json')
QUESTION = 'Will write_text replace the contents of an existing file?'

def digest(data):
    return hashlib.sha256(data).hexdigest()

def canonical(value):
    return json.dumps(value,sort_keys=True,separators=(',',':'),ensure_ascii=True).encode()

def package(source, destination, label):
    """Copy the explicit runtime closure into a new immutable candidate directory."""
    source, destination = Path(source), Path(destination)
    if destination.exists():
        raise ValueError('destination_exists')
    if not isinstance(label,str) or not label or len(label)>64:
        raise ValueError('invalid_label')
    destination.mkdir(parents=True)
    for name in FILES:
        path=source/name
        if path.is_symlink() or not path.is_file():
            raise ValueError('invalid_source_file')
        target=destination/name
        target.parent.mkdir(parents=True,exist_ok=True)
        shutil.copyfile(path,target)
    (destination/'release-config.json').write_bytes(canonical({'schema':1,'label':label,'deployment':'local-staging-only'}))
    manifest={'schema':1,'files':{name:digest((destination/name).read_bytes()) for name in (*FILES,'release-config.json')}}
    release_id=digest(canonical(manifest))
    (destination/'manifest.json').write_bytes(canonical(manifest))
    verify(destination,release_id)
    return release_id

def verify(directory, expected_id):
    """The caller retains expected_id separately from the candidate it checks."""
    directory=Path(directory)
    if directory.is_symlink() or any(p.is_symlink() for p in directory.rglob('*')):
        raise ValueError('unexpected_bundle_entry')
    manifest=json.loads((directory/'manifest.json').read_bytes())
    if set(manifest)!= {'schema','files'} or type(manifest['schema']) is not int or manifest['schema']!=1:
        raise ValueError('manifest_schema')
    if digest(canonical(manifest))!=expected_id:
        raise ValueError('release_identity_mismatch')
    expected=set(FILES)|{'release-config.json'}
    if set(manifest['files'])!=expected:
        raise ValueError('file_closure_mismatch')
    actual={str(p.relative_to(directory)) for p in directory.rglob('*') if p.is_file()}
    if actual != expected|{'manifest.json'} or any(p.is_symlink() for p in directory.rglob('*')):
        raise ValueError('unexpected_bundle_entry')
    for name,sha in manifest['files'].items():
        if digest((directory/name).read_bytes())!=sha:
            raise ValueError('file_hash_mismatch')
    return manifest

def smoke(directory, expected_id):
    """Start the actual staged service, send real HTTP, then reap its child."""
    directory=Path(directory)
    verify(directory,expected_id)
    spec=importlib.util.spec_from_file_location('staged_loopback',directory/'loopback.py')
    harness=importlib.util.module_from_spec(spec)
    # Load the helper without writing __pycache__ inside the sealed candidate.
    exec(compile((directory/'loopback.py').read_bytes(), str(directory/'loopback.py'), 'exec'), harness.__dict__)
    # Prevent the Python child from adding bytecode to the sealed runtime closure.
    previous=os.environ.get('PYTHONDONTWRITEBYTECODE')
    os.environ['PYTHONDONTWRITEBYTECODE']='1'
    try:
        with harness.running_server() as (client,lifecycle):
            health=client.get('/health')
            valid=client.post('/retrieve',json={'question':QUESTION})
            invalid=client.post('/retrieve',json={'question':QUESTION,'max_steps':True})
            body=valid.json()
            checks={'health':health.status_code==200,
                    'valid_request':valid.status_code==200 and body['status']=='completed',
                    'strict_refusal':invalid.status_code==422,
                    'wire_digest':digest(valid.content)==valid.headers.get('X-Retrieval-Body-SHA256'),
                    'no_model':body['model_called'] is False}
    finally:
        if previous is None:os.environ.pop('PYTHONDONTWRITEBYTECODE',None)
        else:os.environ['PYTHONDONTWRITEBYTECODE']=previous
    verify(directory,expected_id)
    return {'release_id':expected_id,'checks':checks,'passed':all(checks.values()),
            'scope':'local-runtime-smoke-only','lifecycle':lifecycle}

def atomic_state(path, value):
    path=Path(path)
    path.parent.mkdir(parents=True,exist_ok=True)
    fd,name=tempfile.mkstemp(prefix='.candidate-',dir=path.parent)
    try:
        with os.fdopen(fd,'wb') as stream:
            stream.write(canonical(value));stream.flush();os.fsync(stream.fileno())
        os.replace(name,path)
    finally:
        if Path(name).exists():Path(name).unlink()

def task_gate(directory, expected_id):
    """Recheck the pinned preceding task report; do not run a model or rerank it."""
    from evaluator import release_gate
    directory=Path(directory)
    verify(directory,expected_id)
    raw=(directory/'fixtures/task-evaluation.json').read_bytes()
    saved=json.loads(raw)
    report=saved['normal']
    snapshot=json.loads((directory/'fixtures/snapshot.json').read_bytes())
    if report['identity']['corpus_id']!=snapshot['snapshot_id']:
        return {'decision':'NOTCOMPARABLE','release_id':expected_id,'reason':'corpus_mismatch'}
    result=release_gate(report,report['identity'],'scripted_refinement')
    return {'decision':result['status'],'release_id':expected_id,'report_sha256':digest(raw),
            'result':result,'scope':'archived-scripted-development-fixtures-only'}

def promote(pointer, directory, expected_id, smoke_record):
    """Move only a local artifact pointer; this does not switch running traffic."""
    verify(directory,expected_id)
    names={'health','valid_request','strict_refusal','wire_digest','no_model'}
    checks=smoke_record.get('checks',{})
    if (smoke_record.get('release_id')!=expected_id or smoke_record.get('passed') is not True
        or set(checks)!=names or any(value is not True for value in checks.values())):
        raise ValueError('smoke_not_accepted')
    if task_gate(directory,expected_id)['decision']!='PASS':
        raise ValueError('task_gate_not_accepted')
    pointer=Path(pointer)
    old=json.loads(pointer.read_bytes()) if pointer.exists() else None
    state={'current':{'directory':str(Path(directory).resolve()),'release_id':expected_id},
           'previous':old['current'] if old else None}
    atomic_state(pointer,state)
    return state

def rollback(pointer):
    pointer=Path(pointer);state=json.loads(pointer.read_bytes());old=state.get('previous')
    if not old:raise ValueError('no_previous_release')
    verify(old['directory'],old['release_id'])
    restored={'current':old,'previous':state['current']}
    atomic_state(pointer,restored)
    return restored
Stage, inspect refusal and exercise rollbackTerminal · bash
python run.py --output scratch-result.json
python -m unittest -v test_release
Saved CLI outputRecorded output · textDownload
staged runtime smoke: True
archived scripted task gate: FAIL
promotion: task_gate_not_accepted pointer unchanged: True
seeded local rollback: True
changed artifact: file_hash_mismatch

The driver creates and removes temporary candidate directories. Its successful exit means the expected refusal and rollback assertions held. It does not mean the candidate was approved for release. There is no cloud resource, publisher identity service, production process manager or migration in this lab.

Inspect the complete saved result

This saved result predates the helper-loading fix described in the README; current tests also check that loading the helper leaves the sealed bundle unchanged.

Actual staging and artifact-registry resultRecorded output · jsonDownload
{
  "schema_version": 1,
  "execution_mode": "actual-local-staging-http-and-artifact-registry",
  "model_called": false,
  "release_ids": {
    "previous_fixture": "c1d499c3dbac6c76eb213339d5cd5c2109aa476df55d3420e655fae06ce0670c",
    "candidate": "7375e5ceb066fdf6e88eee9d12b5bfada7023f2cd064ee741b5a518cfb272e74"
  },
  "runtime_smoke": {
    "release_id": "7375e5ceb066fdf6e88eee9d12b5bfada7023f2cd064ee741b5a518cfb272e74",
    "checks": {
      "health": true,
      "valid_request": true,
      "strict_refusal": true,
      "wire_digest": true,
      "no_model": true
    },
    "passed": true,
    "scope": "local-runtime-smoke-only",
    "lifecycle": {
      "command": [
        "python3",
        "serve.py",
        "--fd",
        "<inherited-loopback-socket>",
        "--app",
        "api:app"
      ],
      "startup_observed": true,
      "shutdown_exit_status": -15,
      "stdout": "",
      "stderr": "INFO:     Started server process [58084]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     Shutting down\nINFO:     Waiting for application shutdown.\nINFO:     Application shutdown complete.\nINFO:     Finished server process [58084]\n",
      "shutdown_complete_observed": true,
      "server_process_reaped": true
    }
  },
  "task_gate": {
    "decision": "FAIL",
    "release_id": "7375e5ceb066fdf6e88eee9d12b5bfada7023f2cd064ee741b5a518cfb272e74",
    "report_sha256": "f89e7459226cee8cb810b59a6dae61246f04d0ce805c988c38d9e66fe4a6fb39",
    "result": {
      "status": "FAIL",
      "reason": "fixture_thresholds_not_met",
      "thresholds": {
        "minimum_supported_pass_fraction": 1.0,
        "maximum_integrity_failures": 0,
        "require_deterministic_repeats": true
      },
      "supported_trials": 6,
      "supported_outcomes": {
        "pass": 4,
        "fail": 0,
        "incomplete": 2
      },
      "unsupported_trials": 2,
      "integrity_failures": 0,
      "deterministic_repeats": true
    },
    "scope": "archived-scripted-development-fixtures-only"
  },
  "promotion": {
    "refused": "task_gate_not_accepted",
    "pointer_unchanged": true
  },
  "rollback": {
    "setup": "synthetic two-entry artifact registry; not prior deployment",
    "restored_previous_identity": true
  },
  "changed_artifact": {
    "refused": "file_hash_mismatch"
  },
  "scope": "The staged service really ran over loopback HTTP. Promotion was blocked by archived task failures. Only a local artifact pointer is changed in the rollback drill; no running traffic, cloud release or answer-quality approval."
}

Explain why a healthy candidate stayed inactive

Locate the passing runtime checks and the failed task gate. Explain why the pointer stayed unchanged despite a successful top-level CLI exit. Then change a previous bundle in the rollback test and identify which check prevents restoration. Distinguish restoring an artifact record from routing requests to a running process.

The staged HTTP checks pass, but the pinned scripted retrieval-task gate fails. What should this controller do?

Choose one answer, then check your reasoning.

Read the explanation

Runtime health, task acceptance and artifact identity are separate checks.

The next capstone starts a different corpus and application contract. Its release result must be read separately from this refused Python-task promotion; the earlier failed cases remain in the saved results.

What’s next

Bring the pieces together in Build the Developer Knowledge Assistant: a local browser UI and CLI with versioned retrieval, collection permissions and cited answers.

References

The official Python file-replacement documentation describes os.replace; the temporary-file documentation covers temporary-file creation and cleanup. hashlib supplies the file digests. The retained identity, explicit closure, task threshold and refusal behavior are application policies tested by this local drill.

Written by Arjun Prabhulal.

Module 12

Evals and release