ttsc 0.31.0 → 0.31.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/cmd/platform/main.go +2 -2
  2. package/internal/sourceprocess/contain_windows.go +10 -13
  3. package/internal/sourceprocess/started_windows.go +41 -0
  4. package/internal/sourceprocess/windows_started_publication_distinguishes_pending_from_refusal_test.go +76 -0
  5. package/internal/watchhelper/run.go +30 -20
  6. package/internal/watchhelper/run_other.go +4 -4
  7. package/internal/watchhelper/run_windows.go +471 -0
  8. package/internal/watchhelper/watchhelper.go +8 -2
  9. package/internal/watchhelper/windows_notifications_preserve_mutations_without_access_noise_test.go +506 -0
  10. package/lib/compiler/internal/build/BuildExecution.d.ts +4 -2
  11. package/lib/compiler/internal/build/BuildExecution.js +40 -12
  12. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  13. package/lib/compiler/internal/startResidentTransform.js +5 -4
  14. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  15. package/lib/compiler/internal/transformProjectInMemory.js +9 -7
  16. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  17. package/lib/internal/javascriptRuntimeCapabilities.d.ts +13 -4
  18. package/lib/internal/javascriptRuntimeCapabilities.js +56 -6
  19. package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
  20. package/lib/internal/nativeBinary.d.ts +3 -3
  21. package/lib/internal/nativeBinary.js +3 -3
  22. package/lib/internal/pluginSource.d.ts +1 -0
  23. package/lib/internal/pluginSource.js +1 -0
  24. package/lib/internal/pluginSource.js.map +1 -1
  25. package/lib/internal/resolveNodeBinary.d.ts +6 -1
  26. package/lib/internal/resolveNodeBinary.js +6 -2
  27. package/lib/internal/resolveNodeBinary.js.map +1 -1
  28. package/lib/internal/runtimeExecutableIdentity.d.ts +22 -13
  29. package/lib/internal/runtimeExecutableIdentity.js +192 -102
  30. package/lib/internal/runtimeExecutableIdentity.js.map +1 -1
  31. package/lib/launcher/internal/runtime/installRuntimeHooks.js +58 -4
  32. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  33. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.d.ts +5 -1
  34. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js +7 -2
  35. package/lib/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.js.map +1 -1
  36. package/lib/launcher/internal/ttscserver/runTtscserver.js +8 -1
  37. package/lib/launcher/internal/ttscserver/runTtscserver.js.map +1 -1
  38. package/lib/plugin/internal/CapabilityResolutionFormat.d.ts +21 -62
  39. package/lib/plugin/internal/CapabilityResolutionFormat.js +15 -106
  40. package/lib/plugin/internal/CapabilityResolutionFormat.js.map +1 -1
  41. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +6 -0
  42. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +4 -4
  43. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
  44. package/lib/plugin/internal/load/loadProjectPlugins.d.ts +24 -14
  45. package/lib/plugin/internal/load/loadProjectPlugins.js +141 -56
  46. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  47. package/lib/plugin/internal/readCapabilityResolution.d.ts +6 -0
  48. package/lib/plugin/internal/readCapabilityResolution.js +8 -5
  49. package/lib/plugin/internal/readCapabilityResolution.js.map +1 -1
  50. package/lib/plugin/internal/source/GoEnvironmentReading.d.ts +48 -0
  51. package/lib/plugin/internal/source/GoEnvironmentReading.js +156 -0
  52. package/lib/plugin/internal/source/GoEnvironmentReading.js.map +1 -0
  53. package/lib/plugin/internal/source/NativeSourcePackages.d.ts +51 -13
  54. package/lib/plugin/internal/source/NativeSourcePackages.js +182 -27
  55. package/lib/plugin/internal/source/NativeSourcePackages.js.map +1 -1
  56. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +42 -3
  57. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +68 -7
  58. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
  59. package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.d.ts +5 -3
  60. package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.js +14 -8
  61. package/lib/plugin/internal/source/PluginBuildEnvironmentWitness.js.map +1 -1
  62. package/lib/plugin/internal/source/PluginContentIdentities.d.ts +265 -0
  63. package/lib/plugin/internal/source/PluginContentIdentities.js +452 -0
  64. package/lib/plugin/internal/source/PluginContentIdentities.js.map +1 -0
  65. package/lib/plugin/internal/source/PluginLoadAnswers.d.ts +63 -0
  66. package/lib/plugin/internal/source/PluginLoadAnswers.js +128 -0
  67. package/lib/plugin/internal/source/PluginLoadAnswers.js.map +1 -0
  68. package/lib/plugin/internal/source/SourceBuildCacheLayout.d.ts +10 -0
  69. package/lib/plugin/internal/source/SourceBuildCacheLayout.js +12 -0
  70. package/lib/plugin/internal/source/SourceBuildCacheLayout.js.map +1 -1
  71. package/lib/plugin/internal/source/SourcePluginWorkspace.d.ts +53 -19
  72. package/lib/plugin/internal/source/SourcePluginWorkspace.js +111 -22
  73. package/lib/plugin/internal/source/SourcePluginWorkspace.js.map +1 -1
  74. package/lib/plugin/internal/source/buildSourcePlugin.d.ts +34 -18
  75. package/lib/plugin/internal/source/buildSourcePlugin.js +52 -24
  76. package/lib/plugin/internal/source/buildSourcePlugin.js.map +1 -1
  77. package/lib/plugin/internal/source/computeCacheKey.d.ts +25 -7
  78. package/lib/plugin/internal/source/computeCacheKey.js +85 -34
  79. package/lib/plugin/internal/source/computeCacheKey.js.map +1 -1
  80. package/lib/plugin/internal/source/hashPluginBuildEnvironment.d.ts +7 -3
  81. package/lib/plugin/internal/source/hashPluginBuildEnvironment.js +75 -102
  82. package/lib/plugin/internal/source/hashPluginBuildEnvironment.js.map +1 -1
  83. package/lib/plugin/internal/source/pluginBuildEnvironment.d.ts +8 -3
  84. package/lib/plugin/internal/source/pluginBuildEnvironment.js +8 -4
  85. package/lib/plugin/internal/source/pluginBuildEnvironment.js.map +1 -1
  86. package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js +12 -2
  87. package/lib/plugin/internal/source/pluginBuildEnvironmentWorker.js.map +1 -1
  88. package/lib/plugin/internal/source/pluginBuildVersions.d.ts +2 -2
  89. package/lib/plugin/internal/source/pluginBuildVersions.js +4 -17
  90. package/lib/plugin/internal/source/pluginBuildVersions.js.map +1 -1
  91. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +6 -5
  92. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +10 -17
  93. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
  94. package/lib/plugin/internal/source/pluginSourceState.d.ts +6 -2
  95. package/lib/plugin/internal/source/pluginSourceState.js +20 -8
  96. package/lib/plugin/internal/source/pluginSourceState.js.map +1 -1
  97. package/lib/plugin/internal/source/pluginSourceStateHolds.d.ts +7 -2
  98. package/lib/plugin/internal/source/pluginSourceStateHolds.js +12 -7
  99. package/lib/plugin/internal/source/pluginSourceStateHolds.js.map +1 -1
  100. package/lib/plugin/internal/source/processPluginBuildEnvironment.d.ts +7 -2
  101. package/lib/plugin/internal/source/processPluginBuildEnvironment.js +8 -3
  102. package/lib/plugin/internal/source/processPluginBuildEnvironment.js.map +1 -1
  103. package/lib/plugin/internal/source/provenPluginSourceDigest.d.ts +28 -0
  104. package/lib/plugin/internal/source/provenPluginSourceDigest.js +41 -0
  105. package/lib/plugin/internal/source/provenPluginSourceDigest.js.map +1 -0
  106. package/lib/plugin/internal/source/pruneCacheFileRoot.d.ts +3 -2
  107. package/lib/plugin/internal/source/pruneCacheFileRoot.js +3 -2
  108. package/lib/plugin/internal/source/pruneCacheFileRoot.js.map +1 -1
  109. package/lib/plugin/internal/source/ttscPackageVersion.d.ts +26 -0
  110. package/lib/plugin/internal/source/ttscPackageVersion.js +47 -0
  111. package/lib/plugin/internal/source/ttscPackageVersion.js.map +1 -0
  112. package/lib/plugin/internal/writeCapabilityResolution.d.ts +6 -0
  113. package/lib/plugin/internal/writeCapabilityResolution.js +11 -7
  114. package/lib/plugin/internal/writeCapabilityResolution.js.map +1 -1
  115. package/lib/plugin/resolveCapabilityPlugins.js +67 -15
  116. package/lib/plugin/resolveCapabilityPlugins.js.map +1 -1
  117. package/package.json +8 -8
  118. package/src/compiler/internal/build/BuildExecution.ts +46 -13
  119. package/src/compiler/internal/startResidentTransform.ts +12 -3
  120. package/src/compiler/internal/transformProjectInMemory.ts +10 -2
  121. package/src/internal/javascriptRuntimeCapabilities.ts +71 -5
  122. package/src/internal/nativeBinary.ts +3 -3
  123. package/src/internal/pluginSource.ts +1 -0
  124. package/src/internal/resolveNodeBinary.ts +12 -1
  125. package/src/internal/runtimeExecutableIdentity.ts +227 -141
  126. package/src/launcher/internal/runtime/installRuntimeHooks.ts +68 -3
  127. package/src/launcher/internal/ttscserver/captureLSPPluginSelectionInputs.ts +11 -2
  128. package/src/launcher/internal/ttscserver/runTtscserver.ts +18 -1
  129. package/src/plugin/internal/CapabilityResolutionFormat.ts +23 -107
  130. package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +14 -4
  131. package/src/plugin/internal/load/loadProjectPlugins.ts +421 -211
  132. package/src/plugin/internal/readCapabilityResolution.ts +17 -6
  133. package/src/plugin/internal/source/GoEnvironmentReading.ts +178 -0
  134. package/src/plugin/internal/source/NativeSourcePackages.ts +365 -48
  135. package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +83 -5
  136. package/src/plugin/internal/source/PluginBuildEnvironmentWitness.ts +18 -8
  137. package/src/plugin/internal/source/PluginContentIdentities.ts +563 -0
  138. package/src/plugin/internal/source/PluginLoadAnswers.ts +151 -0
  139. package/src/plugin/internal/source/SourceBuildCacheLayout.ts +14 -0
  140. package/src/plugin/internal/source/SourcePluginWorkspace.ts +148 -25
  141. package/src/plugin/internal/source/buildSourcePlugin.ts +104 -36
  142. package/src/plugin/internal/source/computeCacheKey.ts +129 -55
  143. package/src/plugin/internal/source/hashPluginBuildEnvironment.ts +152 -105
  144. package/src/plugin/internal/source/pluginBuildEnvironment.ts +9 -2
  145. package/src/plugin/internal/source/pluginBuildEnvironmentWorker.ts +18 -2
  146. package/src/plugin/internal/source/pluginBuildVersions.ts +5 -26
  147. package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +13 -25
  148. package/src/plugin/internal/source/pluginSourceState.ts +28 -12
  149. package/src/plugin/internal/source/pluginSourceStateHolds.ts +15 -8
  150. package/src/plugin/internal/source/processPluginBuildEnvironment.ts +8 -2
  151. package/src/plugin/internal/source/provenPluginSourceDigest.ts +45 -0
  152. package/src/plugin/internal/source/pruneCacheFileRoot.ts +3 -2
  153. package/src/plugin/internal/source/ttscPackageVersion.ts +45 -0
  154. package/src/plugin/internal/writeCapabilityResolution.ts +29 -8
  155. package/src/plugin/resolveCapabilityPlugins.ts +87 -13
