Skip to content
RasaGet a free licence
Menu

tutorial

Chapter 4 of 4

Check a proposed Python file without running it

by Rod Rivera Published

Judge saved bytes without running them, compare three rejected proposals and record your release decision.

You have a Python file from the labeled sample or the optional coding-agent build for your router helper. Its check_line_status() function should return a fixed status: "ok" and changes_anything: False for a pretend internet-line check. It measures no connection. You will judge its saved bytes without running it, then decide whether to keep it for manual review.

Allow about 20 minutes. Keep the builder’s receipt, including its saved path and hash, beside the file. Use the newest unchanged proposal from the previous part and retain whether it came from the sample or a live build. A completion message alone cannot settle whether those bytes meet the requirements.

Run the gate on the exact file

Open a spare terminal. Replace the sample value below with the absolute path from your receipt, keeping the quotes:

PROPOSAL='/absolute/path/from/your/receipt/check_line_status.py'
G=/workspaces/rasa-community-resources/workshops/software-factories-masterclass/router-helper/gate
sha256sum "$PROPOSAL"

Compare that hash with the full hash from its receipt. If they differ, stop: you are reviewing different bytes. Only if they match, run the gate:

python3 "$G/gate.py" "$PROPOSAL"

A matching file prints:

ACCEPT - the change passed every check

If the file is missing, check the copied path. Do not resolve a server-relative path against an unrelated helper folder. If it is rejected, keep the file and the named failing checks. Do not edit the gate to get a pass.

The gate reads the source once and inspects its syntax tree. It does not import or execute the proposed file. It accepts only one function, with no arguments and a single literal-dictionary return. These are the values required by the exercise:

def check_line_status():
    return {"operation": "check_line_status", "status": "ok", "changes_anything": False}

This is a toy status result, not a real line measurement. The restricted shape makes the check inspectable. It also means this gate cannot accept a useful general-purpose Python program with network calls or changing state.

The gate reads a proposal without running it

Parsing the file is a different operation from importing it. No arrow in this path executes the candidate’s function.

FigureThe gate reads a proposal without running it

Catch a passing check on changed bytes

The gate checks structure and values, not the hash in your builder receipt. On 2 November, the public fixture returned a 114-byte file. Appending a harmless comment grew it to 156 bytes. The unmodified gate still printed ACCEPT, while the saved file’s hash no longer matched the returned receipt. The accepted structure was unchanged; the evidence identifying the proposal was stale.

Reproduce this with a new local fixture, preserving your earlier proposal. In a spare terminal:

B=/workspaces/rasa-community-resources/workshops/software-factories-masterclass/router-helper/builder
"$HOME/.venv-builder/bin/python" "$B/builder.py" --fixture

Copy this new fixture’s absolute path into SAMPLE below. Do not use a live proposal or your earlier decision file. Keep the original bytes before editing:

SAMPLE='/absolute/path/from/the/new/fixture/check_line_status.py'
cp "$SAMPLE" "$SAMPLE.before-edit"
printf '\n# Changed after the receipt was created.\n' >> "$SAMPLE"
python3 "$G/gate.py" "$SAMPLE"
sha256sum "$SAMPLE"

The gate still accepts. The current hash differs from the fixture’s receipt. The recorded local reproduction kept these hashes:

EvidenceSHA-256
Fixture receipt, before edit543faea519658986f01b17e32cf8a092bd5801a6ef1dc7b53c5419af7e6452f3
Saved file, after edit7a236233c173b618e3690b7e0caf53900404585dfdf5fbb74e7499be1cc13ed5

A mismatch is a reason to stop even when the gate accepts. For your real decision, return to the unchanged proposal from the previous part and compare its receipt hash before recording acceptance. Retain the edited fixture and its .before-edit copy as failure evidence.

These separate terminal checks are a learning exercise. A production release should compare the expected hash and inspect the same bytes in one controlled operation, then release those bytes. A file could change between independent commands. This tutorial leaves that production enforcement to your deployment system.

Prove that the gate can reject

Use the four public candidates. Run each command and compare its result:

python3 "$G/gate.py" "$G/candidates/good.py"
python3 "$G/gate.py" "$G/candidates/bad_sends_reset.py"
python3 "$G/gate.py" "$G/candidates/bad_writes_a_file.py"
python3 "$G/gate.py" "$G/candidates/bad_opens_network.py"

The good file must be accepted. Each bad file must be rejected. A rejection exits with code 1; it is the expected result for those examples, not an installation failure.

CandidateWhat the file attemptsRequired result
GoodReturn the agreed plain valuesAccept
ResetRecord a reset commandReject
File writeWrite a local fileReject
NetworkOpen a network connectionReject

The bad files are read as text. Do not run them with Python to see what they do. The gate’s own checks explain the rejection without letting the proposal perform its action.

Make the decision yourself

Write a short decision record in the editor. Include the build identifier, path, and hash, whether it was a fixture or live result, the gate outcome and your reason for accepting or refusing it.

If the gate accepts but the receipt refers to another file, you have not checked the same proposal. If the receipt is missing, do not fill in a plausible hash. Resolve the mismatch before recording acceptance.

Take the mechanism to another project

You built three separate controls: confirmation before the helper’s action, a visible receipt for the builder’s output, and a check over the saved proposal. Each answers a different question.

For an email assistant, for example, confirmation would name the recipient and message being sent. Its external send receipt would establish delivery status. A proposal gate would test the proposed change before deployment. Recipient consent belongs to the confirmation and send workflow. The proposed code needs its own checks before deployment.

Keep the builder’s output separate from the running service. In a real deployment, enforce who can change the gate and who can release a passed proposal. This tutorial does not create that operating-system or repository permission separation.

Optional: check the completed reference

Your own helper is the exercise. The completed companion also has native project and trained-archive checks if you want a fixed reference for comparison. The checks use uv. The prepared Codespace does not install that tool, so install the pinned version below in a separate environment inside the Codespace, leaving Mantle’s environment alone:

python3 -m venv "$HOME/.venv-uv"
"$HOME/.venv-uv/bin/pip" install uv==0.10.8
export PATH="$HOME/.venv-uv/bin:$PATH"
cd /workspaces/rasa-community-resources/workshops/software-factories-masterclass/router-helper/mantle
uv sync --locked --prerelease=allow --python 3.12
uv run rasa train
uv run python -m unittest discover -s . -v

Expect successful training and eight tests ending in OK: six installer tests and two native checks. The native checks validate the supplied project and compare the trained archive’s skill files with their source. They run no model conversation or builder session. If uv is missing, check that this terminal has the PATH line above. If training rejects your license, correct your Codespaces secret and restart; do not count an older archive as a new pass.

Finish without leaving services running

Stop the Inspector and the tool server with Ctrl+C in their own terminals. Preserve your decision and any failure evidence. In GitHub’s Codespaces list, choose Stop codespace when you are done. Stopping compute does not remove stored files; storage can still count toward your allowance.