Skip to content

compiler-cli: watch mode can compile a file while a slow non-atomic save is incomplete #177

Description

@julia-script

Problem

silk check --watch can compile an intermediate file image when a non-atomic save takes longer
than one second.

The watcher correctly waits for two equal source-tree fingerprints, but it caps that wait at 40
samples spaced 25 ms apart. When the cap expires, it returns the last fingerprint even if the tree
is still changing. A slow writer can therefore leave a source file truncated or partially written
when compilation begins.

This is product behavior, not only a flaky test: shell redirection, sed -i, and editors without
atomic save can expose the same open(O_TRUNC) → write → close sequence.

Current behavior

The settle loop is effectively:

sample tree
repeat 40 times:
  wait 25 ms
  sample tree
  if unchanged: compile
compile the last sample anyway

The final line runs after about one second even when every sample changed.

A file that previously contained:

pub fn main() -> i32 { return 42 }

may temporarily be observed as empty or as a prefix while the replacement is still being written.
Watch mode can compile that intermediate state and publish diagnostics that do not describe any
completed save.

Expected behavior

A finite non-atomic save must be compiled only after the complete file image settles, even when the
writer takes longer than one second.

An intentionally empty completed file remains a valid edit and must still compile. The watcher
therefore cannot solve this by rejecting every zero-length file.

A source tree rewritten continuously forever must not block the watch loop without a defined policy.
The implementation may use an adaptive deadline, writer-progress detection, or another bounded
strategy, but it must distinguish a finite slow save from an edit stream that never settles.

Required cases

Slow finite save

  1. Start from a non-empty valid Silk file.
  2. Truncate it.
  3. Write the replacement in several chunks over more than one second.
  4. Close the writer.

Watch mode must compile the complete replacement and must never compile an empty or partial prefix.

Intentional empty save

Truncate the file and finish the save without writing bytes. After the tree settles, watch mode must
compile the empty file exactly once and report its ordinary diagnostics.

Continuous rewrites

Keep changing the file without a settled interval. Watch mode must follow one documented bounded
behavior and remain responsive to interruption; it must not claim that an arbitrary mid-write
snapshot is a completed save.

Evidence

  • packages/compiler-cli/src/Workflow.ts defines settleInterval = '25 millis' and
    settleSamples = 40.
  • settledFingerprint returns the last changing fingerprint after the loop rather than a distinct
    “not settled” result.
  • packages/compiler-cli/test/Workflow.test.ts already performs 200 non-atomic writes and rejects
    any observed source outside the set of complete programs, but its timing is load-dependent.

Acceptance criteria

  • A deterministic test writes one file non-atomically for longer than one second and observes no
    compilation of an empty or partial image.
  • The completed replacement is compiled once after it settles.
  • An intentionally empty completed file still compiles once.
  • A continuously changing tree follows a documented bounded policy and can be interrupted
    promptly.
  • Separate completed saves remain separate compilation passes.
  • The test uses controlled writer synchronization rather than depending on runner load.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Clear valuebugSomething isn't workingtoolingCLI, formatter, docs tooling

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions