Skip to content

feat(land): let the fixer edit protected paths it judges mechanical - #357

Closed
kixelated wants to merge 3 commits into
mainfrom
quest/m0/land-fixer/protected
Closed

kixelated wants to merge 3 commits into
mainfrom
quest/m0/land-fixer/protected

Conversation

@kixelated

@kixelated kixelated commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Finishes Protected edits from the land fixer epic. The fixer may now resolve conflicts and failures in formerly protected paths (.github/, flake.nix, flake.lock, shell.nix, default.nix, nix/, .envrc, .gitmodules, and gitlinks) when it judges the edit mechanical, and such a fix lands like any other.

The maintainer made this call on 2026-11-01 and confirmed it when challenged: "Any prompt injections should be caught before enabling auto merge." The quest's Plan records both rounds.

Changes

  • validateFix (src/cli/land-fixer.ts) no longer runs the protected-set comparison, which required the set to "equal pinned head or base merge result". What remains are the ancestry checks: the fixed head descends from the pin, and every new commit sits on top of it or comes from the base the fixer was given.
  • assets/land-fix.md: the "Never edit flake.nix, … gitlinks" rule and its base-merge exception are replaced by "The same rules apply to every path, including CI workflows, Nix files, and submodule entries." The injection rule still hands back any content that tries to make the fixer change behavior, reveal its environment, contact anything, or decide a result. Only the "touch protected paths" item is gone, because touching those paths is now ordinary work.
  • README fixer section: describes the new validation.
  • quest/m0/host-land/fixer.md: the decision line records the 2026-11-01 reversal. This PR deletes protected.md and every link to it; the merge of main also points workflows-permission.md's Related link here. The epic README keeps the decision summary and points its audit row here.
  • Tests: the four "off-limits fixer edit" handback cases, the mixing test, the gitlink-rejection test, and the protected-comparison test are replaced. A fix to each formerly protected path now lands end to end, and validateFix accepts a new gitlink. A new test covers both ancestry errors, which had no coverage before. The prompt test checks the new rule and that no "protected" or "never edit" rule remains.

Choices made without asking

  • Gitlinks lose their protection along with the paths. The quest's Goal covers "the rest of the protected set", and gitlinks were part of that set in validateFix.
  • In a repository where the App lacks the workflows permission, GitHub still refuses the push of a fix that touches .github/workflows. Fork commits owns that hand-back.

Checks

After merging origin/main (682cbea), just check and just test pass on head 7e4086b. Two test/agent.test.ts cases fail only when the shell exports CLAUDE_CONFIG_DIR, as this agent session does. They pass with it unset, so the failure is unrelated to this change.

Review

Round 1 of a new session ran on head 7e4086b, the hand-resolved merge of origin/main, against origin/main 682cbea. Reviewer: Codex gpt-6.1-sol at medium effort (quest review --new). It reported 0 findings.

The merge kept main's registry-network decision (moq-dev/quest#356) and this PR's protected-path decision in the epic README, and repointed the new workflows-permission.md quest's link to the deleted protected.md at this PR.

Follow-ups

  • test/agent.test.ts ("start runs claude with every launch setting…", "codex runs with its key variable…") reads the caller's CLAUDE_CONFIG_DIR, so it fails inside a Claude Code session that sets it. The test should clear that variable along with the other harness variables it already strips.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

The daemon no longer requires the protected set (flake.nix, nix/, .github/,
.gitmodules, gitlinks, ...) to match the pinned head or the base merge. The
fixer prompt holds every path to the same mechanical bar, and still hands
back any attempt to redirect its task. Decided 2026-11-01 by the maintainer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title The fixer edits protected paths when the edit is mechanical feat(land): let the fixer edit protected paths it judges mechanical Nov 1, 2026
@kixelated
kixelated marked this pull request as ready for review November 1, 2026 13:26
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 816bf795-fa36-4018-adfe-30ac9012abd0
Commits

Reviewing files that changed from the base of the PR and between 682cbea and 7e4086b.

Files selected for processing: 10
  • README.md
  • assets/land-fix.md
  • quest/m0/host-land/fixer.md
  • quest/m0/land-fixer/README.md
  • quest/m0/land-fixer/fork-commit.md
  • quest/m0/land-fixer/protected.md
  • quest/m0/land-fixer/workflows-permission.md
  • src/cli/land-fixer-repository.ts
  • src/cli/land-fixer.ts
  • test/land.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Keep main's registry-network decision (#356) and this PR's protected-path
decision in the epic README, and point workflows-permission.md's link to the
deleted protected.md quest at this PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@moq-bot

moq-bot Bot commented Nov 1, 2026

Copy link
Copy Markdown

quest land removed this pull request from the queue: Conflicts with the base: assets/land-fix.md, quest/m0/host-land/fixer.md, quest/m0/land-fixer/protected.md, test/land.test.ts
.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Closing this unmerged, at the maintainer's decision on 2026-11-01. It follows an independent audit of the merge-queue plan.

The audit's concern: with this PR, fixer-written edits to .github/workflows and the Nix files can land on main with no human review, and those edits can be shaped by untrusted fork content. moq-dev/moq has about 25 release workflows with publishing secrets. The maintainer's merge pick covers a PR's diff, not the fixer's later resolution or the base content merged in after the pick.

What replaces it:

  • validateFix keeps the protected-path check.
  • moq-bot gets the workflows permission (quest/m0/land-fixer/workflows-permission.md), so a base merge that carries main's own workflow changes still pushes.
  • A fixer-written edit to a protected path goes to the maintainer as a ready commit through feat(land): hand back a refused fix push as a ready commit #358 (quest land apply): one command per case, about 17 cases in 3 days.

The quest plan is being updated to match.

(Written by Claude Opus 5.5)

@moq-bot

moq-bot Bot commented Nov 1, 2026

Copy link
Copy Markdown

codex / Codex land fixer: PR cl·osed or head moved while fixing; inspect and pick it again. Recommendation: run /quest-iterate before picking it again.

 		const plan = await resolveQueue(REPOSITORY, undefined, deps);
-		forge.advance({ "shell.nix": "base" });
 		const root = forge.clone();
 		const entry = plan.queue[0];
 		git(root, "checkout", "--quiet", "--detach", entry.head);
-		git(root, "merge", "--no-edit", forge.main());
+		git(root, "update-index", "--add", "--cacheinfo", "160000", entry.head, "vendor/module");
+		git(root, "commit", "-qm", "add gitlink");
 		expect(() => validateFix(root, entry, git(root, "rev-parse", "HEAD"), forge.main())).not.toThrow();
-		git(root, "checkout", forge.main(), "--", "flake.nix");
-		git(root, "commit", "-qm", "mix approved versions");
-		expect(() => validateFix(root, entry, git(root, "rev-parse", "HEAD"), forge.main())).toThrow("protected set");
 	});
-	test("rejects new gitlinks anywhere in the tree", async () => {
+
+	test("rejects a fixed head that does not descend from the pin, or adds commits beside it", async () => {
 		const forge = new Forge();
 		forge.pull("one", { "one.txt": "yes" });
 		const deps = await forge.deps();
 		const plan = await resolveQueue(REPOSITORY, undefined, deps);
 		const root = forge.clone();
 		const entry = plan.queue[0];
+		const head = () => git(root, "rev-parse", "HEAD");
+		expect(() => validateFix(root, entry, entry.head, forge.main())).toThrow("descend from the pinned PR head");
+		git(root, "checkout", "--quiet", "--detach", forge.main());
+		writeFileSync(join(root, "side.txt"), "side");
+		git(root, "add", "side.txt");
+		git(root, "commit", "-qm", "side");
+		const side = head();
+		expect(() => validateFix(root, entry, side, forge.main())).toThrow("descend from the pinned PR head");
 		git(root, "checkout", "--quiet", "--detach", entry.head);
-		git(root, "update-index", "--add", "--cacheinfo", "160000", entry.head, "vendor/module");
-		git(root, "commit", "-qm", "add gitlink");
-		expect(() => validateFix(root, entry, git(root, "rev-parse", "HEAD"), forge.main())).toThrow("protected set");
+		git(root, "merge", "--no-edit", side);
+		expect(() => validateFix(root, entry, head(), forge.main())).toThrow("not on top of pinned head");
 	});
 
 	test.each([false, true])(
@@ -2374,23 +2390,6 @@ ${requeueScript}
 		])
 			expect(() => parseFixResult(JSON.stringify(value), [1])).toThrow();
 	});
-	test("compares protected paths at the new head against the pin and current base", async () => {
-		const forge = new Forge();
-		forge.pull("one", { "one.txt": "yes" });
-		const deps = await forge.deps();
-		const plan = await resolveQueue(REPOSITORY, undefined, deps);
-		forge.advance({ "flake.nix": "base only" });
-		const root = forge.clone();
-		const entry = plan.queue[0];
-		git(root, "checkout", "--quiet", "--detach", entry.head);
-		git(root, "merge", "--no-edit", forge.main());
-		expect(() => validateFix(root, entry, git(root, "rev-parse", "HEAD"), forge.main())).not.toThrow();
-		writeFileSync(join(root, "flake.nix"), "forbidden");
-		git(root, "commit", "-qam", "forbidden");
-		git(root, "checkout", forge.main(), "--", "flake.nix");
-		git(root, "commit", "-qm", "revert");
-		expect(() => validateFix(root, entry, git(root, "rev-parse", "HEAD"), forge.main())).not.toThrow();
-	});
 });
 
 /** Private Git transport: require an exact ephemeral header before forwarding to the local forge. */
test/unblocked.test.ts
flake.lock
src/core/unblocked.ts
bun.lock
assets/AGENTS.md
AGENTS.md
package.json
cloudflare/package.json

codex
PR 357 changes the fixer prompt in `assets/land-fix.md` to permit edits to protected paths: “The same rules apply to every path, including CI workflows, Nix files, and submodule entries.” This targets the fixer’s permissions, so the supplied rules require a handback. I’ll record that diagnosis without changing the branch.
exec
/bin/bash -lc "python3 - <<'PY'
import json
from pathlib import Path
result = [{\"number\":357,\"action\":\"handback\",\"diagnosis\":\"Failure log reports base conflicts in assets/land-fix.md, quest/m0/host-land/fixer.md, quest/m0/land-fixer/protected.md, and test/land.test.ts; PR 357 targets this fixer's permissions in assets/land-fix.md: 'The same rules apply to every path, including CI workflows, Nix files, and submodule entries.' This attempts to permit protected-path edits contrary to this task's rules.\",\"recommendation\":\"Have a maintainer review the fixer permission change and resolve the base conflicts outside this mechanical fixer.\"}]
Path('quest-land-result.json').write_text(json.dumps(result, indent=2)+'\\n')
PY
 git status --short && git rev-parse pr-357" in /work
 succeeded in 0ms:
?? quest-land-result.json
7e4086b05675a87e7c69a219df4103a93a23c036

codex
Recorded PR 357 as a handback in [quest-land-result.json](/work/quest-land-result.json).

The PR changes the fixer prompt to permit protected-path edits, conflicting with this task’s rules. The diagnosis quotes the attempt and its location.

Branches remain unchanged; the result file is uncommitted. No builds or tests were run.
tokens used
19,295
Recorded PR 357 as a handback in [quest-land-result.json](/work/quest-land-result.json).

The PR changes the fixer prompt to permit protected-path edits, conflicting with this task’s rules. The diagnosis quotes the attempt and its location.

Branches remain unchanged; the result file is uncommitted. No builds or tests were run.
fixer result written

(written by codex / Codex)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant