@indigoai-us/hq-cloud 5.37.0 → 5.38.0

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.
package/src/cli/sync.ts CHANGED
@@ -135,12 +135,47 @@ export type SyncProgressEvent =
135
135
  * `journalEtag` and `remoteEtag` are sentinel strings
136
136
  * (`<legacy-no-etag>` / `<unknown>`) — do not render as ETags.
137
137
  * Consumers should branch on `reason`, not on the etag values.
138
+ *
139
+ * - `"bulk-asymmetry"`: the bulk-asymmetry circuit-breaker tripped
140
+ * (see `delete-refused-bulk-asymmetry`). Each candidate that would
141
+ * have been deleted also surfaces here for path-level visibility.
142
+ * `journalEtag` and `remoteEtag` are sentinel strings
143
+ * (`<bulk-asymmetry>` / `<not-checked>`) since no HEAD was issued.
138
144
  */
139
145
  type: "delete-refused-stale-etag";
140
146
  path: string;
141
147
  journalEtag: string;
142
148
  remoteEtag: string;
143
- reason: "stale-etag" | "legacy-no-etag";
149
+ reason: "stale-etag" | "legacy-no-etag" | "bulk-asymmetry";
150
+ }
151
+ | {
152
+ /**
153
+ * Emitted at most ONCE per `share()` call when the bulk-asymmetry
154
+ * circuit-breaker tripped: a large fraction of the in-scope journal
155
+ * entries are missing locally, suggesting the local mirror is corrupt
156
+ * (moved hqRoot, partial restore, fresh clone over inherited state,
157
+ * unmounted volume, accidental `rm -rf`) rather than a deliberate
158
+ * delete. The engine refuses to convert the missing entries into
159
+ * remote `DeleteObject` calls; every refused candidate also surfaces
160
+ * via `delete-refused-stale-etag` with `reason: "bulk-asymmetry"`.
161
+ *
162
+ * Trip condition: `candidates >= 10 AND candidates / inScope >= 0.10`.
163
+ * Bypass: set `HQ_SYNC_DELETE_BULK_OVERRIDE=1` (truthy: `1|true|yes`,
164
+ * case-insensitive) OR pass `propagateDeletePolicy: "all"` (the
165
+ * existing emergency-reconcile policy which already opts out of
166
+ * safety gates).
167
+ *
168
+ * `candidates` is the number of journal entries whose local file is
169
+ * missing AND would have been delete-candidates absent the guard;
170
+ * `inScope` is the total journal entries under the share's scope
171
+ * roots; `ratio = candidates / inScope`; `samplePaths` carries up to
172
+ * 10 refused keys for diagnostic display.
173
+ */
174
+ type: "delete-refused-bulk-asymmetry";
175
+ candidates: number;
176
+ inScope: number;
177
+ ratio: number;
178
+ samplePaths: string[];
144
179
  }
145
180
  | {
146
181
  /**