toolroll 0.9.88 → 0.9.89

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.
@@ -20,6 +20,7 @@ import { basename, delimiter, dirname, isAbsolute, join, resolve } from "node:pa
20
20
  import { run } from "./exec.js";
21
21
  import { ENV, ENV_ALLOWLIST } from "./remote-main.js";
22
22
  import { WORKTREE_TIMEOUT_MS } from "./worktree.js";
23
+ import { FOLDER_DIDNT_ANSWER, folderAnswers } from "./folder-gate.js";
23
24
  /** Fixed on every teardown command, ahead of its own arguments. */
24
25
  export const TEARDOWN_FLAGS = [
25
26
  "--no-optional-locks", "-c", "core.hooksPath=/dev/null", "-c", "core.fsmonitor=false", "-c", "protocol.ext.allow=never",
@@ -346,8 +347,16 @@ function gitDirsOf(checkout) {
346
347
  catch {
347
348
  return null;
348
349
  }
350
+ // Where the checkout's own files point (a build can write both): read, in the console, only while that folder
351
+ // answered a look lately, and never vouched for by the checkout pool's spelling (folder-gate.ts). Held back: not one
352
+ // git would read the same way, so every caller refuses.
353
+ if (gitdir !== join(checkout, ".git") && !folderAnswers(gitdir, true))
354
+ return null;
349
355
  const named = regularFile(join(gitdir, "commondir"))?.trim();
350
- return { gitdir, common: named === undefined || named === "" ? gitdir : resolve(gitdir, named) };
356
+ const common = named === undefined || named === "" ? gitdir : resolve(gitdir, named);
357
+ if (common !== gitdir && !folderAnswers(common, true))
358
+ return null;
359
+ return { gitdir, common };
351
360
  }
352
361
  /** What includeIn answers when Git didn't finish parsing the file in time: still a refusal, named as what it is. */
353
362
  export const INCLUDE_PARSE_TIMED_OUT = "Git didn't finish reading it in time";
@@ -1062,6 +1071,9 @@ export function checkoutGit(inner = run, mode = "build", binding = {}) {
1062
1071
  if (file !== "git")
1063
1072
  return inner(file, args, options);
1064
1073
  const cwd = options?.cwd;
1074
+ // In the console, a checkout whose folder hasn't answered a look lately is not read on its thread (folder-gate.ts).
1075
+ if (cwd !== undefined && !folderAnswers(cwd))
1076
+ return refused(FOLDER_DIDNT_ANSWER);
1065
1077
  if (cwd === undefined || checkoutIncludes(cwd))
1066
1078
  return refused("the checkout's git config includes another file, or couldn't be read as a plain file");
1067
1079
  // git-lfs also reads .lfsconfig from the checkout: one that names an extension (a command it would run) is refused.
@@ -1158,6 +1170,9 @@ export function viewedGitSync(cwd, args, options = {}) {
1158
1170
  const expire = () => { if (limit !== undefined && limit() <= 0)
1159
1171
  throw new Error("the read's deadline passed"); };
1160
1172
  expire();
1173
+ // In the console, a checkout whose folder hasn't answered a look lately is not read on its thread (folder-gate.ts).
1174
+ if (!folderAnswers(cwd))
1175
+ throw new Error(FOLDER_DIDNT_ANSWER);
1161
1176
  if (checkoutIncludes(cwd, gitFile, limit))
1162
1177
  throw new Error("the checkout's git config includes another file, or couldn't be read as a plain file");
1163
1178
  expire();
@@ -74,6 +74,7 @@ import { noteAssignmentStatus } from "./assignment-status.js";
74
74
  import { completionProblem } from "./landed-cleanup.js";
75
75
  import { COMPLETION_ACTION, LANDED_ACTOR } from "./result-completion.js";
76
76
  import { optionsProblem, PROPOSAL_ID, proposalFileOf, proposalIdOf, readYouiyouxProposal, readYouiyouxRequest, readYouiyouxRequestTask, YOUIYOUX_CARD_KIND, YOUIYOUX_FILED_VIA, YOUIYOUX_HOME_PATH } from "./contracts/youiyoux-request.js";
77
+ import { FolderUnavailable } from "./folder-gate.js";
77
78
  export { YOUIYOUX_FILED_VIA };
78
79
  /** Who files the tasks: an automation, like a flow trigger. */
79
80
  export const YOUIYOUX_ACTOR = "youiyoux";
@@ -605,7 +606,16 @@ function intake(store, given, now, io, options) {
605
606
  const pass = NOTHING();
606
607
  const none = { pass, repo: null };
607
608
  // The project as Toolroll recorded it, resolved: a folder that isn't one of its projects is never read.
608
- const repo = canonicalProject(given);
609
+ let repo;
610
+ // A folder the console's gate holds back (folder-gate.ts) isn't read this pass.
611
+ try {
612
+ repo = canonicalProject(given);
613
+ }
614
+ catch (error) {
615
+ if (error instanceof FolderUnavailable)
616
+ return none;
617
+ throw error;
618
+ }
609
619
  if (repo === null || !store.listProjects().some(one => one.path === repo))
610
620
  return none;
611
621
  const row = intakeRow(store, repo);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolroll",
3
- "version": "0.9.88",
3
+ "version": "0.9.89",
4
4
  "description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
5
5
  "directories": {
6
6
  "doc": "docs"