A full scan lists deploys made outside the dashboard from the tool's own history
Decision record 0073
Amended by 0076: an outside deploy that ended after a stack’s failed deploy clears that row’s failure line. The trail keeps both lines.
Record 0016 made deploys outside Sluiceway legal and left them undetected, and rejected reading pulumi stack history because all it would improve was the list of commits on a row. Its note of 2026-09-21 names a second benefit the owner asked for: a trail that lists every deploy, not only the dashboard’s own. The research on Pulumi’s update history (branch research/pulumi-update-history) found that a history entry says what went out, when, and from which commit, that it names the GitHub Actions run it ran in, and that it does not say who ran it on a self-managed backend. Build plan slice 5.6 brings it in.
This amends 0016 (outside deploys are now listed, and still legal and never stopped), 0029 and 0062 (the trail holds more than deployment records).
Decision
- An optional adapter method,
deployHistory. It reads the tool’s own history of one stack and hands over its deploys, newest first:deployordestroy, when it ended, the commit that was checked out and whether the tree held changes in no commit, and the GitHub Actions run it ran in. A deploy is an entry that went out (succeeded), is anupdateor adestroy, and changed something (a count other thansame). A failed run, a refresh, a preview and a deploy that changed nothing are left out, as a failed deploy was left out of the first trail (0029). The shape has no field that can hold a value, a message or a person. - Pulumi only.
pulumi stack history --json --page-size <n> --stack <name>, from the stack’s directory, with the job environment minusINPUT_*(0013).--json, not--output json, which v3.229.0 does not know. Never--show-secrets, so no passphrase is needed. It takes no stack lock and adds no entry. OpenTofu, Helm and kubectl keep no history of their deploys (OpenTofu’s state serial says that something wrote the state, not what or when), so their adapters leave the method out, and the scan says in its job log which stacks it could not read and why. - The schema drops what is not needed, whole. Of an entry only
kind,result,endTime,resourceChangesand fourenvironmentkeys are read:git.head,git.dirty,ci.systemandci.build.id.configholds every plain config value, so it is never parsed (0021).messageis anybody’s words,git.authorandgit.committername the people behind the commit and not the person who deployed, and their e-mail addresses do not belong on an issue. A commit that is not a whole commit id and a run id that is not digits are left out, not passed on. Stdout never reaches the job log: only stderr is the tool’s words (0022). - Full scans only. It is one more backend call per stack, so a scan reads the histories once every stack is previewed, through the same pool, with each stack’s preview time limit. A narrowed scan reads none. A scan after a merge that narrows reads none. A read that fails is a warning on the run, the tool’s words in a log group of their own, and the stack keeps the lines the dashboard had. The scan stays green: the trail only explains.
- As many entries as the trail lists.
--page-sizeisdashboard.recentlyDeployed. With0nothing is read. The page counts every entry, so a stack with many refreshes since its last outside deploy may not reach it. A longer page reads more small objects from the backend for lines the trail would cut anyway. - Sluiceway’s own deploys are told apart by the run. A deploy whose run id is the run on a deployment record of the same stack is Sluiceway’s own, and the record already makes its line. Every other deploy is an outside deploy: one from a laptop or a script has no run id, one from another workflow has another run id. The run on the record is the run id of the
applyjob, which the tool writes into its history because Sluiceway hands it the job environment. Every record of the stack that this version can read counts, whatever its result. - The line says when and from which commit, never who.
- network:dev · deployed outside the dashboard, from commit 59ff6e7 · 2026-09-21 18:11 UTC, the commit linked to its page in the repo,with uncommitted changesafter it when the tree was dirty,destroyedfor a destroy, and no commit when the tool recorded none. Under a header it has the green result dot of a deploy that went out. No run link: the run page names the person who started it, and the line does not point at people. The time is the other machine’s clock, shown as every trail time is (0029). It sits in the one list by time and counts towarddashboard.recentlyDeployed. - Every writer carries the lines in a marker. Only a full scan can read the history, and
resolve,applyand a narrowed scan hold no credentials or no reason to. So each line ends in<!-- sluiceway:outside stack="..." kind="deploy" at="<ISO>" commit="<sha>" dirty="true" -->, every writer reads it back withparseDashboardand draws the line again, and a full scan replaces the lines of every stack whose history it read. A stack the repo no longer has loses its lines. A marker a person broke is not read. Of two lines for one deploy the first stays.
Consequences
- A full scan costs one call of
pulumi stack historyper Pulumi stack: about 0.1 s on a file backend (research), more on a bucket, where the tool lists the whole history directory of the stack. - An own deploy whose deployment record is no longer on the page of the newest 100 records per environment (0003) would be listed as outside. By then a hundred newer records of its environment exist, and the trail’s length cuts the line in nearly every repo.
- A workflow that runs its own
pulumi upon the same stack in the same run as Sluiceway’sapplyhas its deploy taken for Sluiceway’s. That is an unusual workflow. - The commit link points at this repo. A commit that was never pushed has no page there.
- Attribution still starts at the last successful deployment record (0026). An outside deploy does not move it, so a pending row may still list commits that are already live, as 0016 says.
- State surgery (
pulumi stack import,pulumi stateedits) adds no history entry and stays invisible, as before. - A body written before this version has no outside lines. The next full scan adds them.
Rejected
- Naming who deployed. A self-managed backend does not record it.
git.authoris the author of the last commit, often not the person who ran the tool. Pulumi Cloud’srequestedByis only in its REST API, which would be a network call outside the tool (0001, build plan section 1). - A run link for a deploy from another workflow. It points at the person who started the run, and Pulumi builds the link with
github.comeven on a GitHub Enterprise server. - Change counts on the line. The dashboard’s own lines have none, and the history counts resources by the tool’s ops, which are not Sluiceway’s ops (0007).
- Reading the history on every scan, or only for stacks whose
lastUpdatemoved. The first costs a backend call per stack on every push. The second needs a value stored per stack and a second command, for a saving the research could not measure on a real bucket. - Matching by time. One second of precision, a clock Sluiceway does not control, and two deploys in one window cannot be told apart.
- An adapter method on OpenTofu that reports the state serial. It says that the state changed, not that something was deployed, when, or from which commit.
Settled while building (slice 5.10)
- The line drops the word
commitand takes the short time of the trail (0029):- network:dev · deployed outside the dashboard, from 59ff6e7 · 09-21 18:11. With a stack id of 40 characters that fits on one line at GitHub’s issue width, and withcommitit did not. A line withwith uncommitted changesafter the commit still takes two lines at that length; no shorter words said it as plainly.