Skip to main content
Plexara
Governance12 min read

605 - What a run may do, and the record it leaves

A run presents the roles its author held at the save, every call is authorized at that moment, and narrowing the persona takes effect on the next run. A save requires one format, work in main(), the structural rules, and tests that pass, reach the code, and read the data, and refuses a credential-shaped literal. A new version is replayed against recent successful runs, and one that changes what the automation does waits for the person’s agreement to a plain-language summary. The dialect has no network, no filesystem, no clock, and no imports beyond pinned libraries, so everything a script does is a platform call audited under the script’s own identity. The lifecycle, who sees what, state and checkpoints on a failed run, ownership and an administrator’s transfer, and the administrator’s view of every script and every run.

What you will take away from this lesson

A script runs when nobody is watching. That is the point of it, and it is also the reason the question of what a run may do has to be answered before the first schedule fires. The answer on Plexara is short: a run may do exactly what its author could do at the keyboard on the day the version was saved, and every step it takes is written down.

This lesson is the governance half of the series. It covers the authority a run carries, what a save requires before anything is stored, the agreement a change in behavior waits for, why the language itself cannot reach outside the platform, the lifecycle of a script, who sees what, what a failed run keeps, and the view an administrator gets of all of it.

Learning Objectives

  1. 01State the authority rule: a run presents the roles its author held at the save, every call is authorized at that moment, and narrowing the persona takes effect on the next run.
  2. 02Know what a save requires: one format, work in main(), the structural rules, tests that pass, reach the code, and read the data, and no credential-shaped literal.
  3. 03Explain what happens when a new version would change what an automation does: the replay against past runs, and the plain-language summary the person agrees to before the save goes through.
  4. 04Explain why a script cannot reach out: no network, no filesystem, no clock, no imports beyond pinned libraries, and every action a platform call audited under the script’s own identity.
  5. 05Read the lifecycle, active through disabled, deprecated, and superseded, and what each state does to runs and schedules.
  6. 06Know who sees what: a script’s definition is everyone’s, its runs’ detail and its state are its owner’s, and libraries are shared by anyone who loads them.
  7. 07Know what a failed run keeps: state saved only on success, and a checkpoint committed however the run ends.
  8. 08Understand an administrator’s transfer of ownership and what it changes, and find the administrator’s view of every script and every run.

Where this lesson sits

Lessons 602 through 604 built a script, gave it outputs, and put it on a schedule. This lesson steps back and asks what that schedule is allowed to do while nobody is watching, and what it leaves behind so that somebody can check later.

600 Series: Automations

Open index

The 600 series picks up the registered table from the 500 series and the prompt mechanics from the 400 series. It is the last numbered series in the curriculum.

The authority a run carries

The rule that makes unattended work safe is that a run never has more reach than its author had at the keyboard. The roles the author held are captured on the version at the moment it is saved, and every run of that version presents exactly those roles, whoever asked for the run and whenever the schedule fired it.

Authorization happens per call, not per script. Each query, export, and tool call a run makes is checked against the persona those roles resolve to, at the moment it is made, the same way an agent’s call is checked. That is why narrowing a persona takes effect on the script’s next run without anyone editing the script.

The authority a run carries, in five rules

  1. 1

    A run executes as the script’s own principal

    Not as you, and not with your session. It authenticates as script:<name>, and that is the name audit records and the name its outputs belong to.

  2. 2

    It presents the roles its author held at the save

    The roles are captured on the version when it is saved and cannot be set any other way. A script can never do unattended what the person who wrote it could not do themselves.

  3. 3

    Every call is authorized at the moment it is made

    Each query, export, and tool call a run makes crosses the same authorization an agent’s call crosses, against the persona those roles resolve to. There is no script-side allow list to keep in step.

  4. 4

    Narrowing the persona takes effect on the next run

    Remove a connection or a tool from the persona and the script’s next run is refused at that call, in the persona filter’s own words. Nothing about the script has to change.

  5. 5

    A cadence carries no authority of its own

    Re-timing a script reaches nothing the script could not already reach; the run gate and the persona are re-read at every fire.

The platform says the same thing every time a script is saved. The answer to the first save in 602 read: “Saved, and it runs: run_script executes it under the access you held when you saved it, and a schedule you set will fire it.”

What a save requires

A save is the version that runs, so the checks that protect a run happen at the save, where the author can fix the problem, rather than at the next fire. They are written for the agent that writes the script: a person who asks for an automation never reads the code and never sees a finding.

What a save requires

  • One format

    Rewritten, never refused.

    The source is stored in one canonical layout, every comment kept, so what is read back is what runs and a diff between two versions shows only what changed. Source already in that layout is stored byte for byte.

  • The work in main()

    Refused until it is.

    The platform calls main() after the script loads. The top level holds functions, constants written as literals, and docstrings, which keeps each part of the script testable on its own. A source with no main() is saved as a library.

  • The structural rules

    Refused, naming the rule, the line, and the fix.

    Functions that are too long, too branched, or nested too deep; a local or parameter never read; a name that hides a built-in; a function with no docstring; SQL assembled from values instead of bound with params; a platform call made once per element of a collection.

  • Tests that pass and reach the code

    Refused, naming the lines no test reached.

    At least one test, every test passing, and at least 80% of the script’s statements reached. A test that makes no assertion is refused.

  • Tests that read the data

    Refused, naming what went unread.

    A test that still passes when the rows it replays are altered (the last row dropped, the first row’s values changed) is blind to its data and is refused. Together the tests must assert on every output they produce: each column of each export, the staged state, each notification, and the run’s result.

  • Recorded calls

    What a test runs against.

    Every draft run and every run records each host call it makes and the answer it got. A test replays a recording, or declares the answer a call gets with testing.answer, so a test never reaches an upstream and can take the branch no draft takes.

  • No credential-shaped literal

    Refused for a known format; a warning for a name.

    A private key header, a cloud access key id, a well-known token format, or a URL with a password in it blocks the save. A variable merely named like a secret is a warning. The scan is a tripwire that catches a paste, not a guarantee; scripts name connections, and Plexara holds the credentials.

  • Source that parses

    Refused at the keyboard.

    Code that cannot run is refused rather than stored, so the failure surfaces while somebody is looking at it instead of at the next fire.

The same checks apply to every save, from the agent or from the portal editor, including the next edit of a script saved before the rules existed. They are checked at the save, never at a run, so an older script keeps running as it is until somebody saves it again. An output addressed to a destination Plexara does not have is reported by validate rather than refused at the save, because refusing the save would also refuse the edit that fixes it.

The test report a save keeps

The tests travel with the version they proved. The script’s Tests tab shows the report from its save, so anyone who opens the automation can see what its tests reached and that each one passed.

The Tests tab of the ACME demo automation acme-returns-alert-608: 100% of statements reached by the tests (19 of 19), 2 tests: 2 passed, as they ran when version 1 was saved, listing test_posts_to_people_and_systems and test_quiet_day_sends_nothing, each with a passing mark.
The Tests tab keeps the report from the save: how much of the code the tests reached and each test that passed. The tests are versioned with the script, so the report always belongs to the version that runs.

When a new version changes what the automation does

Passing its own tests shows a version is internally sound. It does not show the version still does what the person relied on. So a new version of a script that has run is replayed against the recordings of its most recent successful runs, through both the saved source and the new one, and the two are compared.

A new version, replayed against the old one

Compared across the five most recent successful runs

  • The rows each output produces, its columns, and their types
  • The state the run would save
  • The notifications it would post
  • The result it hands back
  • What it reaches: tools, connections, and destinations
Nothing differs
The save goes through. This is how a refactor is shown to be safe: the new version produced what the old one did from the same recorded inputs.
Something differs
The save is refused, naming each difference, until it carries a plain-language summary of what the automation will now do differently and the agent’s confirmation that the person it runs for agreed (change_summary and user_agreed). Both are kept on the version with who saved it and when.

The person approves behavior, never code. There is no reviewer role, no queue, and no second approver. The script’s page lists every version’s summary under What changed, with who saved it and when it was agreed, and shows no code, diff, or test output there.

Why the language cannot reach out

The dialect a script is written in is deliberately small, and the absences are the security model. A script has no way to open a connection, read a disk, tell the time, or pull in code from outside the platform, so the only outside world it has is the platform, and the platform records everything it is asked to do.

Why the language cannot reach out

  • No network

    A script opens no socket and supplies no endpoint. An external API is reached only through a connection Plexara already holds, with platform.call, so what a script can reach is what its author can reach.

  • No filesystem

    A file is read through the registered table or a platform call, and written as an output. Nothing lands anywhere the platform does not record.

  • No clock, no randomness

    The fire time is pinned on the run, so the same version with the same parameters asks the same question. A run may ask how long it has left before its time limit, and that answer is recorded with the run like any call’s, so a replay reads the same number. A run nobody can reproduce is not a governed run.

  • No imports, only libraries

    A script starts with platform, json, xml, date, run, sum, and fail, and a test also has testing and assert. load() brings in a saved library at a named version and nothing else, and a library version never changes, so the program is the source on the version plus library code that cannot move under it.

Everything a script does is therefore a platform call, over the same governed path an agent’s call takes, and every one of them is audited under script:<name>. A run’s reach can be read from its source before it runs, which is what validate reports, and its record is complete after.

The lifecycle

A script is in one of four states, and the state is the only thing that decides whether a saved version’s run is admitted. There is no approval queue and no reviewer between a save and a run; a save that changes what the automation does waits only for the agreement of the person it runs for, recorded on the version. After the save, the lifecycle is what can take a script out of service in one action, and it says so wherever the script is shown.

The lifecycle of a script

  1. 1

    Active

    The saved version runs: from any session, from the Run button, and on its schedule.

  2. 2

    Disabled

    Nothing executes it. The run gate refuses in its own words on every surface, and a cadence set on a disabled script saves and stays inert, which the tool and the page both say plainly.

  3. 3

    Deprecated

    Taken out of service and marked as such. The record stays; runs are refused.

  4. 4

    Superseded

    Retired in favor of a named replacement. The reference to the old script still resolves, and says which script replaced it.

There is no approval queue and no reviewer between a save and a run. What stands in front of a version is the save itself: its checks, and the person’s agreement when the version changes what the automation does. Once saved it runs; taking it out of service is the only gate after that, and a schedule is never deleted, only disabled, so the row that explains past runs stays.

Who sees what

A script is how a report or a file somebody was given was produced, so its definition is open: everyone signed in can read what it is, its source, its versions, and how its runs have gone. Acting on it, and reading what a run was given and printed, stays with its owner and administrators. The team shares the outputs, which have the sharing model of every other asset, and the libraries scripts load.

Who sees what

  • Everyone signed in

    Every automation and schedule, and each script’s definition: what it is, its parameters, whether a run would be admitted, its schedule, its source, and its version history. Its run history too, as how each run went, when, how long it took, and why one failed. Reading a script grants nothing; a run still presents its author’s captured roles.

  • Its owner and administrators

    What each run was given, printed, and wrote; the state; the roles each version’s author held at the save; and every action: running, editing, scheduling, granting, and deleting.

  • A persona, role, or API key granted its runs

    The right to start a run and read the runs it started, with their outputs. A grant decides who may ask for a run, never what the run may reach, and it cannot change the script.

  • Whoever asked for one run

    That run: its parameters, outputs, and log.

  • The team

    The outputs, under the sharing model of every other asset, and the libraries. Anyone can load any library, so the pagination, date windows, and table shaping many scripts repeat are written once and shared.

A prompt that references a script resolves it for every signed-in reader; the last successful run and what it produced come with it only for the owner and administrators. The way a whole team benefits from one person’s script is the way 606 shows: the review it produces is shared, and the script stays its owner’s to run and change.

Ownership and transfer

Because a script is personal, someone leaving the team or changing roles is a real event for the automations they own. An administrator can move a script to another person, and the move changes more than a name on a listing.