@@ -3,8 +3,8 @@
3
3
  // The real compiler and runner commands live in the JavaScript launchers so
4
4
  // they can resolve the consuming project's `typescript` and
5
5
  // plugin descriptors. This binary supplies version/platform metadata, and the
6
- // hidden `__watch` command: the Linux directory notification helper the
7
- // unplugin adapter runs.
6
+ // hidden `__watch` command: the native Linux and Windows directory notification
7
+ // helper the unplugin adapter runs.
8
8
  package main
9
9
 
10
10
  import (
@@ -125,6 +125,7 @@ func runCommand(req request, cancel <-chan struct{}, out, errOut io.Writer) (com
125
125
  targetPid := 0
126
126
  targetHandle := windows.Handle(0)
127
127
  var targetErr error
128
+ targetPending := false
128
129
  innerResult := false
129
130
  defer func() {
130
131
  if targetHandle != 0 {
@@ -133,21 +134,17 @@ func runCommand(req request, cancel <-chan struct{}, out, errOut io.Writer) (com
133
134
  }
134
135
  }
135
136
  }()
136
- retainTarget := func() error {
137
- data, readErr := os.ReadFile(filepath.Join(dir, "started.json"))
137
+ retainTarget := func() (bool, error) {
138
+ target, pending, readErr := readWindowsStarted(filepath.Join(dir, "started.json"))
138
139
  if readErr != nil {
139
- return readErr
140
- }
141
- var target windowsStarted
142
- if decodeErr := json.Unmarshal(data, &target); decodeErr != nil {
143
- return decodeErr
140
+ return pending, readErr
144
141
  }
145
142
  retained, retainErr := retainWindowsTarget(target, job)
146
143
  if retainErr != nil {
147
- return retainErr
144
+ return false, retainErr
148
145
  }
149
146
  targetHandle, targetPid = retained, int(target.Pid)
150
- return nil
147
+ return false, nil
151
148
  }
152
149
  for !finished {
153
150
  select {
@@ -196,8 +193,8 @@ func runCommand(req request, cancel <-chan struct{}, out, errOut io.Writer) (com
196
193
  }
197
194
  }
198
195
  if started && !running && !finished {
199
- targetErr = retainTarget()
200
- if targetErr != nil && !os.IsNotExist(targetErr) {
196
+ targetPending, targetErr = retainTarget()
197
+ if targetErr != nil && !targetPending {
201
198
  value = failure("EPROCESS", targetErr.Error())
202
199
  finished = true
203
200
  }
@@ -233,8 +230,8 @@ func runCommand(req request, cancel <-chan struct{}, out, errOut io.Writer) (com
233
230
  // identity can still be retained here, before termination. Missing identity
234
231
  // after START is unknown, even if the job subsequently becomes empty.
235
232
  noTarget := !started || (innerResult && value.Version == 1 && value.Pid == 0 && value.Error != nil)
236
- if started && targetHandle == 0 && !noTarget && (targetErr == nil || os.IsNotExist(targetErr)) {
237
- targetErr = retainTarget()
233
+ if started && targetHandle == 0 && !noTarget && (targetErr == nil || targetPending) {
234
+ targetPending, targetErr = retainTarget()
238
235
  }
239
236
  originalJoined := innerResult && targetPid > 0 && value.Pid == targetPid && value.Cleanup.DirectChildJoined
240
237
  value.Cancelled = cancelled
@@ -0,0 +1,41 @@
1
+ //go:build windows
2
+
3
+ package sourceprocess
4
+
5
+ import (
6
+ "encoding/json"
7
+ "errors"
8
+ "os"
9
+
10
+ "golang.org/x/sys/windows"
11
+ )
12
+
13
+ // readWindowsStarted acquires the helper's suspended-target publication. An
14
+ // absent file or an actual Windows sharing violation means acquisition is
15
+ // pending, not that the helper failed or that any target was enrolled. Windows
16
+ // can briefly refuse reads while the complete file is atomically published.
17
+ // The command owner continues observing helper exit, cancellation and deadline.
18
+ //
19
+ // WARNING (#1718): keep pending file acquisition separate from process identity
20
+ // validation. Malformed JSON and other I/O failures refuse immediately; even
21
+ // readable bytes grant no RUN permission. retainWindowsTarget must still obtain
22
+ // the original process by PID, creation identity and owned Job membership.
23
+ // Never classify its native errors as a pending publication, fabricate a joined
24
+ // target from an empty Job, or replace identity proof with a retry grace period.
25
+ //
26
+ // Each attempt reads and decodes the current complete bytes with no historical
27
+ // positive cache. Work and temporary memory scale with receipt bytes, and the
28
+ // native file handle closes within os.ReadFile. Only the owning command loop
29
+ // retries; final retirement still reports unknown if original authority could
30
+ // not be acquired. The Windows build constraint owns the native error code.
31
+ func readWindowsStarted(file string) (windowsStarted, bool, error) {
32
+ data, err := os.ReadFile(file)
33
+ if err != nil {
34
+ return windowsStarted{}, os.IsNotExist(err) || errors.Is(err, windows.ERROR_SHARING_VIOLATION), err
35
+ }
36
+ var started windowsStarted
37
+ if err := json.Unmarshal(data, &started); err != nil {
38
+ return windowsStarted{}, false, err
39
+ }
40
+ return started, false, nil
41
+ }
@@ -0,0 +1,76 @@
1
+ //go:build windows
2
+
3
+ package sourceprocess
4
+
5
+ import (
6
+ "errors"
7
+ "os"
8
+ "path/filepath"
9
+ "testing"
10
+
11
+ "golang.org/x/sys/windows"
12
+ )
13
+
14
+ // TestWindowsStartedPublicationDistinguishesPendingFromRefusal verifies native
15
+ // publication acquisition without treating its readable bytes as enrollment.
16
+ //
17
+ // 1. Keep missing input pending, then lock a real complete receipt exclusively.
18
+ // 2. Require an actual sharing violation to remain pending and recover on close.
19
+ // 3. Refuse malformed bytes and a native directory read, then recover a repair.
20
+ //
21
+ // @evidence contracts/testing.md#behavioral-verification Actual Windows CreateFile with no sharing makes the maintained receipt reader fail with ERROR_SHARING_VIOLATION; releasing that handle yields the literal decoded record. Missing input stays pending, while malformed JSON and a real directory read refuse without pending authority. Repair is observed without historical success or failure caching.
22
+ // @evidence contracts/testing.md#independent-expectations Authored schema-one bytes declare PID 7 and creation 42 solely as decoding inputs; Windows' exclusive sharing contract supplies the expected native error. These literals do not certify a real process, which remains retainWindowsTarget's separate admission responsibility.
23
+ // @evidence contracts/testing.md#distinguishing-cases Missing and temporarily unreadable complete publications contrast with readable valid bytes, corrupt JSON and other native I/O refusal. Real release and repair distinguish acquisition recovery from a cached verdict. TestWindowsTargetOwnsSuspendedAdmission separately rejects foreign/missing PID, creation and Job identity; ordinary owned-process E2E covers actual RPC cancellation and cleanup.
24
+ // @evidence contracts/testing.md#execution-ownership This Windows package unit directly calls its owning reader with t.TempDir files and one real exclusive native handle, closed on every path before directory cleanup. It builds or starts no compiler, plugin, product host or consumer and does not replace filesystem/process methods.
25
+ func TestWindowsStartedPublicationDistinguishesPendingFromRefusal(t *testing.T) {
26
+ directory := t.TempDir()
27
+ file := filepath.Join(directory, "started.json")
28
+ if target, pending, err := readWindowsStarted(file); !pending || !os.IsNotExist(err) || target != (windowsStarted{}) {
29
+ t.Fatalf("missing publication: target=%+v pending=%t error=%v", target, pending, err)
30
+ }
31
+ data := []byte(`{"version":1,"pid":7,"created":42}`)
32
+ if err := os.WriteFile(file, data, 0600); err != nil {
33
+ t.Fatal(err)
34
+ }
35
+ name, err := windows.UTF16PtrFromString(file)
36
+ if err != nil {
37
+ t.Fatal(err)
38
+ }
39
+ handle, err := windows.CreateFile(name, windows.GENERIC_READ, 0, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0)
40
+ if err != nil {
41
+ t.Fatal(err)
42
+ }
43
+ defer func() {
44
+ if handle != 0 {
45
+ if err := windows.CloseHandle(handle); err != nil {
46
+ t.Error(err)
47
+ }
48
+ }
49
+ }()
50
+ if target, pending, err := readWindowsStarted(file); !pending || !errors.Is(err, windows.ERROR_SHARING_VIOLATION) || target != (windowsStarted{}) {
51
+ t.Fatalf("locked publication: target=%+v pending=%t error=%v", target, pending, err)
52
+ }
53
+ if err := windows.CloseHandle(handle); err != nil {
54
+ t.Fatal(err)
55
+ }
56
+ handle = 0
57
+ expected := windowsStarted{Version: 1, Pid: 7, Created: 42}
58
+ if target, pending, err := readWindowsStarted(file); err != nil || pending || target != expected {
59
+ t.Fatalf("released publication: target=%+v pending=%t error=%v", target, pending, err)
60
+ }
61
+ if err := os.WriteFile(file, []byte(`{"version":1`), 0600); err != nil {
62
+ t.Fatal(err)
63
+ }
64
+ if target, pending, err := readWindowsStarted(file); err == nil || pending || target != (windowsStarted{}) {
65
+ t.Fatalf("malformed publication: target=%+v pending=%t error=%v", target, pending, err)
66
+ }
67
+ if _, pending, err := readWindowsStarted(directory); err == nil || pending {
68
+ t.Fatalf("directory refusal: pending=%t error=%v", pending, err)
69
+ }
70
+ if err := os.WriteFile(file, data, 0600); err != nil {
71
+ t.Fatal(err)
72
+ }
73
+ if target, pending, err := readWindowsStarted(file); err != nil || pending || target != expected {
74
+ t.Fatalf("repaired publication: target=%+v pending=%t error=%v", target, pending, err)
75
+ }
76
+ }
@@ -2,31 +2,41 @@ package watchhelper
2
2
 
3
3
  import "io"
4
4
 
5
- // Run serves the protocol over stdin and stdout and returns an exit code.
5
+ // Run serves the platform watch protocol over caller-owned stdin and stdout.
6
6
  // End of scanned input, including a scanner error, ends the request stream;
7
7
  // backend failures or an output flush failure can also end service.
8
8
  //
9
- // One loop polls the inotify instance and a wake pipe the stdin reader writes
10
- // after queueing each request. That loop serializes emitted messages and a
11
- // `sync` drains the instance to an observed empty read before answering. This
12
- // is not a total ordering of kernel event creation and request arrival, nor a
13
- // guarantee that no event arrives after that read.
9
+ // Linux uses the inotify protocol. One loop polls the instance and a wake pipe
10
+ // the stdin reader writes after queueing each request. A sync drains the instance
11
+ // to an observed empty read before answering. Windows uses the unplugin broker
12
+ // protocol. One OS-thread-locked completion loop drains currently queued native
13
+ // completions before a request, so a joining subscriber does not receive those
14
+ // older events. Neither frontier totally orders concurrent kernel writes and
15
+ // request arrival, nor guarantees no event arrives after the empty observation.
16
+ // Continuous activity can prolong either drain; elapsed quiet is never proof.
14
17
  //
15
- // stdin and the output streams remain caller-owned. The helper owns its inotify
16
- // and wake descriptors and attempts their closure on every return. Its reader can remain
17
- // blocked on caller input after an early backend failure; the command process
18
- // ends when Run returns, while a library caller must close its input.
19
- // Queue sends are canceled on return, and wake writes are synchronized with
20
- // descriptor closure so a delayed reader cannot write to a reused descriptor.
18
+ // Windows excludes access-only notifications at native acquisition (#1719).
19
+ // NEVER discard an actual mutation by comparing its final hash or timestamps:
20
+ // edits restored to the original bytes still invalidate compiler input proof.
21
+ // Overflow, malformed notifications, directory loss and native failures also
22
+ // withdraw coverage. Linux retains its native loss and directory-loss semantics.
21
23
  //
22
- // @evidence contracts/common.md#principled-implementation The Linux inotify instance reports actual overflow and directory loss. One poll loop drains queued events before acknowledging sync, preserving the protocol's observation ordering without guessing that quiet means unchanged.
23
- // @evidence contracts/common.md#clear-and-simple-design A stdin reader only decodes and queues requests; one helper loop owns watch state, ordered emission and synchronization. Kernel handles remain behind this native backend.
24
- // @evidence contracts/common.md#prohibited-implementation-shortcuts Supported syscalls supply watch capabilities; no foreign runtime method is replaced and no test event substitutes for an actual ready or drain boundary.
25
- // @evidence contracts/common.md#meaningful-documentation Native paragraphs state ordering, exit and stream ownership, including the blocked-reader limitation. The package describes every protocol message separately under documentation-skill guidance.
26
- // @evidence contracts/portability.md#os-neutral-implementation A Linux build constraint isolates inotify and poll. Native directory paths remain syscall inputs, not shell text; other platforms select an explicit unsupported implementation that reports usage code 2 and use adapter-owned watching.
27
- // @evidence contracts/performance.md#efficient-algorithms Each kernel batch is decoded linearly in its bytes; fanout visits K subscribers and JSON output also costs the repeated name bytes. Sorting O(K log K) is shared until membership changes. Removal uses indexed descriptor lookup, and the loop performs no userspace directory traversal. A drain has no event-count or time cap and may keep reading under continuous activity; native syscalls and caller writes have their own blocking costs.
28
- // @evidence contracts/performance.md#reuse-equivalent-work Kernel watch descriptors are shared by subscribers of the same directory. Ordered subscriber IDs are cached per descriptor and invalidated on add, remove or directory loss; all mutation belongs to the same event loop.
29
- // @evidence contracts/performance.md#bound-retention-and-release-resources Deferred cleanup cancels queued reader sends and attempts closure of all three owned descriptors; a mutex prevents late wake writes after closure. Blocking input reads and output writes remain caller-owned. The request channel holds at most 256 values, the scanner buffer is limited to 16 MiB, and event decoding reuses 64 KiB. Subscription maps and order slices have no independent numeric cap; matching remove/gone paths reclaim entries and process exit releases state. Reusing an ID for another descriptor without removal can leave its earlier subscription until that directory is gone or the process exits.
24
+ // The backend owns its native handles. Linux closes its inotify and wake
25
+ // descriptors on return. Windows cancels reads, retains each overlapped buffer
26
+ // until its completion, then closes the completion port. A mutex synchronizes
27
+ // reader wakes with closure, and queued sends are canceled on return. A reader
28
+ // can remain blocked on caller input after early backend failure: the command
29
+ // exits when Run returns, while a library caller must close that input. Blocking
30
+ // output also remains caller-owned; this function has no independent deadline.
31
+ //
32
+ // @evidence contracts/common.md#principled-implementation Native inotify/completion queues establish the actual observation frontier; loss and native failure withdraw coverage. Windows omits access semantics at subscription rather than erasing delivered mutations. A joining subscriber is attached only after currently queued completions were consumed, under the documented concurrent-write limit.
33
+ // @evidence contracts/common.md#clear-and-simple-design A stdin reader only decodes and queues requests; one backend loop owns watch state, ordered emission and synchronization. Platform protocol and kernel handles remain behind this native boundary.
34
+ // @evidence contracts/common.md#prohibited-implementation-shortcuts Supported native APIs supply watch capabilities; no runtime method or system access-time setting is changed. Equal final bytes do not excuse A-B-A writes, and no test event substitutes for native ready/drain authority.
35
+ // @evidence contracts/common.md#meaningful-documentation Native paragraphs state each protocol, frontier limits, mutation policy, EOF cleanup and caller stream ownership. The package documents protocol messages separately; private backend comments explain acquisition, fanout, loss and pending buffer ownership.
36
+ // @evidence contracts/portability.md#os-neutral-implementation Build constraints isolate Linux inotify/poll and Windows overlapped completion APIs. Paths remain native API inputs, not shell text. Other native helper platforms return explicit usage code 2; the unplugin macOS adapter owns its separate FSEvents broker.
37
+ // @evidence contracts/performance.md#efficient-algorithms Each native batch is decoded linearly in its bytes; fanout visits K subscribers and encoded name bytes. Sorting O(K log K) is shared until membership changes. Indexed native descriptors/keys serve removal and completion without userspace project traversal. Continuous activity can prolong drains; native calls and caller writes can block independently of event counts.
38
+ // @evidence contracts/performance.md#reuse-equivalent-work Linux descriptors and Windows exact directory/recursion keys share native observation among subscribers. Ordered subscriber IDs are invalidated on membership changes; each owner retains distinct readiness and event policy. One loop serializes mutation, and Windows completion keys are never reused for another watch.
39
+ // @evidence contracts/performance.md#bound-retention-and-release-resources Deferred cleanup cancels queued sends and closes owned handles, retaining Windows buffers until cancellation completion. A mutex prevents late wake operations against a closed/reused native handle. The request channel holds at most 256 values, scanner framing is limited to 16 MiB and native buffers use 64 KiB each. Active subscriptions/order slices have no numeric cap; removal/loss reclaim live entries, with retiring Windows entries retained until completion. Caller stream blocking has no independent bound. Linux callers must remove before reusing an ID for a different descriptor, or its earlier subscription lasts until loss or process exit.
30
40
  func Run(stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
31
41
  return run(stdin, stdout, stderr)
32
42
  }
@@ -1,4 +1,4 @@
1
- //go:build !linux
1
+ //go:build !linux && !windows
2
2
 
3
3
  package watchhelper
4
4
 
@@ -7,10 +7,10 @@ import (
7
7
  "io"
8
8
  )
9
9
 
10
- // run reports that the helper serves Linux only. Windows and macOS watch in
11
- // the adapter's own broker instead.
10
+ // run reports that the helper serves Linux and Windows. macOS uses the
11
+ // adapter's FSEvents broker instead.
12
12
  // This alternative acquires no watch or reader task and returns usage code 2.
13
13
  func run(stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
14
- fmt.Fprintln(stderr, "ttsc __watch: only Linux is supported")
14
+ fmt.Fprintln(stderr, "ttsc __watch: only Linux and Windows are supported")
15
15
  return 2
16
16
  }