Not in v1
One list of everything that was considered and left out of v1, so planning for v2 starts from a complete picture. Each line says what it is, why it is not in v1, and where that was decided. The decision record holds the detail; this file only points.
Rule: whenever a decision leaves something out of v1, add a line here in the same change.
Deferred, door left open
Left out of v1 on purpose, and v1 was shaped so these can be added without a breaking change.
| What | Why not in v1 | Decided in |
|---|---|---|
| A line on the dashboard when the open pull requests could not be read | The section shows what an earlier scan listed, which is still true, and the run’s warning and the summary say why the list is old. A line would need a place in a section that may be empty. | 0119 |
The line of a failed run from the resolve, apply and settle jobs of the split workflow |
Their docs put if: ${{ !cancelled() }} on the scan job’s step alone. A resolve job holds no credentials and rarely fails before Sluiceway, and the next scan counts any run that failed. Adding the if: to the other jobs needs no change to the action. |
0120 |
| The reason of a failed drift check on the row | The row says drift not checked and stays quiet. The reason is the tool’s, and the warning and the job log name it. |
0121 |
| A second try for a preparation that failed, such as an init that could not reach a registry | The second try of 0117 is for previews. A preparation runs alone and before the pool (0053), one for every stack of a directory, and trying it again is a change to that order with a cost of its own. Such a stack is a preview failure that the next scan tries again. | 0117 |
| Calling a deploy busy when it could not start because another update holds the stack’s lock | A Pulumi deploy, a Helm upgrade and an OpenTofu apply each say so in words of their own and with the exit code of any failure, so the failure line reads the tool exited with an error. A reason of its own needs the deploy reasons of 0022 looked at as a whole, and a recording of each tool. |
0117 |
| Recordings of a held state lock through Terragrunt and cdktf, and of a preview against Pulumi Cloud while an update runs | They go through the code that reads tofu’s and terraform’s diagnostic, which is recorded for both. Terragrunt may wrap the JSON log, and then the stack reads as a tool error, tried twice, as before 0117. The recorder has no Pulumi Cloud account; its docs name a preview as the run that does not collide with an update. | 0117 |
| A break-glass tick that deploys outside the deploy window, visible on the trail and with a permission of its own | A window holds a tick until it opens; the person who must deploy at once widens the stack’s window with stacks[].deployWindows: [] in a reviewed change, or waits. Who may break the glass, and how the trail says it, is a decision of its own. |
0104 |
The check warning about a split workflow whose resolve job does not run on the schedule while a stack has a deploy window |
Without it a window opens only when a dispatch happens. The split workflow page says to add schedule to the job’s if:, and the check does not read that if: yet. |
0104 |
The soft policy failure: a deny a repo marks soft keeps the box and asks for a second person to tick |
It needs a new row state, two tickers walked from the edit history and a record carrying both, and that is where a bug about who may deploy would live. Record 0020 rejected a built-in second approval as a general setting; the owner keeps the door open for this one shape, built when a team asks for two signatures. | 0106, 0020 |
The check listing the policy paths of each stack, saying when one is not in the repo, and warning about a workflow with policies and no step that installs conftest |
Slice 5.41 (0106) says each on the row and in the job log at the scan. The check reads files, and which step installs conftest is any run step. |
0106, 0061 |
Checkov as a second policy runner, the program’s files as a policy input, and running the policies again in apply before the deploy |
The document and the runner seam of 0106 are made so a runner can follow. A tick approves the preview, not the source. apply holds the deploy to the hash of the preview the policies passed on. |
0106 |
| Policies on the pull request preview | The check runs the preview for a reviewer (0101), and its pages could carry the policies as a scan’s do. | 0106, 0101 |
The check reading what a secret loader action exports, by the names its with: or env: map, and the names a run step writes to GITHUB_ENV |
Each action has a rule of its own, and a script can write any name. Slice 5.34 (0099) names such a step as one the check cannot see into, which is honest, and reads an env file of the repo that a step loads for its names. | 0099 |
| More rows in the check’s table of credentials: every provider and backend the tools know, a region or a project for clouds other than AWS, the repository or registry of a Helm chart reference, and the packages of a Java Pulumi program | The table of 0099 holds the clouds and services the docs name and the ones a first repo meets. A provider or a backend without a row is said as such, never guessed. Rows are added as repos ask for them. | 0099, 0013 |
| A per-job table of credentials in the check’s summary, and the same reading in a scan’s summary | The summary gets one line per need nothing names, and the job log lists which way each job gives. A scan holds the credentials and runs the tool, which says for itself what it lacks. | 0099 |
| A result file field and an output that say a deploy went out on merge | The result file keeps ticker as the name on the record, whoever merged for a deploy on merge. Its schema is published, so a field is a change of its own, and the trail, the row and the summary already say it. |
0095 |
| Deploying on merge from a scan that no push started, such as the schedule finding a change whose push scan failed its preview | There is no merge to attribute it to, and a change the scan of its merge could not preview is one a person should look at. It waits for a tick, and its row says why. | 0095 |
tickers judging whoever merged a stack set to on-merge |
It would cost a permission lookup in every scan of a merge and refuse every app that merges. Who may merge is the default branch’s rules, and a stack only named people may deploy stays on a tick or gets an environment with required reviewers. | 0095 |
| Saying on the dashboard whether the tick rule or an environment decides who may deploy, and the check reading whether an environment has required reviewers | Both need the environment’s rules, which only the GitHub API answers. The check’s promise is no GitHub call, and the dashboard has no place that explains the tick rule. The docs and the check’s line say it from what the files show. | 0093 |
init for the Terraform family: declaring Terraform root modules, Terragrunt units and the stacks of a CDK for Terraform app, and their setup steps (setup-terraform, terragrunt, cdktf) |
init reads a directory of .tf files as an OpenTofu root module. Telling Terraform from OpenTofu needs a hint from the repo, a lock file or a version pin, and a CDK for Terraform app’s stack names are only in its program. |
0068, 0065 |
dependsOn: auto from a Terragrunt unit’s dependency blocks, and zero-config discovery of Terragrunt units from terragrunt.hcl |
Terragrunt reads its own dependencies. A unit is declared with wrapper: terragrunt, and a dependency is named in dependsOn by hand. |
0068, 0059 |
varFiles on a Terragrunt unit or a CDK for Terraform stack |
A unit passes its var files in its own terragrunt.hcl, and an app sets its variables in code. A var file would be relative to a directory Terragrunt copies the code into, or that synth writes. |
0068 |
Root module discovery, part 2: a root module that reads terraform.workspace, has var files of its own or a cloud block with tags, found once per workspace or var file; root modules in a repo with Terragrunt files; a module source that is a Git address of the same repo |
Slice 5.29 (0092) finds a root module only when its files name one stack in the default workspace, and finds nothing in a Terragrunt repo. Workspaces live in the backend, a var file is chosen by a person, and a Terragrunt source can be built at run time, so each would be a guess, and a wrong guess is a row for something that is not a stack. stacks entries declare them. |
0092, 0053 |
A secretsProvider option for the stack createInBackend makes, and the salt it writes committed by Sluiceway |
Slice 5.42 (0107) runs pulumi stack init with the name alone, so the tool’s default secrets provider stands and the passphrase is the job’s, and Sluiceway commits nothing to a repo. Nobody has asked for another provider. |
0107 |
A backendConfig option for OpenTofu (tofu init -backend-config) |
Workspaces and var files cover the layouts the research found common, and backend settings go through the environment. It would also mean one init per stack, not per directory. | 0053 |
Helm, part 3: a kubeContext option, zero-config discovery from Chart.yaml, and --take-ownership for objects made outside the release |
Slice 5.2 built part 2: the drift check, createNamespace, the deploy flag per helm version and nested local dependencies (0069). Each of these is a named option that no user has asked for yet, and a kube context goes through the workflow and the environment today. |
0058, 0069, 0015 |
| Pruning of a Kubernetes manifests stack through kubectl’s own ApplySet, and drift in fields its manifests do not set | Slice 5.3 prunes from an inventory of Sluiceway’s own, because the ApplySet is alpha and kubectl diff cannot show what it would prune. A field someone added that the manifests do not set is never removed by a server-side apply, so a tick could not repair it. |
0070 |
| Zero-config discovery of kustomizations, and a hint in the check for a directory of manifests that no entry declares | A directory of YAML says nothing about its cluster, and many are bases that are never applied alone. tool: kubectl entries declare stacks. |
0060, 0042 |
init, part 2: a credential step for a secret manager other than 1Password’s env file of references or for a cloud through OIDC, a setup action for languages other than Node, a check workflow next to the starter workflow, adding to a sluiceway.yaml that is there, and Kubernetes manifests stacks |
The first version writes only steps it found the makings of in the repo and never invents a credential step. Each of these is a template that needs a real setup to copy. | 0065 |
Several files in one env-file input, ${NAME} expansion inside a value, a comment after a value on the same line, and a value list without quotes across lines |
One file, one strict format, and a line that does not fit is refused by its number. Two files raise which wins on a name both set, expansion would read the job environment by name, and an inline comment is a guess about a # in a password. A step before Sluiceway can join files or expand names. |
0100 |
| Resolving a secret reference in the env file, for any secret manager | Sluiceway fetches no credential (0014, promise 1). The secret manager’s own run or inject resolves the file first, with its own token on that step. |
0100, 0014 |
Credentials that only read in scans, and an environment per stack, without an if:: resolve dispatches a second workflow once per deploy, whose one job names the stack’s environment, loads the credentials that write and deploys that record |
One job previews and deploys with one set of credentials. The split workflow keeps both, with if: and needs:. A dispatch is the one event the workflow token may start, so this would work, and it needs a second workflow file, a dispatch input per deploy and a new mode of resolve. |
0077, 0014, 0020 |
| One set of outputs and one result file per deploy when one step deploys several stacks, and deploys side by side in one step | The one step deploys in turn and sets outcome, stack and result-file once per deploy, so a step after it sees the last one. The step is red when any failed, and the split workflow has one job and one set of outputs per deploy. |
0077, 0041 |
| A check in auto mode that knows a repo’s issue edits are many and suggests the split workflow | The check reads files. How often issues are edited is not in a file. | 0077, 0061 |
| An AWS CDK and CloudFormation adapter (change sets as the preview) | Fits the model, a different format from plan JSON, a large all-AWS audience. Wanted, waiting for a user who asks. | 0006, 0007 |
| A branch preview of updates past the oldest 30, and a branch preview carried over by a scan that did not preview its stack | Each is one more run of the tool per stack on every scan that lists the update. The oldest 30 are the ones every dashboard lists, and a narrowed scan runs them again because the code under the branch may have moved. | 0071 |
Reading Renovate presets from npm, a web address or another platform, presets with parameters, and packageRules, for the merge method |
npm and a web address would be a network call that is not GitHub’s. Parameters need Renovate’s templating. Which package rule matches depends on the package manager’s view of the update. Presets in this repo and in GitHub repos the workflow token can read are followed. | 0071, 0064, 0054 |
| Waiting, in the scan after a merge, for a dependency that became pending after the merge | resolve refuses to merge for a stack whose dependency has a change waiting or deploying. A change that lands on the dependency in the minutes between the merge and its scan is not waited for, and the merged change deploys first. |
0054, 0056 |
A warning from the check when mergeAndDeploy is on and the workflow does not declare the sluiceway-merged input |
Without it the scan after a merge is full, which is correct and costs more. The job log of resolve says which input to declare. |
0064, 0061 |
| The counts on a deploying row’s marker, and the drift counts on a pending row’s marker | A deploying row copies destroys and deletes from the row it replaced and shows no counts, so a reader has nothing to draw from them yet; a pending row that also shows drift carries drift="true" and neither gone nor changed. Both are one more key each, under the rule of 0009, if a reader asks. |
0110 |
| Drift detection, part 3 | Part 1 (slice 4.3, 0055) and part 2 (slice 4.7, 0059) are built: drift.enabled and stacks[].drift.enabled, the check in scheduled scans and in runs a person starts, the Drifted section, the drift state and marker key, one hash over both diffs, a preview page for a drifted stack, a tick that repairs, and the trail line that says so. Left: a drift check for OpenTofu (resource_drift of the plan, which 4.1 ignores); values of drifted paths through dashboard.showValues; a drifted step output and a drifted count in the result file’s dashboard counts; the note about shortened rows counting drifted rows; a drift check in the scans that Sluiceway dispatches itself. |
Brief M3, 0009, 0055, 0059 |
| Stack dependencies, part 3 | Part 1 (slice 4.4, 0056), part 2 (slice 4.7, 0059) and phases (slice 4.16, 0067) are built: dependsOn as stack ids or auto from Pulumi stack references, phases with a phase per stack or read from a key of the Pulumi project file, the refused tick, one layer per run, queued rows, and the check listing them. Left: a phase read from the files of another tool, such as a label in Chart.yaml; a queued stack that is ready while no resolve run is coming says so on its row instead of only waiting for the next run; transitive waiting (a stack waits only on its own dependencies); a deploy of several layers in one run; auto for OpenTofu, from its terraform_remote_state data sources. |
Brief M3, 0009, 0056, 0059 |
A named refresh option (preview, re-preview and deploy with refresh on every run of a stack) |
The drift design (0055) repairs drift without it: a deploy runs with refresh only when the approved hash covers drift that apply has just checked again. A stack that wants refresh on every preview would still need this, as one named option on all three calls so the hash stays honest. |
0015, 0055 |
| Showing property values on the dashboard or in the summary without a list | Property paths are in v1 (0046) and scan.logDiff shows values in the job log (0048). Built in slice 2.19: dashboard.showValues, an opt-in list of paths whose values appear (0052). Without a list, values stay out. |
0021, 0007, 0008, 0046, 0048, 0052 |
A finer dashboard.redact (per stack, or a middle level such as types without names) |
One boolean is enough to launch. | 0023 |
| Teams in the tick rule | The workflow token cannot read team membership, and a second stored token breaks the credential promises. The org/team syntax is reserved and fails with a clear message. |
0018, 0017 |
| A GitHub App token or PAT as the bot identity | One identity in v1. It would arrive as a new optional input, together with an identity input and a loop guard. The owner asked on 2026-09-21 for the dashboard to be written by a Sluiceway bot instead of github-actions[bot]. That is this item: a bot with its own name and picture is a GitHub App, and only a token minted from the app’s private key can act as it. With the user’s own app the key is the user’s secret and nothing else changes here. With one shared official app the key is ours, so a service has to mint the tokens, which is a backend and belongs to a hosted version. Either way edits made with an app token do start workflows, so the loop guard comes with it. |
0017 |
| Spreading one scan over several runners (sharding) | One job with a bounded pool is enough for now. A shard would be a narrowed scan, so no new merge rule is needed. | 0011, 0012 |
A full scan whenever sluiceway.yaml changes, also in a repo with a stack at the root |
The file lies outside every stack directory, so changing it is a full scan with no special case. A stack at the repo root claims every file, and there a push that turns dashboard.redact on rewrites only the rows it previews. The carried rows keep their names until the next full scan, which the rescan box gives at once. |
0010, 0023, slice 1.12 |
| Treating the files of an ignored stack as unrelated | An ignored stack claims nothing, so a change in its directory has no claimant and gives a full scan. Another stack may read those files, so dropping them by default could leave a row stale. scan.unrelated takes the directory when nothing reads it. |
0010, slice 1.12 |
| Links to the job, not the run, on the rows written from a deployment record (deploying rows, failure lines, Recently deployed) | The record holds its run and no job (0003). apply knows its own job’s id, and the log address on its deployment status could carry it, which a scan would then have to read. |
0044, slice 2.14 |
A link that lands on one stack’s group in the job log (#step:<n>:<line>) |
GitHub supports the address, but the step’s number and the line’s number are not the action’s to know, and a wrong one lands on another stack. | 0044, slice 2.14 |
| The carried rows in the summary of a narrowed scan | The summary shows the stacks its run previewed. A carried row links to the summary of the run that previewed it. | 0011, 0037, slice 1.12 |
An empty tickers list, as a way to get a dashboard that nobody can tick |
Config loading refuses it, because in a rule about who may deploy an empty list is far more likely a mistake than a wish. A read-only mode got its own key, dashboard.readOnly (0045). |
Slice 1.2 |
A scan that works out by itself that its workflow has no resolve job, so that dashboard.readOnly is not needed |
It would have to read and understand the workflow file, its triggers and its jobs, which the workflow token can do but which breaks the moment a repo splits the jobs over two files. | 0045, slice 2.17 |
dashboard.readOnly as a lock: resolve refusing a tick while it is on |
What keeps a read-only workflow from deploying is that it has no resolve or apply job. A second lock in the config would be one more thing to keep in step. |
0045, slice 2.17 |
| The stacks a narrowed scan carried, in the result file of a scan | The scan’s file lists what its summary lists, the previewed stacks. They can join as a new field. Attribution, the apply’s timings and the published schema joined in slice 4.10 (0061). | 0041, slice 2.11 |
Who made a deploy outside the dashboard, from Pulumi Cloud’s requestedBy |
A self-managed backend does not record it, and on Pulumi Cloud only its REST API has it, which is a network call outside the tool with the user’s access token. The trail says when and from which commit (slice 5.6). | 0073 |
| Deploys made outside the dashboard for OpenTofu, Helm and kubectl stacks | Their tools keep no history of deploys. OpenTofu’s state serial says that something wrote the state, not what, when or from which commit, and reading it prints the whole state with every value. | 0073, 0016 |
Reading the tool’s history in a narrowed scan, gated by pulumi stack ls and its lastUpdate |
One call per project would say which stacks’ state moved, but needs the last value kept per stack and moves on a refresh too. Full scans read every history instead, and a narrowed scan carries the lines. | 0073 |
Placing the orphan tick note in the order of record 0027 when resolve adds it |
resolve has no diff and never reads a row’s text, so it cannot tell an attribution line from a delete line. It puts the note right under the first line. The next scan renders the row in the fixed order. |
0025, 0027, slice 2.4 |
| Carrying on with the other records after one deployment record could not be written | A missing deployments: write would cost 256 failed requests. resolve stops at the first failure, hands on what exists and goes red. The ticks that are left stay ticked for the next run or the orphan sweep. |
0035, slice 2.4 |
A scan that carries a ticked rescan box while a resolve run is on its way |
A scan writes the rescan box unticked, as it always has. A narrowed scan that lands between the tick and its resolve run loses the request without a word, and the person ticks once more. Carrying it would be a change to the scan, with the run lookup of the orphan sweep. |
0025, slices 2.4 and 2.7 |
The scan clearing an orphan tick with clearTick in place of a preview |
Slice 2.4 built the way to clear a box on a block without a diff (src/render/clear-tick.ts). The scan still previews the stack, which also puts the note in the order of record 0027. Switching is a choice between one preview and the place of one line. |
0004, 0025, slices 2.4 and 2.7 |
| Telling a full edit history that lost nothing from a capped one | GitHub keeps 100 entries and says nothing about what it dropped, so a history of exactly 100 entries is read as capped and its oldest entry is never used. That can cost one fresh tick on the one day a dashboard has exactly 99 edits, and never names a wrong ticker. | 0025, slice 2.2 |
Looking past the newest 100 runs that an issue edit started, when a scan asks whether a resolve run is on its way |
One request, newest first. A run that is not over and has 100 newer ones behind it started its resolve job long ago, and a waiting run is always among the newest. The worst case is one tick cleared that has to be made again. |
0025, slice 2.7 |
| Sweeping an orphan tick off a row without previewing its stack | Only the row renderer writes a row, and a row block is never patched inside. So an orphan tick on a row the scan carries costs one preview. If resolve gets a way to clear a box on a block it has no diff for, the scan can use the same. |
0004, 0025, slice 2.7 |
The orphan tick sweep in a repo that keeps scan and resolve in two workflow files |
The scan asks for the runs of its own workflow, as the record says. The README’s one workflow is the only supported shape. With two files the scan sees no run on its way and may clear a tick a moment before resolve reads it, which costs a fresh tick and never a wrong deploy. |
0025, slice 2.7 |
A real uses: step against the fake GitHub server in the e2e workflow |
A runner sets GITHUB_API_URL, GITHUB_WORKSPACE, GITHUB_SHA and GITHUB_EVENT_NAME itself, and neither a step nor a job can replace them (tried on a hosted runner). A real step could only reach the fake through a new input or variable for the API address, which would be a product feature that exists for a test. The e2e starts the committed bundle itself, with the environment a runner would build from action.yml, and the smoke job keeps a real uses: step that stops at a config error. |
Slice 1.13 |
| Starting a deploy without a person ticking, through an input of Sluiceway’s own (unattended deploys of chosen stacks) | Since slice 5.44 a writer that recordWriters names opens the deployment record itself and dispatches the workflow (0109), and GitHub records who opened the record and who dispatched. A workflow_dispatch with a stack input, where Sluiceway opens the record, would be a second way to say the same thing. Not a bot ticking boxes. |
0018, 0109 |
| Telling authors that their merge is waiting for a deploy | An @ mention in the issue body notifies and subscribes every author, with no way to opt out. If it comes, it is an opt-in setting or a comment on the pull request, not a mention on the row. |
0026 |
| The files of each change outside a stack, in the fold that names them | A path can name a stack with redact on, and every path costs room in the body. The pull request’s hover card and the compare link show the files. | 0072 |
| Looking past a failed record for an older success to start attribution from | The REST fall back of 0003 reads only the newest record of a stack. Such a row reads not deployed from this dashboard yet until the next success. |
0026, 0003, slice 2.8 |
| Attribution for a scan of a commit that is not on the default branch | The walk follows the scanned commit, so a branch’s own commits read as direct pushes. v1 scans the default branch. | 0026, slice 2.8 |
| A recently deployed list longer than 50 lines | Past 50 the list would need more than the one page of records every writer reads, and it takes room that pending rows need. The trail of a whole year is GitHub’s own deployments view. | 0062 |
| Reading a deployment record whose payload has another version | This version reads v: 1 and leaves every other record alone, open ones too, and says so in the job log. One workflow pins one version of the action, so two versions writing records in one repo is not a case v1 plans for. |
0003, slice 2.1 |
Finding a stack’s records after its environment label changed |
The page is read per environment name that a stack uses now. A record under the old name is only found by the fall back, which looks for the task alone. For a stack that is in sync, a failure line from before the change can go missing. No deploy depends on it. | 0003, slice 2.1 |
| A failure line on a row that a narrowed scan carries through | A carried row is never read inside, so it keeps the failure line it has, or has not. apply writes that row itself, settle writes it with the failure line and starts a full scan (record 0113), and the next preview of the stack brings it in line. |
0004, slice 2.1 |
| Allowing for a runner clock that differs from GitHub’s | The scan compares the time of a deployment status, which is GitHub’s, with the start of its own preview, which is the runner’s. With a clock that is off, a scan can keep a live row it did not have to, or take its own row a moment too early. Both are repaired by the next scan, and no deploy depends on either. | 0004, slice 2.1 |
| A shortening level that also drops a row’s links, for more than about 100 stacks pending at once | Past what one issue can usefully show. The scan fails with a clear message instead. | 0028 |
| A link to the summary in the note about shortened rows | A row that a narrowed scan carried through links to the summary of an earlier run, so one link would be wrong for some rows. Every shortened row holds its own link. | 0028, slice 1.8 |
| A finer personality switch (the header without the voice, or the reverse) | One boolean, dashboard.personality, is enough to launch. |
0034 |
| A custom header image, or a palette setting | The header is Sluiceway’s brand, and a user image brings back every file rule as something to validate. Turning it off is the v1 answer. | 0034, 0033 |
| A drift picture with crates, or drift above pending in the header | The drift picture (built in slice 4.3) is a closed gate with nothing waiting, so a pending row wins over it and the counts line says N drifted. Its crate count is therefore always 0, and slices 4.15 and 5.8 gave the count to failing, deploying and queued only. A picture per crate count with a seep only makes sense together with drift above pending. |
0055, 0038, 0031, 0066 |
| A header picture for a scan that is running (the crate on the quay) | A scan running is not a state of the stacks, the header shows one state and bad news wins, and a new picture set is about ninety files. The line under the scan line says it. | 0108 |
| Taking the scan-running line back when a scan fails inside Sluiceway | One more write on the failure path for a line whose link shows a red run and which the next scan replaces. Would make the line true only while a scan is alive or died hard. | 0108 |
| Levels for deploying or failing (how many are deploying, how much failed) | One deploy and five look the same from the quay, and one failure is already the whole message. A level is new files and a new line in the table. | 0039 |
| More than 20 exact crates | Slice 5.8 raised the maximum from 12 to 20 with a second and a third tier on the pile. More would stack the pile into the coping at the top water step, and a count past 20 is not read on a phone: the overflow picture says more than 20, and the counts line has the number. Every count more is 16 more files. | 0047, 0075 |
| The destroy sign painted on the wall right of the wordmark, or on a pole at the far right | The pole right of the wordmark won. The far right crowds the reeds and the edge. | 0047 |
| The overflow as a pile running off the edge | A half crate at the edge says the same with less. | 0047 |
| Count dots as small images in the brand colours | GitHub wraps every image in a link, so each dot would be clickable, and they are more files that say nothing an emoji does not. | 0040 |
| A cap above 10 KB per header file | The fullest file, deploying-more-deletes-replaces-dark.svg, is 363 bytes under the cap, after slice 5.8 drew the water once per file. Final art that needs more asks the owner, with a reason. |
0039, 0066, 0075 |
| The header generator in this repo | The files are generated by prototype code that lives in the private lab repo. It moves here, tested, when final art is settled. | 0038 |
| Header images for a fork of the action, or for an action repo under another name | The image URLs name sluiceway/sluiceway. A fork that runs at its own commit SHA points at a commit the public repo may not hold, and shows a broken image. dashboard.personality: false is the v1 answer. If it comes, the glue hands the repo over from GITHUB_ACTION_REPOSITORY. |
Slice 1.7, 0033 |
| Final art for Penny | The files in assets/mascot/ are generated concept art after one polish round, good enough to ship a first release with. Final art may redraw every shape and keeps the character, the colour pair, the composition, the states, the crates and water steps, the destroy sign and the file rules. |
0030, 0033, 0038, 0039, 0043, 0047 |
| Showing a change that touches only a stack’s outputs | pulumi preview --json does not report it. It needs a second parser for the tool’s event stream. The shape is fixed already: one update change per stack with the output names as changed keys. Until then such a stack is in sync and is deployed from outside. |
0036, 0007 |
| Deploying the exact plan that was previewed, for Pulumi | Pulumi’s update plans are still experimental. OpenTofu deploys the plan file its fresh preview saved and hashed since slice 4.1 (0053). For Pulumi the hash check on a fresh preview is the answer. | 0001, 0008, 0053 |
| A size budget level for the summary beyond its two, or the full diffs as a file to download | The job log already holds every diff in full. | 0037 |
The count of changes outside a stack, and earlier changes and the compare link in the summary |
The row carries them already. The summary adds only what a row has no room for: every claimed pull request and direct push, with its title. | 0026, slice 1.9 |
| A link from the summary’s note to the stack’s group in the job log | A job’s page has no address that can be worked out without an API call, and a group has none at all. The note names the run’s job log and the title of the group. | 0037, slice 1.9 |
| A live example dashboard in this repo | CI tests the loop against a fake GitHub, so nothing writes a real issue here. A public demo is launch work. | Build plan |
A setting for which directories discovery never enters, or reading .gitignore for it |
Discovery skips .git and node_modules and follows no symlink. ignore on the stack id covers every other case, since a false find costs a row and not a deploy. |
PR 42 |
| The check reading git’s own list of tracked files | The check walks the checkout, skipping .git and node_modules like discovery. Right after a checkout that is the tracked files, and asking git means a process or a parser for its index. A step that writes files before the check makes them show as unclaimed, and the README says to run it right after the checkout. |
0042, slice 2.12 |
The check suggesting inputs, part 2: files a program in a general-purpose language builds a path to, OpenTofu var files and module sources outside the stack, the bases a kustomization names |
Slice 5.7 (0074) suggests what a stack’s own files name plainly: a Pulumi YAML program’s fn::readFile, fn::fileAsset and fn::fileArchive, a plain config value that is the path of a file of the repo, and a Helm stack’s local chart and values files. A path that code builds at run time cannot be read from a file, and the other tools’ references wait for a user who needs them. |
0074, 0042 |
The check reading more of a workflow than triggers, permissions, the jobs, the ref, the concurrency groups, the status checks in if: and the second apply job: the label in the if: of resolve, the branch a push listens to, and a reusable workflow’s caller |
Each is a condition GitHub evaluates, often through an expression, and a wrong guess about one would be a warning people learn to ignore. The concurrency groups, !cancelled() and always() and the second apply job came with slice 5.7, read only where the text plainly lacks them (0074). The README explains the rest, and the tests hold every workflow the docs ship to them. |
0061, 0074 |
| The check asking the backend about OpenTofu workspaces, Helm releases or Kubernetes objects | Slice 5.7 (0074) asks Pulumi’s backend with backend: true. A missing OpenTofu workspace is made by the scan’s init, a Helm release that is not installed yet is a first install, and a manifest stack has no list of its own, so none of them fails its first scan the way a missing Pulumi stack does. |
0074 |
The ignore glob on the row of a stack that does not exist in the backend, or one ready-to-paste ignore block for all such stacks in the summary of a scan |
The row keeps the constant reason of 0022 and stays short. The summary names the glob next to each such stack, one click from the row, and the check with backend: true gives the one block since slice 5.7 (0074). |
0022, slice 2.13, 0074 |
Reading a Pulumi project file for more than stackConfigDir |
Discovery only needs to know where the stack files are. A project file that is wrong in another way is the tool’s to refuse, as a preview failure on the row. | PR 42 |
Showing a Pulumi resource that only moves to a new address through an alias (tracking: move) |
pulumi preview --json holds no step for it, on the minimum and on the newest CLI. Like an output change it needs the tool’s event stream. Until then such a stack is in sync, and the rename rides along with the next deploy, which leaves the real object alone. |
0007, 0036 |
Folding the Pulumi step ops that no recording shows (create-replacement, delete-replaced, read-replacement, import-replacement, discard, discard-replaced, remove-pending-replace) |
The table from step ops to changes is settled from recordings only. A step op outside it fails that stack’s preview and is never shown as in sync. Each needs a scenario that makes the tool print it. | 0007, build plan |
| Previewing a Pulumi stack whose state holds a resource twice at one URN, such as a copy left waiting for deletion by a deploy that failed half way | An address is unique within a diff, so two changes at one address are refused and the row is a preview failure until the stack is repaired from outside. Folding the two needs a recording of what the tool prints. | 0007, 0008 |
| Naming the step op that Sluiceway did not know, in the job log | It is a word the tool wrote at a place Sluiceway could not read, and a parse error never says what was found. The log names the place, steps[3].op. |
0021 |
Stopping GitHub from linking a commit id of the repo, or drawing an emoji for a code such as :key:, inside a resource name or a stack id |
Record 0112 keeps #, @, GH- and web addresses plain. A commit id is linked only when the repo holds that commit, and a name that holds one of its own commits, or an emoji code, is rarer still. Splitting every run of seven hex characters would touch far more names than it saves. |
0027, 0112 |
| Trying a dashboard write again after an API error | The write loop tries again when a write did not stick. A call that GitHub answers with an error ends the job, and the next scan repairs the body. | Slice 1.10, 0004 |
| A preview after a deploy that went out, to check the row | The row is written in sync straight away: the tool deployed exactly the diff the tick approved. A program that gives another diff on its next run shows pending again after the next scan that previews it. A deploy that failed is previewed again, because it may have gone half way. | 0004, slice 2.5 |
A row of its own for a deploy that ended before any preview (the tool missing, the stack gone, a broken sluiceway.yaml) |
apply has no diff to make a row from. The record gets its result, and the next scan previews a deploying row whose record ended, a narrowed scan too, and puts the failure line on it. |
0004, slice 2.5 |
| Deploying a record from another run | apply deploys a record only in the run whose resolve created it, because the record lives as long as its run. A deploy started some other way would be a deploy without a tick, which is the later stack input. |
0003, 0035, slice 2.5 |
| A budget for the summary of an apply | It is about one stack, and 0037 says it needs none. A stack of many thousand changes could still pass 1 MiB. The job log holds the diff in full either way. | 0037, slice 2.5 |
scan.logDiff per stack, or as an action input |
One switch per repo is enough to launch, and it belongs in the reviewed config, not in each workflow. A stacks[] key could narrow it later without a breaking change. |
0048, slice 2.16 |
| The tool’s own diff for the preview after a failed deploy, and for a narrowed scan’s carried rows | apply prints it for its fresh preview, which is what went out or what moved. The row after a failed deploy links to the summary, and a carried row keeps the link it had. |
0048, slice 2.16 |
| Stopping workflow commands around the tool’s other words (its stderr and diagnostics) | Record 0012 gives them the standing of any other step of the user’s workflow. Only the tool’s own diff, which holds values on purpose, is printed with workflow commands stopped. | 0012, 0048, slice 2.16 |
| Values on a preview page from a list of its own | Built with slice 2.19: the page shows the values of dashboard.showValues, as the row does (0052). A second list for the page alone would be a second place to get a secret wrong. |
0050, 0052, slice 2.19 |
dashboard.showValues per stack |
One list per repo is enough to launch. A stacks[] key could narrow it later without a breaking change. |
0052, slice 2.19 |
A value at a path that only diffReasons or replaceReasons names |
The tool says there which property changed and not which side it compared, so the value shown could be the wrong one. Paths from detailedDiff carry that flag. |
0052, slice 2.19 |
A value that is an object, a list or several lines, and ** in the list |
An object or a list can hold a secret next to a harmless leaf, and several lines do not fit on a row. ** would show a whole subtree nobody listed. List the leaves instead. |
0052, slice 2.19 |
| Blanking or marking the preview page of a stack that is no longer pending | A page stays on its commit as it was. It shows no value, the row no longer links to it, and a scan of a new commit writes no page for a stack in sync. Blanking would cost a request per such stack on every scan of a commit that was scanned before. | 0050, slice 2.18 |
A preview page for the pending row that apply writes after a change that moved |
The row lives until the next scan, which writes the page. apply would need checks: write for it. |
0050, slice 2.18 |
| A link from the preview page to the dashboard’s own number | The page is written before the dashboard, whose number a first scan does not know. It links to the issue list with the dashboard’s label instead, which holds one open issue. Writing the page after the dashboard would cost a second write of every row. | 0050, slice 2.18 |
checks: write on the scan job alone |
A job’s permissions replace the workflow’s, so every user would repeat the whole block for one line. resolve and settle only run Sluiceway, and apply runs the same programs the scan does. A team that requires checks can still move it by hand. |
0050, slice 2.18 |
A built-in notification for a failed preview, for a scan that failed before it wrote the dashboard, and for a deploy that settle ended |
The owner named five events. A failed preview is on the dashboard and strict turns the scan red for it, a scan that failed has no dashboard to link, and settle holds no channel. A step of your own on the outputs covers them (docs/notifications.md). |
0078 |
resolve without a checkout, from the GitHub API: one recursive tree call for discovery’s paths and GraphQL reads of the files it opens, for the resolve job of a split workflow on a very large repo |
Measured in slice 5.23: on a runner the checkout of a repo of up to a few thousand files takes 0.3 to 1.9 s, and the API route is as slow or slower, since every Pulumi project file and Helm Chart.yaml must be read. Only repos of 20,000 files or more without Pulumi projects gain, 7 to 9.5 s. The one-step workflow deploys in the same job, so it needs the checkout anyway. |
0087 |
A faster body write in resolve: the largest part of its own time after a tick, 1.2 to 2.8 s of 2.8 to 4.9 s on the test bed |
Slice 5.23 measured it and changed nothing resolve does. Every read in the write is there for a rule: the late read of the write loop (0004), the trail from the deployment records (0062), attribution (0026). Doing them in another order or at once is a change to what the body says in a race, and needs a slice of its own. |
0087 |
Rejected on principle
Not planned. Bringing one of these back means reopening the decision named here first.
| What | Why | Decided in |
|---|---|---|
| A command wrapper or per-stack hooks around the tool | The workflow prepares the environment. A wrapper resolves secrets again for every call and makes Sluiceway responsible for the user’s tooling. | 0013 |
| Generic extra tool arguments | Arbitrary flags can make the preview and the deploy disagree, which breaks the hash. Only named adapter options. | 0015 |
| A pattern-based mask for things that look like tokens | No property value ever leaves the adapter, so there is nothing to mask. | 0021, 0022 |
| A built-in second approval (a second person who also has to tick) | GitHub’s required reviewers do this. Without locked-in credentials it would not be real protection. | 0020 |
| Holding or storing any credential | The five credential promises. | 0014 |
| The Pulumi Automation API as the driver | 13.9 MB bundle, still needs the CLI, and its preview result lacks per-resource steps. | 0001 |
| A partial list of a row’s deletes and replaces (“the first 20, and 431 more”) | Which ones show would be an accident of sort order, and a partial list can be taken for the whole. All or none, with the full count in the warning. | 0028, 0024 |
| Relative times on the dashboard (“2 hours ago”) | The body must be byte-identical for the same input, or every render is a write. | 0029, 0004 |
| A fenced diff block or a table for a row’s changes | Both make every list loose and scroll sideways on a phone. | 0027 |
| Playful wording anywhere but the good-news line and the first-run line (rows, comments, a line for a successful deploy, the pending and deploying states) | A comment is a notification in someone’s inbox, and rows are where people decide. Anything with a delete or replace is plain twice over. | 0032, 0024 |
| A header that shows what is happening now before bad news (deploying above failing) | A failure would vanish from the picture for the length of every deploy. | 0031 |
| A grey header with no face and no colour for any delete or replace (the plain state) | Routine replaces are everyday in some repos, so the header with Penny and its colours almost never showed, and a warning that is always on is not read. The header always shows the real state, and a destroy adds a sign to the same picture. | 0043, 0031 |
| A header state of its own for a delete or replace, in colour, in place of the pending pictures (the careful state) | It hides the real state behind the warning. A sign inside the real picture hides nothing. | 0043 |
| The destroy sign as one stamped crate, or as a striped board with a beacon | The crate is small on a phone and drifts out of view while deploying. The board and beacon are loud and move, and a sign that shows most days and nags is tuned out. The still sign on the wall won. | 0043 |
| An Ansible adapter | Ansible has no state. --check --diff gives a dry run, but “what is pending since the last deploy” would need the whole playbook in check mode on every scan, and the dashboard could only promise “what this playbook would change now”. Decided 2026-09-22 with the owner: not a fit. |
The adapter interface |
| A Docker or Docker Compose adapter | No plan, no diff, no state to compare. “Pending” could only mean “the file changed since the last run”, which is a deploy button, and there are many. It would dilute what a row on the dashboard means. Decided 2026-09-22. | The adapter interface |
Per-resource selection (--target) |
Granularity is one checkbox per stack. | Brief non-goals |
| A comment on the pull request with its preview, rewritten on every push | The pull request preview writes a check run per stack, which is quieter and already exists for the scan’s pages. A comment notifies every subscriber on every push. The dashboard stays the one place a change is ticked. | 0101 |
| A pull request preview of 300 changed files or more, through the pull request’s own file list | The comparison of base and head is what a push’s narrowed scan reads and needs no new permission. The pull request’s file list pages to 3,000 files and needs pull-requests: read. |
0101 |
An env key with values in sluiceway.yaml |
The file is committed, and a value in it would be a credential in the repo. A stack names a file with envFile and the file holds the values, where a step may have resolved them. |
0103, 0013 |
| The tool’s native diff text in the summary, or an excerpt of the tool’s error on a row | Native text prints unmarked values, and an error can quote a connection string. Only words Sluiceway wrote leave the job log. | 0021, 0022 |
| Values shown for deletes and replaces only | The one exception that brings back every risk, on the rows people read most closely. If values ever come, they come for every op by one rule. | 0024 |
| A redacted summary | The ticker would approve bare numbers with no way on GitHub to see what is behind them. Redact is about reach, not access. | 0023 |
| The commit SHA in the diff hash | A commit SHA would void every outstanding tick on every merge. The digest of values that this line once rejected with it is the value fingerprint of slice 5.37, with its cost stated. | 0008, 0102 |
| A salt for the value fingerprint | It would close the offline guess of a low-entropy value that is neither shown nor marked secret. There is no place both the scan job and the apply job can read that the issue does not hold: the workflow token is per run, a repo secret would be a credential input, and a cache can be evicted. |
0102, 0014 |
| A value fingerprint for an object the Helm plugin adds, and for Helm and kubectl drift | The diff plugin prints no manifest for an object it adds, and those drift checks read no values. A render of the chart, or a read of the live objects, would give them. | 0102 |
| A retry through “Re-run failed jobs”, checked against the person who pressed it | Two ways to start a deploy, and a record that names the wrong ticker. A fresh tick is the only retry. | 0019 |
A tick level below write |
People with read or triage access cannot edit a bot’s issue, so the level would promise something it cannot do. | 0018 |
| A list in the tick rule that grants access on its own | A typo or a removed collaborator would keep deploy rights that the repo no longer gives. A list only narrows. | 0018 |
| A comment on the dashboard for a failed deploy | The failure line on the row says it, and a comment is a notification in every subscriber’s inbox. Sluiceway writes a comment for a refused tick and for a change that moved since the tick, both about what a person did, and none for a deploy that failed. | 0004, 0018, 0032, 0051 |
| A global write lock on the dashboard | Actions concurrency works per job, so a lock would hold ticks hostage for the length of a scan. Late read, write, verify instead. | 0004 |
| The last scan’s results kept as a workflow artifact | A second store with its own expiry. The live body already is the cache. | 0004, 0011 |
| One job per stack (a matrix) for scans | Every job pays the setup again, and a small runner pool starves. One job with a bounded pool. | 0012 |
| Working out a stack’s inputs from an import graph or workspace dependencies | Per-language knowledge in the adapter, and it still misses what programs read at run time. The claim rule fails open instead. | 0010 |
Bigger efforts, each its own plan
| What | Note |
|---|---|
| A hosted GitHub App with an org-wide dashboard | A control plane only. Previews and deploys always run in the user’s own runners. It reuses the open source core. |
| GitLab and Bitbucket | The UI is a GitHub issue, so this is a different product surface. |
| Interactive notifications (a tick from a button in Slack) and a metrics endpoint | A button needs an app that Slack can call back, and a GitHub Action is not running when someone clicks. The built-in messages go one way (0078), and metrics are pushed from the outputs. Both fit a hosted version. |
| Cost estimation | Out of scope for v1. |
| The cost estimate in the summary, the preview page, the result file and the notifications | The row is where a person decides, so the line lives there. Each of the others is a published shape or a message of its own, and a cost in the result file is a field of a versioned schema. |
| A cost line for Pulumi, Helm and Kubernetes manifests stacks | The Infracost CLI reads Terraform plans and nothing else. A line only appears where it can be honest. |
A cost.currency key, a usage file and other settings of the Infracost CLI |
The CLI reads INFRACOST_CURRENCY and its own files from the environment and the runner, which is the workflow’s (0013). |
A warning from the check when cost.enabled is on and no step installs the Infracost CLI |
The scan says so, once per stack, with the warning “Cost not estimated”, and the row shows no line. |
| The Infracost CLI’s 2.x line | Its scan sends the code to Infracost’s own service behind a login, which is not the call the owner allowed: the pricing API, with resource types, regions and quantities. The 0.10 line’s diff reads the plan’s JSON and asks only that. |
| Docs site and Marketplace launch | Part of the public launch, after the core loop is proven. |
Launch material: screenshots, a note on merge queues, the JSON schema in SchemaStore, the 1.0.0 tag and the v1 moving tag |
Same. Until then the moving tag is v0. |