Ownership, and an administrator’s transfer

  • Only an administrator moves a script

    From the script’s page, choosing the new owner from the people who have signed in to your deployment at least once. An owner cannot give a script away.

  • The whole script moves at once

    What the owner sees, edits, runs, and schedules, and the history: the new owner reads the run records and dry-run accounts the previous owner produced.

  • The move is a version, authored by the administrator

    From then on a run presents that administrator’s roles. Moving a script to an administrator is how it comes to run with an administrator’s reach.

  • A name clash is refused

    If the receiving owner already keeps a script of the same name, the transfer does not happen.

  • It is audited

    Like every other administrative write, the transfer is recorded in the audit log.

The transfer control

The transfer is done on the script’s own page, the same page the owner reads it on. It is the one thing that page offers an administrator and not an owner.

A script’s page opened by an administrator, with the Owner control for moving the script to another person, chosen from people who have signed in at least once.
The script page an administrator opens is the page its owner opens, plus one control: Owner. There is one script page for both surfaces, so the two views cannot drift apart.

The record a run leaves

Every run writes a row: the version that executed, what triggered it, how long it took, the outputs it wrote and the asset versions they became, and the log the script printed. Every call the run made is also in the audit log under the script’s own principal. The trace below is the last successful run of the margin script from 604, read back.

One run, and the record it leaves

What the agent calls. This is the exchange the agent has with Plexara on your behalf, shown for the technical reader. You ask in plain language; you never type any of it.

run_script name=acme-supplier-margin args={"month":"2025-12-01","conn":"acme","mode":"refresh"}
→ status succeeded   version 6   trigger tool   duration 8,231 ms
  outputs
    supplier-margin-by-category   csv    asset 871da875-cd99-44f9-b6d9-55732b4e9086  version 3
    supplier-margin-feed          json   asset 589855b4-cfe1-4355-af26-ae68cbc5770f  version 3
    supplier-margin-dashboard     refresh  asset bab7c8e7-67e6-4561-834a-32524f203fc8  version 2
  audited as script:acme-supplier-margin, presenting the roles captured at the save of version 6

The run row names the version that executed, what triggered it, how long it took, every output it wrote with the asset version it produced, and the log it printed. The audit log records each call the run made under the script’s own principal. Between the two, a reader six months later can say exactly what happened and under whose authority.

Versions, their test reports, and the dry-run account

The version history is the other half of the record. Each version names who saved it and the roles a run of it presents, keeps the test report from its save, and carries the change summary when it changed what the automation does. It also carries the account of the dry run that preceded it, if there was one. A version nobody dry-ran is called out as code that first executed unattended.

A script’s Version history in the portal: each version with its author and the plain-language summary of what it changed, an older version expanded to show the roles a run of it presents and a Compare with control.
Version history sits under the source. Each version names its author and the roles a run of it presents, which are the roles captured at that save. The test report from the save is on the Tests tab, and the account of the dry run that preceded it is attached when there was one.

What a version keeps from its save

Who saved it, and their roles
The author and the roles they held at the save, which are the roles a run of the version presents.
The test report
How each test did at the save and the share of statements they reached, with the lines no test reaches. The Tests tab shows it, and the flow diagram marks each step the tests miss. A version saved without a source change, such as an ownership transfer, keeps the report of the version before it.
The change summary
When the version changed what the automation does: the plain-language summary, and when the person agreed to it.

What a dry-run account holds

Who ran it, and when
The author’s own identity, under their own persona.
How it ended
Succeeded, or the failure and the line it failed on.
What it printed
The bounded log, head kept, tail dropped with a marker when it overflows.
The shape of its outputs
Each export’s format, row count, and size, measured without being written.
Which source it executed
The account is keyed to the exact source, so it attaches to whichever version later carries that code. A version with no account is code that first executed unattended, and the version detail says so.

State, and what a failed run keeps

An incremental job carries a watermark or a cursor from one run to the next in its state. Whether that state moves when a run fails decides whether the next run repeats work or skips it, so the rule is explicit: saved state moves only on success, and a checkpoint moves however the run ends.

