@bli-cockpit/cli 0.2.256 → 0.2.257

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.
@@ -16,4 +16,23 @@ import { OUTSIDE_APPROVED_ROOT_REASONS } from "./session-sync-health.js";
16
16
  */
17
17
  export function rootScopeForReason(reason) {
18
18
  return OUTSIDE_APPROVED_ROOT_REASONS.has(reason) ? "outside" : "inside";
19
+ }
20
+ /**
21
+ * The terminal attribution states: attribution finished and named no repo. They
22
+ * are labels, never a reason to withhold (session-first collection).
23
+ */
24
+ const TERMINAL_ATTRIBUTION_STATES = new Set(["ambiguous", "unattributed", "skipped"]);
25
+ /**
26
+ * Reasons a session carries no workspace of its own that are NOT an outside-root
27
+ * fact, so `rootScopeForReason` keeps calling them "inside". Today: a transcript
28
+ * with no cwd, workspace or origin signal at all (`no_repo_signals`).
29
+ */
30
+ export const NO_WORKSPACE_REASONS = new Set(["no_repo_signals"]);
31
+ /**
32
+ * Does this session still lack a workspace the upload can stand in? Every
33
+ * terminal state without a worktree rides the collector's synthetic workspace,
34
+ * whatever its reason; attribution stays exactly as scored.
35
+ */
36
+ export function needsTransportWorkspace(result) {
37
+ return !result.worktree && TERMINAL_ATTRIBUTION_STATES.has(result.state);
19
38
  }
@@ -24,7 +24,7 @@ import fs from "node:fs/promises";
24
24
  import path from "node:path";
25
25
  import { getCollectorRuntimePaths } from "../local-state.js";
26
26
  import { repoFingerprintFromLocalRoot, stableWorktreeFingerprint, } from "../repo-identity.js";
27
- import { rootScopeForReason } from "./agent-session-root-scope.js";
27
+ import { NO_WORKSPACE_REASONS, needsTransportWorkspace, rootScopeForReason } from "./agent-session-root-scope.js";
28
28
  /**
29
29
  * The label the sync path writes for a report with no real worktree
30
30
  * (`session-sync-record.ts`). Reused verbatim: one label, not a second one.
@@ -64,18 +64,22 @@ export function isOutsideRootCandidate(candidate) {
64
64
  export function labelOutsideRootCandidates(candidates, workspace) {
65
65
  let codex = 0;
66
66
  let claude = 0;
67
+ let noRepoSignals = 0;
67
68
  const labelled = candidates.map((candidate) => {
68
- if (candidate.worktree || !isOutsideRootCandidate(candidate))
69
+ if (!needsTransportWorkspace(candidate))
69
70
  return candidate;
70
71
  if (candidate.source === "codex")
71
72
  codex += 1;
72
73
  else
73
74
  claude += 1;
75
+ if (NO_WORKSPACE_REASONS.has(candidate.reason))
76
+ noRepoSignals += 1;
74
77
  return { ...candidate, worktree: workspace };
75
78
  });
76
79
  console.error("[backfill] outside-root sessions labelled for upload", JSON.stringify({
77
- root_scope: "outside",
80
+ reason: "unattributed_sessions_labelled_for_upload",
78
81
  count: codex + claude,
82
+ no_repo_signals_count: noRepoSignals,
79
83
  codex_count: codex,
80
84
  claude_count: claude,
81
85
  candidate_count: candidates.length,
@@ -21,7 +21,7 @@ export async function runCockpitCli(argv, io) {
21
21
  }
22
22
 
23
23
  if (command === "--version" || command === "-V" || command === "version") {
24
- writeLine(io?.stdout ?? process.stdout, "0.2.256");
24
+ writeLine(io?.stdout ?? process.stdout, "0.2.257");
25
25
  return 0;
26
26
  }
27
27
 
@@ -47,7 +47,7 @@ import { isRedactionStubObjectKey } from "../cursors/raw-evidence-stub-entry.js"
47
47
  import { describeError } from "../health-detail.js";
48
48
  import { getCollectorRuntimePaths } from "../local-state.js";
49
49
  import { isMissingFileError, writeJsonFileAtomic, writeJsonFile } from "../local-state-files.js";
50
- import { rootScopeForReason } from "./agent-session-root-scope.js";
50
+ import { NO_WORKSPACE_REASONS, needsTransportWorkspace, rootScopeForReason } from "./agent-session-root-scope.js";
51
51
  import { ensureOutsideRootsWorkspace, isOutsideRootsWorkspace, outsideRootsWorkspace, } from "./backfill-outside-roots.js";
52
52
  /** Most outside-root sessions that upload bytes in one tick. 4 ticks an hour, so up to 3,840 a day. */
53
53
  export const OUTSIDE_ROOT_SESSIONS_PER_TICK = 40;
@@ -112,7 +112,7 @@ export function widenForCatchUp(sinceMinutes, catchUpPending, normalMinutes) {
112
112
  * than the horizon (that is backfill's).
113
113
  */
114
114
  export function outsideRootEntryOwesUpload(entry, now = new Date()) {
115
- if (rootScopeForReason(entry.reason) !== "outside")
115
+ if (rootScopeForReason(entry.reason) !== "outside" && !NO_WORKSPACE_REASONS.has(entry.reason))
116
116
  return false;
117
117
  if (entry.uploaded_object_key && !isRedactionStubObjectKey(entry.uploaded_object_key))
118
118
  return false;
@@ -120,9 +120,13 @@ export function outsideRootEntryOwesUpload(entry, now = new Date()) {
120
120
  return false;
121
121
  return now.getTime() - entry.file_mtime_ms <= OUTSIDE_ROOT_HORIZON_MINUTES * 60_000;
122
122
  }
123
- /** Is this scan result a session outside every approved root with no worktree of its own? */
123
+ /**
124
+ * Is this scan result a session with no worktree of its own that attribution
125
+ * left unattributed (outside every approved root, no repo signals, or any other
126
+ * terminal state)? All of them ride the synthetic workspace: a label is never a gate.
127
+ */
124
128
  function isUnclaimedOutsideRootResult(result) {
125
- return !result.worktree && rootScopeForReason(result.reason) === "outside";
129
+ return needsTransportWorkspace(result);
126
130
  }
127
131
  /** Already durable remotely with the bytes the scan just read: it needs no upload. */
128
132
  function isDurableAndUnchanged(result, entry) {
@@ -224,7 +228,8 @@ export async function admitOutsideRootSessions(options) {
224
228
  },
225
229
  outsideRoots: { workspace, holds, counts },
226
230
  };
227
- console.error("[session-sync] outside-root raw upload", JSON.stringify({ reason: "outside_root_sessions_admitted", ...counts, ...budget }));
231
+ const noRepoSignals = admitted.filter((sighting) => NO_WORKSPACE_REASONS.has(sighting.result.reason)).length;
232
+ console.error("[session-sync] outside-root raw upload", JSON.stringify({ reason: "outside_root_sessions_admitted", no_repo_signals_riding: noRepoSignals, ...counts, ...budget }));
228
233
  return labelled;
229
234
  }
230
235
  /** Session ids and queue turns only; no source paths or transcript content. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/cli",
3
- "version": "0.2.256",
3
+ "version": "0.2.257",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {