Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ dist/
.automaker/features/
.automaker/checkpoints/
.automaker/trajectory/

# auto-mode runtime lock — never commit (tracking it conflicts every concurrent PR)
.automaker-lock
22 changes: 22 additions & 0 deletions lib/workspace-config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,27 @@ export const WORKSPACE_STANDARD = /** @type {Rule[]} */ ([
ok: (m) => !m.hasFile('.beads/beads.db'),
fix: 'git rm --cached .beads/beads.db and add it to .gitignore',
},
{
id: 'automaker-lock-gitignored',
description: '.automaker-lock is gitignored (auto-mode runtime lock — must never be committed)',
severity: 'error',
ok: (m) => resolveIgnored(m, '.automaker-lock', ['.automaker-lock']),
fix: 'Add `.automaker-lock` to .gitignore',
},
{
// The headline rule: a *tracked* .automaker-lock is what bites. auto-mode
// rewrites this lock on every run, so once it's committed every concurrent
// auto-mode PR mutates it and they all collide on it — the whole branch's
// PRs go CONFLICTING/DIRTY the moment one merges (observed fleet-wide:
// protoContent, protoMaker #4109/#4120). Gitignoring alone does NOT fix an
// already-tracked file — it must be untracked.
id: 'automaker-lock-not-committed',
description:
'.automaker-lock is NOT committed (a tracked runtime lock makes every concurrent auto-mode PR conflict on it)',
severity: 'error',
ok: (m) => !m.hasFile('.automaker-lock'),
fix: 'git rm --cached .automaker-lock and add it to .gitignore',
},
{
id: 'automaker-settings-committed',
description: '.automaker/settings.json is committed (versioned per-repo agent baseline)',
Expand Down Expand Up @@ -322,6 +343,7 @@ export const REQUIRED_GITIGNORE = [
'.automaker/features/',
'.automaker/checkpoints/',
'.automaker/trajectory/',
'.automaker-lock',
];

/**
Expand Down
11 changes: 7 additions & 4 deletions test/workspace-config.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function goodManifest() {
'.automaker/features/',
'.automaker/checkpoints/',
'.automaker/trajectory/',
'.automaker-lock',
].join('\n')
);
}
Expand Down Expand Up @@ -134,7 +135,7 @@ test('missing automaker transient ignores is a WARN, not an error', () => {
// Everything required present + ignored, but no transient-dir ignores.
const m = manifest(
['.beads/issues.jsonl', '.automaker/settings.json'],
'.beads/beads.db\n.worktrees/'
'.beads/beads.db\n.worktrees/\n.automaker-lock'
);
const r = evaluateStandard(m);
assert.equal(r.ok, true, 'warn-only violation should not fail the standard');
Expand All @@ -151,13 +152,15 @@ test('missing automaker transient ignores is a WARN, not an error', () => {

test('a bare repo (nothing) reports all errors + the warn', () => {
const r = evaluateStandard(manifest([], ''));
// 4 errors: issues.jsonl, beads-db-gitignored, automaker-settings, worktrees
// (beads-db-not-committed passes — it's absent — so it is NOT a violation)
assert.equal(r.errorCount, 4);
// 5 errors: issues.jsonl, beads-db-gitignored, automaker-settings, worktrees,
// automaker-lock-gitignored. (beads-db-not-committed AND automaker-lock-not-committed
// pass — both absent — so they are NOT violations.)
assert.equal(r.errorCount, 5);
// 2 warns: automaker-transient-gitignored + workflow-security-lint
assert.equal(r.warnCount, 2);
assert.equal(r.ok, false);
assert.ok(r.passed.includes('beads-db-not-committed'));
assert.ok(r.passed.includes('automaker-lock-not-committed'));
});

test('each rule carries a fix hint', () => {
Expand Down
Loading