State, and what a failed run keeps

  • platform.save_state(obj)

    Committed only when the run succeeds.

    Applied in the same step that marks the run succeeded, with a compare-and-set on the revision the run read. A failed run leaves the state where it was, so a watermark never moves past work that did not happen.

  • platform.checkpoint(obj)

    Committed however the run ends.

    Stages progress the platform keeps when the run fails or is halted at its time limit, so a pipeline that merged half its pages does not lose its place for all of them. A successful run commits its save_state, or its last checkpoint if it never called save_state.

  • A checkpoint that loses the compare-and-set

    The run stays failed for its own reason.

    Another run wrote the state first. The checkpoint is refused and the refusal is recorded beside the run’s own failure, which it does not replace.

  • platform.remaining_ms()

    How long the run has left.

    A script has no clock, so this is how a long job decides to checkpoint and stop before its time limit rather than be halted mid-page. The value is recorded with the run, so a test replays it exactly.

  • fail() after save_state

    A warning at the save, not a refusal.

    A failed run’s save_state is discarded, so a function that saves state and then fails never writes it. The save says so, and a draft run reports the staged value as discarded rather than saved.

State is one JSON object per script, at most 64 KiB, for either call. It belongs to the script rather than to a version, so it survives a new version, a disable, and an ownership transfer. The run page labels state that came from a checkpoint, and the owner’s failure notice says how far the failed run got.

A checkpoint on the run page

A run that records progress as it goes shows it on the run page. The run below swept ACME’s eight regions and checkpointed after each one; had it failed partway, the regions it finished would still be in its state for the next run.

Run history of the ACME demo automation acme-restock-sweep with its succeeded run expanded: requested by, computed against, cost, no parameters, State read (revision 0) as an empty object, Checkpoint saved (revision 1) listing regions_done from CE through WE, and the run’s result with low-stock items and units to order per region.
A run of the ACME restock sweep, which checkpoints after each region. It succeeded without calling save_state, so its last checkpoint became the saved state, and the run page labels it as a checkpoint rather than as saved state.

The administrator’s view

An administrator sees every script and every run on the deployment, on the same pages owners use, with an owner column and two panels the owner view does not need: the busiest scripts and the fires that were missed.

The administrator’s All scripts listing: every script on the deployment with its owner, schedule, next fire, and how its last run went.
All scripts, with the Owner column added to the listing owners read on their own Scripts page. A script with no owner reads nobody and is visible to administrators alone; one with no cadence reads On demand.

Every run, across every script

The Runs tab is where a missed fire becomes visible, because a fire that did not produce a run has no row anywhere else. Run history is kept for a year, and older history is still reachable one script at a time.

The administrator’s Runs tab: run counts and durations across every script, panels for the busiest scripts and for missed fires, and the fifty most recent runs.
The Runs tab answers what the run table cannot: a missed fire is a run that does not exist. Busiest scripts and Missed fires each link to the script concerned, and the list shows the fifty most recent runs and says so when it fills. Run history is kept for a year.

What is settled

Unattended never means unaccountable. A run does what its author could do, is refused what its author could not, cannot reach anything the platform does not mediate, and leaves a record that names the version, the trigger, the outputs, and every call.

Key terms

Nine terms cover the authority and the record of an unattended run.

Key Terms

Script principalscript:<name>
The identity a run authenticates as. Audit records it, and the run’s outputs belong to it. It carries the author’s address so the run can act on what the author owns.
Captured roles
The roles the author held when the executing version was saved, stored on that version and presented by every run of it. They cannot be set any other way.
Run gate
The check that admits or refuses a run. It refuses a script taken out of service (disabled, deprecated, or superseded), in its own words on every surface. A library is never run at all.
Dry-run account
The record a dry run leaves of itself: who, when, how it ended, the bounded log, and the shape of the outputs, keyed to the exact source that executed.
Change summarychange_summary
The plain-language account of what a new version will do differently, which the person the automation runs for agrees to before a behavior-changing save goes through. It is kept on the version with who saved it and when.
Test report
What a version’s tests found at its save: each test’s result, the share of statements reached, and the lines no test reaches.
Librarylib:<name>@<version>
A script with no main(), held to the same checks, that other scripts load at a named version. It makes no platform calls, is never run or scheduled, and anyone may load it.
Checkpointplatform.checkpoint
State a run stages that the platform commits however the run ends, so progress made before a failure or a time limit is kept.
Ownership transfer
An administrator’s move of a script to another person, recorded as a version the administrator authored, after which runs present that administrator’s roles.