Merges with nothing to change succeed; submissions are normalized to LF
A function resubmitted unchanged (port-first) made the merge a no-op, and git refused the empty commit, so the merge retried forever and blocked the queue. CRLF from Windows workspaces is normalized on arrival. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNwtXPYqFu3oKaaXwYwNWW
This commit is contained in:
parent
97d3d65364
commit
db9c5b8f89
3 changed files with 14 additions and 0 deletions
|
|
@ -322,10 +322,14 @@ export function createApp(ctx) {
|
|||
return guided({ attemptId: r.attempts[0].attemptId, batchId: r.batchId, state: 'queued' });
|
||||
});
|
||||
|
||||
// The repository stores LF line endings; submissions from Windows workspaces may carry CRLF.
|
||||
const lf = (files) => Object.fromEntries(Object.entries(files).map(([f, c]) => [f, typeof c === 'string' ? c.replace(/\r\n/g, '\n') : c]));
|
||||
|
||||
/** Validates a submission body and queues it as one batch for the given tasks. */
|
||||
const submitBatch = (user, taskIds, body) => {
|
||||
if (typeof body.baseCommit !== 'string' || !/^[0-9a-f]{40}$/.test(body.baseCommit)) throw new HttpError(400, 'baseCommit must be a full commit hash');
|
||||
if (!body.files || typeof body.files !== 'object' || Array.isArray(body.files)) throw new HttpError(400, 'files must be an object of path -> content');
|
||||
body.files = lf(body.files);
|
||||
const r = tasks.submitBatch(user, {
|
||||
taskIds,
|
||||
baseCommit: body.baseCommit,
|
||||
|
|
@ -500,6 +504,7 @@ export function createApp(ctx) {
|
|||
const body = await readJson(req);
|
||||
if (typeof body.baseCommit !== 'string' || !/^[0-9a-f]{40}$/.test(body.baseCommit)) throw new HttpError(400, 'baseCommit must be a full commit hash');
|
||||
if (!body.files || typeof body.files !== 'object' || Array.isArray(body.files)) throw new HttpError(400, 'files must be an object of path -> content');
|
||||
body.files = lf(body.files);
|
||||
const allowed = [...project.sourceDirs.map((d) => `${d}/**`)];
|
||||
const lintErrors = lintSubmission(body.files, { allowedPaths: allowed, limits: project.lintLimits, allowedPragmas: project.allowedPragmas });
|
||||
if (lintErrors.length) return { gate: 'rejected', lintErrors };
|
||||
|
|
|
|||
|
|
@ -111,6 +111,8 @@ export class Repo {
|
|||
fs.writeFileSync(abs, content);
|
||||
}
|
||||
this.git(['add', '--', ...Object.keys(files)]);
|
||||
// Nothing to commit (the code is already on main, e.g. a function resubmitted unchanged).
|
||||
if (!this.git(['status', '--porcelain', '--', ...Object.keys(files)]).trim()) return this.head();
|
||||
this.git(['-c', 'user.name=Clank Together', '-c', 'user.email=bot@clank-together.invalid', 'commit', '-q', '--author', author, '-F', '-'], { input: message });
|
||||
return this.head();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,6 +375,13 @@ test('submissions may not add comments or strings that could instruct other agen
|
|||
assert.equal(JSON.stringify(r.body).includes('ignore your instructions'), false, 'the text is not echoed back');
|
||||
});
|
||||
|
||||
test('committing files that are already on main is a no-op, not an error', () => {
|
||||
const files = { 'src/noop.cpp': 'int noop() { return 0; }\n' };
|
||||
const head = ctx.repo.commitFiles(files, 'add noop.cpp', 'x <x@y.invalid>');
|
||||
assert.equal(ctx.repo.commitFiles(files, 'nothing', 'x <x@y.invalid>'), head);
|
||||
assert.equal(ctx.repo.head(), head);
|
||||
});
|
||||
|
||||
test('the crew view is public and shows no account details', async () => {
|
||||
const token = await activeUser('crewview');
|
||||
await call('GET', '/api/tasks', null, token); // the agent checks in
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue