docs: fix drift from the 0.2 changes and give secrets a page - #37
Merged
Conversation
The README's only code sample called shellRaw(), which 0.2 removed - the sample GitHub and Packagist render was a guaranteed "Call to undefined method" for anyone who pasted it. It now uses exec(), and the README never mentioned exec() at all before. Secret masking is the feature 0.2 is for, and it had no page, no nav entry, no index card and no mention in the README: the only prose lived inside variables.md. It is now docs/secrets.md, in the nav, with what is masked stated before the caveats rather than as a footnote to another page. variables.md keeps a short pointer, and the cross-references in tasks.md, configuration.md and listeners.md follow the move. Two claims were simply wrong: - tasks.md promised "default: no timeout" for `timeout`. There is one - ShellExecutor::DEFAULT_TIMEOUT is 300 seconds, and `null` falls through to it rather than disabling it. A ten-minute migration would have died at five minutes against documentation that said it could not. - tasks.md passed $ctx as log()'s third argument, which is a TypeError. The parameter is an array of context data. Also: the ExecutionResult table was missing `command` and the three getters, `--version` and `--help` were undocumented while CONTRIBUTING tells bug reporters to run `--version`, the resolution-priority list did not say secrets sit outside it, the environments example's comment implied Sputnik echoes quotes it does not echo, and the version strings in the terminal mockups still said 0.1.x. `mkdocs build --strict` passes.
Material makes its two sticky nav labels - the section label in the left nav and the table-of-contents heading - opaque in --md-default-bg-color, with a same-coloured 0.4rem shadow around them, so nav items scrolling underneath disappear behind them. That only blends in when the page background is that flat colour. Ours is an animated three-layer gradient, so both labels showed up as solid #111318 rectangles with a halo, ending mid-column. Reproducing the gradient on the labels would work in principle - background-attachment: fixed resolves percentages against the viewport, so the layers would line up - but the body background is animated, and any timing difference between the two animations would show as a moving seam. Frosting needs no such coupling: nothing to see when only the page is behind the label, and nav items that scroll past stay legible. The left label needed the same specificity Material uses for it (.md-nav--lifted > .md-nav__list > .md-nav__item--active > .md-nav__link, four classes); a shorter selector lost to it and left that one box in place while the table of contents was already fixed.
The pills name what shapes how Sputnik is used - distribution, contexts, container routing, the PHP floor. Masking is a guarantee inside one of those, not a fifth axis, and the feature is already carried by its card in the grid, the nav entry and its own page.
refsz
added a commit
that referenced
this pull request
Aug 19, 2026
…ed (#40) The config schema never declared `secrets`. It survived because the `variables` structure used otherItems(), which accepts any key - so a misspelled section name was valid config. `secrests:` loaded without a word of complaint, every secret it declared resolved to null, and the task ran on with an empty argument and exited 0. Nothing was masked because no value existed. Verified against the binary before and after: before: value=[NULL], "> printf %s token " (exit 0) after: Unexpected item 'variables > secrests', did you mean 'secrets'? `secrets` is now declared as a map of either a literal scalar or a provider with type command|script|env, and `variables` no longer accepts unknown keys. That also moves an unsupported secret type from first access - inside a task run, after other work has happened - to config load. This is a change worth making before 0.2 rather than after: tightening a schema later rejects configs that load today, which would be a breaking change for anyone whose config has a typo in it. Two test configs used a flat `variables: app_name: value` form that the schema happened to tolerate. The variable resolver never read it - it reads variables.constants - so those tests were asserting raw config access through a shape no feature supports. They use constants now. Also corrects two things in the docs I got wrong in #37: ExecutionResult's getOutput() and getErrorOutput() return the properties unchanged, not trimmed, and `timeout: 0` removes the limit just as `tty: true` does (Symfony's validateTimeout turns 0.0 into null).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A pass over every doc page against the source, before the 0.2 tag. Every claim below was checked against
src/, not against the prose.Broken for anyone who pastes it
README.md:22calledshellRaw(). #30 removed it. The README's only code sample - the one GitHub and Packagist render - was a guaranteedError: Call to undefined method.docs/index.mdalready had the corrected version; the README never got it, and as a result never mentionedexec()at all.docs/tasks.mdpromisedtimeouthas no default. It has one:ShellExecutor::DEFAULT_TIMEOUT = 300.0, andTaskContextpasses'timeout' => $options['timeout'] ?? null, sonullfalls through to 300 seconds rather than disabling the limit. A ten-minute migration would have been killed at five minutes against documentation saying that could not happen. Now states the default and thattty: trueis what removes the limit.docs/tasks.mdpassed$ctxaslog()'s third argument. The signature islog(string $level, string $message, array $context = []); passing aTaskContextis a fatalTypeError.Secret masking was nearly invisible
It is the feature 0.2 exists for, and it had no page, no nav entry, no index card, and no mention in the README - the string "secret" did not occur in that file. The only prose lived in the middle of
variables.md, reachable only from inside another page.It is now
docs/secrets.md:exec()example showing the echoed line masked while curl receives the real tokentype: envis secrets-only.variables.dynamicsrejects it (ConfigValidatorallowscommand|git|script|system|composite), so a reader who just learnedtype: envfrom the secrets section would hit a validation error.variables.mdkeeps a short pointer, and the cross-references intasks.md,configuration.mdandlisteners.mdfollow the move -mkdocs build --strictwould fail on a stale anchor, and passes.Smaller corrections
ExecutionResulttable was missingcommand(the property holding the unmasked command line, which the secrets page warns about) andgetOutput()/getErrorOutput()/getCombinedOutput().--versionand--helpwere undocumented, whileCONTRIBUTING.mdtells bug reporters to runsputnik --version.docs/environments.mdshowedsh -c "drush sql-dump | gzip > dump.gz"as if Sputnik echoed those quotes.ShellExecutor::display()joins argv with spaces and makes no quoting promise. The quotes still usefully show the argument boundary, so they stay with a note.README.md,docs/index.mdanddocs/quickstart.mdsaid0.1.x; the README's also dropped the emoji and thevthatSputnikOutputactually prints. Now🛰 Sputnik v0.2.0.Checked and deliberately left alone
shellRawanywhere else; the one hit in the tests is a comment noting its removal.{command}placeholder; the one hit is a negative test asserting the old form is rejected.executor:in the docs is already a list, andenvironment.shellis documented and matchesDEFAULT_SHELL = ['sh', '-c'].exec()vsshell()explanation intasks.mdis correct, including the Go-template escaping warning -{{range .items}}genuinely does not match the parser's pattern while{{end}}does.8.2hits are transitive constraints incomposer.lock.🤖 Generated with Claude Code
https://claude.ai/code/session_018CTvnzcNYmFgm2HQcm821A