Title: fmt: cached check passes while --no-cache fails after formatting · Issue #479 · rstackjs/rstack-cli · GitHub
Open Graph Title: fmt: cached check passes while --no-cache fails after formatting · Issue #479 · rstackjs/rstack-cli
X Title: fmt: cached check passes while --no-cache fails after formatting · Issue #479 · rstackjs/rstack-cli
Description: Description Immediately after rs fmt formats and writes a file: rs fmt --check exits with code 0 and reports that formatting is correct. rs fmt --check --no-cache exits with code 1 and reports a formatting issue for exactly the same file...
Open Graph Description: Description Immediately after rs fmt formats and writes a file: rs fmt --check exits with code 0 and reports that formatting is correct. rs fmt --check --no-cache exits with code 1 and reports a fo...
X Description: Description Immediately after rs fmt formats and writes a file: rs fmt --check exits with code 0 and reports that formatting is correct. rs fmt --check --no-cache exits with code 1 and reports a fo...
Opengraph URL: https://github.com/rstackjs/rstack-cli/issues/479
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"fmt: cached check passes while --no-cache fails after formatting","articleBody":"## Description\n\nImmediately after `rs fmt` formats and writes a file:\n\n- `rs fmt --check` exits with code 0 and reports that formatting is correct.\n- `rs fmt --check --no-cache` exits with code 1 and reports a formatting issue for exactly the same file.\n- Running cached `rs fmt` again leaves the file unchanged.\n- Running formatting again without cache changes the file.\n\nThis reproduces with a fresh cache directory and does not require an existing project or CI environment.\n\n## Environment\n\n- Linux x64\n- Node.js 24.19.0\n- pnpm 12.3.4\n- Rstack 0.7.3\n- Prettier 3.9.6 (Rstack dependency)\n- yuku-parser 0.9.3 (Rstack dependency)\n\n## Minimal reproduction\n\nCreate the following three files in an empty directory.\n\n### package.json\n\n```json\n{\n \"private\": true,\n \"type\": \"module\",\n \"devDependencies\": {\n \"rstack\": \"0.7.3\"\n }\n}\n```\n\n### rstack.config.mjs\n\n```js\nimport { define } from 'rstack';\n\ndefine.fmt({\n singleQuote: true,\n trailingComma: 'all',\n});\n```\n\n### example.ts\n\nPlease preserve the exact formatting of this input:\n\n```ts\nconst fetch = rs.fn\u003ctypeof globalThis.fetch>().mockImplementation(() => Promise.resolve(\n new Response('cached pixels', { headers: { 'content-type': 'image/webp' } }),\n));\n```\n\nHere, `rs` is only an identifier in the formatting input. The code is never executed, so no test framework is required.\n\nInstall dependencies:\n\n```sh\npnpm install\n```\n\nStarting with a nonexistent `repro-cache` directory, run each command separately, in order:\n\n```sh\npnpm exec rs fmt --cache-location ./repro-cache example.ts\n# Exits with 0 and writes the first formatted output, B.\n\npnpm exec rs fmt --check --cache-location ./repro-cache example.ts\n# Exits with 0: Format check passed.\n\npnpm exec rs fmt --check --no-cache example.ts\n# Exits with 1: Formatting issues found in 1 file.\n\npnpm exec rs fmt --cache-location ./repro-cache example.ts\n# Exits with 0, but output B remains unchanged.\n\npnpm exec rs fmt --no-cache example.ts\n# Exits with 0 and changes B into a different output, C.\n\npnpm exec rs fmt --check --no-cache example.ts\n# Exits with 0: Format check passed.\n```\n\n## Expected behavior\n\nCached and uncached checks should produce consistent results for identical file contents, configuration, and tool versions.\n\nFormatting should be idempotent: formatting its output again should not change it.\n\n## Additional investigation\n\nTwo observable behaviors combine to produce this result:\n\n1. For this input, formatting the first output B again produces a different output C.\n2. Rstack caches the first written output B as clean. Subsequent cache hits skip formatting, so they do not detect that B would still be changed by another formatting pass.\n\nIn the installed Rstack 0.7.3 package, `formatFile` in `dist/335.js` stores the hash of `result.formatted` after writing and classifies the cache entry using:\n\n```js\nshouldWrite || unchanged ? 'clean' : 'dirty'\n```\n\nAs a result, a written output is marked clean.\n\nSeparately, directly calling Prettier 3.9.6's `format()` with `parser: 'typescript'`, `singleQuote: true`, and `trailingComma: 'all'` also produces the same B and C.\n\nTherefore, the non-idempotent formatting is not specific to Rstack or its Yuku integration. This report concerns how caching the first written output as clean masks that behavior, causing cached and uncached checks to disagree. There is no evidence that this is a cache-hash collision.\n","author":{"url":"https://github.com/shiyudesu","@type":"Person","name":"shiyudesu"},"datePublished":"2026-09-13T11:00:04Z","url":"https://github.com/rstackjs/rstack-cli/issues/479"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:0c7a4104-0921-7877-4c91-3d95e3e8e40b |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | DC54:1947FF:EA65E9:13C2598:6AEC1DC9 |
| html-safe-nonce | c1ac06fec002de778e3227d727df8bdeaf69da9d243c8ca12e149e56fd0b6226 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEQzU0OjE5NDdGRjpFQTY1RTk6MTNDMjU5ODo2QUVDMURDOSIsInZpc2l0b3JfaWQiOiI2ODk5OTQ3MDgyNTkyNTU4NTM3IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | f50847d0633d10867f2d3847028602991ebf27d62defd0239303f954e23a89ef |
| hovercard-subject-tag | issue:5439634620 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/rstackjs/rstack-cli/479/issue_layout |
| twitter:image | https://opengraph.githubassets.com/f25d08477ab42f09ee2580385f0464daf5f6c62e24f604abb847753f352394a3/rstackjs/rstack-cli/issues/479 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/f25d08477ab42f09ee2580385f0464daf5f6c62e24f604abb847753f352394a3/rstackjs/rstack-cli/issues/479 |
| og:image:alt | Description Immediately after rs fmt formats and writes a file: rs fmt --check exits with code 0 and reports that formatting is correct. rs fmt --check --no-cache exits with code 1 and reports a fo... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | shiyudesu |
| hostname | github.com |
| expected-hostname | github.com |
| None | d063d52022f5f270ef55a70761d126c8085d492d3c251084b07781784c02dad4 |
| turbo-cache-control | no-preview |
| go-import | github.com/rstackjs/rstack-cli git https://github.com/rstackjs/rstack-cli.git |
| octolytics-dimension-user_id | 129049372 |
| octolytics-dimension-user_login | rstackjs |
| octolytics-dimension-repository_id | 1138027854 |
| octolytics-dimension-repository_nwo | rstackjs/rstack-cli |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1138027854 |
| octolytics-dimension-repository_network_root_nwo | rstackjs/rstack-cli |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 56e59340194011ba3cb6809acfa979ccbd174288 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width