@miraland-labs/conduit-bridge 0.16.6 → 0.16.7

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/dist/driver.js CHANGED
@@ -99,6 +99,16 @@ export function buildAssignmentPrompt(context) {
99
99
  }
100
100
  if (changeScope?.length)
101
101
  lines.push("", `CHANGE SCOPE — only modify paths under\n${changeScope.map((item) => `- ${item}`).join("\n")}`);
102
+ const integrationObligations = spec.integration_obligations ?? [];
103
+ if (integrationObligations.length) {
104
+ lines.push("", "INTEGRATION OBLIGATIONS — finalize fails unless these hold");
105
+ for (const obligation of integrationObligations) {
106
+ if (obligation.kind !== "symbol_referenced_from")
107
+ continue;
108
+ const globs = obligation.from_globs?.length ? obligation.from_globs.join(", ") : "runtime entrypoints";
109
+ lines.push(`- symbol "${obligation.symbol}" must be referenced from ${globs}`);
110
+ }
111
+ }
102
112
  if (context.normativeRefs?.length) {
103
113
  lines.push("", "NORMATIVE REFERENCES — read-only contract files; your implementation must align. Do not edit anything under .conduit/normative/.");
104
114
  for (const ref of context.normativeRefs) {
@@ -22,8 +22,14 @@ export async function ensureChangeEvidence(input) {
22
22
  return input.report;
23
23
  const paths = input.changedPaths
24
24
  ?? (input.baseCommit ? await changedPathsSince(input.workspace, input.baseCommit) : null);
25
- if (!paths?.length)
25
+ if (paths === null)
26
26
  return input.report;
27
+ if (!paths.length) {
28
+ return {
29
+ ...input.report,
30
+ evidence: input.report.evidence.filter((item) => item.kind !== "change"),
31
+ };
32
+ }
27
33
  const stat = input.baseCommit ? await diffStatSince(input.workspace, input.baseCommit) : null;
28
34
  const baseLabel = input.baseCommit?.slice(0, 12) ?? "base";
29
35
  const details = [
package/dist/execution.js CHANGED
@@ -1209,10 +1209,9 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
1209
1209
  repositoryFingerprint: executionContract.repository_fingerprint,
1210
1210
  });
1211
1211
  // Ask git what changed before trusting what the report says changed.
1212
- const startCommit = executionContract.requested_base_commit && executionContract.claimed_head
1213
- ? await resolveAttemptStartCommit(workspace, executionContract.requested_base_commit, executionContract.claimed_head)
1212
+ const actualPaths = /^[0-9a-f]{7,40}$/i.test(worktreeStart)
1213
+ ? await changedPathsSince(attemptWorkspace, worktreeStart)
1214
1214
  : null;
1215
- const actualPaths = startCommit ? await changedPathsSince(attemptWorkspace, startCommit) : null;
1216
1215
  // Mechanical test path: agent often forgets verbatim make test / npm test output.
1217
1216
  report = await ensureTestEvidence({
1218
1217
  workspace: attemptWorkspace,
@@ -1223,7 +1222,7 @@ async function runClaimedAssignment(client, config, driver, workspace, brief, ta
1223
1222
  });
1224
1223
  report = await ensureChangeEvidence({
1225
1224
  workspace: attemptWorkspace,
1226
- baseCommit: startCommit,
1225
+ baseCommit: /^[0-9a-f]{7,40}$/i.test(worktreeStart) ? worktreeStart : startCommit,
1227
1226
  report,
1228
1227
  spec,
1229
1228
  grants,
@@ -41,7 +41,7 @@ export function parseIntegrationObligations(value) {
41
41
  async function symbolReferencedFrom(workspace, symbol, globs) {
42
42
  const pathspecs = globs.map((glob) => `:(glob)${glob}`);
43
43
  try {
44
- const { stdout } = await execFileAsync("git", ["-C", workspace, "grep", "-l", "-F", symbol, "--", ...pathspecs], { timeout: 30_000, maxBuffer: 4_000_000 });
44
+ const { stdout } = await execFileAsync("git", ["-C", workspace, "grep", "-l", "-F", "--untracked", symbol, "--", ...pathspecs], { timeout: 30_000, maxBuffer: 4_000_000 });
45
45
  return stdout.trim().length > 0;
46
46
  }
47
47
  catch (error) {
@@ -84,7 +84,7 @@ export function agentClaimsRepositoryWork(text) {
84
84
  }
85
85
  if (NO_CHANGE_OUTCOME.test(text))
86
86
  return false;
87
- return /\b(rewrote|replaced|implemented|fetcher\.rs|head_commit|"changes"\s*:\s*\[)/i.test(text);
87
+ return /\b(rewrote|replaced|implemented|fetcher\.rs|head_commit|"changes"\s*:\s*\[\s*")/i.test(text);
88
88
  }
89
89
  /** Parsed Delivery claims repository work (stricter than free text). */
90
90
  export function reportClaimsRepositoryWork(report) {
@@ -48,9 +48,17 @@ function validateNormativePath(path) {
48
48
  function cacheDir(sourceWorkspace, repository) {
49
49
  return join(sourceWorkspace, ".conduit", "normative-cache", fingerprint(repository).replaceAll("/", "__"));
50
50
  }
51
+ const GIT_NO_PROMPT = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
51
52
  function cloneUrl(repository) {
52
53
  return `https://${fingerprint(repository)}.git`;
53
54
  }
55
+ function assertPublicGitHost(repository) {
56
+ const host = fingerprint(repository).split("/")[0] ?? "";
57
+ if (!host || !host.includes(".") || host === "localhost" || host.endsWith(".localhost")
58
+ || host.endsWith(".internal") || /^\d/.test(host) || host.includes(":")) {
59
+ throw new Error(`${NORMATIVE_REF_PREFIX}: repository host is not a public git host`);
60
+ }
61
+ }
54
62
  async function pathExists(path) {
55
63
  try {
56
64
  await access(path, constants.F_OK);
@@ -72,7 +80,11 @@ async function readPinnedRevision(repoDir) {
72
80
  return null;
73
81
  }
74
82
  }
83
+ async function gitNoPrompt(args, timeout, maxBuffer) {
84
+ await execFileAsync("git", args, { timeout, maxBuffer, env: GIT_NO_PROMPT });
85
+ }
75
86
  async function ensureRepoCache(sourceWorkspace, ref) {
87
+ assertPublicGitHost(ref.repository);
76
88
  const dir = cacheDir(sourceWorkspace, ref.repository);
77
89
  const wantedRevision = ref.revision ?? "HEAD";
78
90
  const marker = join(dir, ".conduit-normative-revision");
@@ -89,7 +101,7 @@ async function ensureRepoCache(sourceWorkspace, ref) {
89
101
  }
90
102
  cloneArgs.push(cloneUrl(ref.repository), dir);
91
103
  try {
92
- await execFileAsync("git", cloneArgs, { timeout: 180_000, maxBuffer: 4_000_000 });
104
+ await gitNoPrompt(cloneArgs, 180_000, 4_000_000);
93
105
  }
94
106
  catch (error) {
95
107
  const message = error instanceof Error ? error.message : String(error);
@@ -98,32 +110,23 @@ async function ensureRepoCache(sourceWorkspace, ref) {
98
110
  }
99
111
  else if (ref.revision && cachedRevision !== wantedRevision) {
100
112
  try {
101
- if (/^[0-9a-f]{40}$/i.test(ref.revision)) {
102
- await execFileAsync("git", ["-C", dir, "fetch", "--depth", "1", "origin", ref.revision], {
103
- timeout: 120_000,
104
- maxBuffer: 4_000_000,
105
- });
106
- await execFileAsync("git", ["-C", dir, "checkout", "--detach", "FETCH_HEAD"], {
107
- timeout: 60_000,
108
- maxBuffer: 1_000_000,
109
- });
110
- }
111
- else {
112
- await execFileAsync("git", ["-C", dir, "fetch", "--depth", "1", "origin", ref.revision], {
113
- timeout: 120_000,
114
- maxBuffer: 4_000_000,
115
- });
116
- await execFileAsync("git", ["-C", dir, "checkout", "--detach", "FETCH_HEAD"], {
117
- timeout: 60_000,
118
- maxBuffer: 1_000_000,
119
- });
120
- }
113
+ await gitNoPrompt(["-C", dir, "fetch", "--depth", "1", "origin", ref.revision], 120_000, 4_000_000);
114
+ await gitNoPrompt(["-C", dir, "checkout", "--detach", "FETCH_HEAD"], 60_000, 1_000_000);
121
115
  }
122
116
  catch (error) {
123
117
  const message = error instanceof Error ? error.message : String(error);
124
118
  throw new Error(`${NORMATIVE_REF_PREFIX}: could not checkout ${ref.revision} in ${fingerprint(ref.repository)} (${message})`);
125
119
  }
126
120
  }
121
+ else if (!ref.revision) {
122
+ try {
123
+ await gitNoPrompt(["-C", dir, "fetch", "--depth", "1", "origin", "HEAD"], 120_000, 4_000_000);
124
+ await gitNoPrompt(["-C", dir, "checkout", "--detach", "FETCH_HEAD"], 60_000, 1_000_000);
125
+ }
126
+ catch {
127
+ // Keep the last successful cache when origin is unreachable (offline tests, private host already rejected).
128
+ }
129
+ }
127
130
  const revision = (await readPinnedRevision(dir)) ?? wantedRevision;
128
131
  await writeFile(marker, `${revision}\n`, "utf8");
129
132
  return { dir, revision };
@@ -170,10 +173,12 @@ export function assertNormativeRefsMaterialized(refs, materialized) {
170
173
  }
171
174
  }
172
175
  }
176
+ function isConduitPath(path) {
177
+ const normalized = path.replaceAll("\\", "/");
178
+ return normalized === ".conduit" || normalized.startsWith(".conduit/");
179
+ }
173
180
  async function markerPresent(workspace, marker, paths) {
174
- const args = ["-C", workspace, "grep", "-l", "-F", marker];
175
- if (paths?.length)
176
- args.push("--", ...paths);
181
+ const args = ["-C", workspace, "grep", "-l", "-F", "--untracked", marker, "--", ...paths];
177
182
  try {
178
183
  const { stdout } = await execFileAsync("git", args, {
179
184
  timeout: 30_000,
@@ -192,14 +197,19 @@ export async function checkNormativeMarkers(workspace, refs, changedPaths) {
192
197
  const scoped = refs.filter((ref) => ref.markers?.length);
193
198
  if (!scoped.length)
194
199
  return { ok: true };
195
- const searchPaths = changedPaths?.length ? changedPaths : null;
200
+ if (changedPaths === null) {
201
+ return { ok: false, failures: ["changed files are unknown; required markers cannot be verified"] };
202
+ }
203
+ const searchPaths = changedPaths.filter((path) => path && !isConduitPath(path));
204
+ if (!searchPaths.length) {
205
+ return { ok: false, failures: ["no changed files to search for required markers"] };
206
+ }
196
207
  const failures = [];
197
208
  for (const ref of scoped) {
198
209
  for (const marker of ref.markers ?? []) {
199
210
  const found = await markerPresent(workspace, marker, searchPaths);
200
211
  if (!found) {
201
- const scope = searchPaths ? "changed files" : "workspace";
202
- failures.push(`marker "${marker}" from ${ref.label} not found in ${scope}`);
212
+ failures.push(`marker "${marker}" from ${ref.label} not found in changed files`);
203
213
  }
204
214
  }
205
215
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraland-labs/conduit-bridge",
3
- "version": "0.16.6",
3
+ "version": "0.16.7",
4
4
  "description": "Conduit Bridge CLI — join, connect, disconnect, multi-driver lanes, and run Claude Code / Codex / Cursor / OpenCode / Pi / Kiro / Antigravity / Grok Build agents for a Conduit organization",
5
5
  "type": "module",
6
6
  "bin": {