diff options
| author | soryu <soryu@soryu.co> | 2026-04-28 21:26:11 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-28 21:26:11 +0100 |
| commit | 5bde7c2d7e099fd9c8b2615602ab1d096bd9b6be (patch) | |
| tree | d605f7c02472f67a88f1c71c9258c1bf0823b44a /makima/src/bin/makima.rs | |
| parent | d1fdfb140cc440664f77a24886172f9976a05a31 (diff) | |
| download | soryu-5bde7c2d7e099fd9c8b2615602ab1d096bd9b6be.tar.gz soryu-5bde7c2d7e099fd9c8b2615602ab1d096bd9b6be.zip | |
revert PRs #93-#98; enforce strict-linear-DAG + mandatory directive verify (#100)
* revert: roll back PRs #93-#98 to pre-Lexical baseline
Reverts the entire chain of directive document UI work and the homepage redesign,
restoring the working tree to the state at 3679ceb (before c8b169d / PR #93).
PRs reverted:
- #93 c8b169d feat: Document UI for directive orchestration with Lexical editor
- #94 d6f01a6 fix: compilation error and warnings already merged via PR #93
- #95 5aa3faf fix: resolve compilation error and warnings in Rust backend
- #97 d513f93 feat: document UI with contract blocks, expandable logs, and interaction controls
- #96 6366941 feat: Redesign homepage with professional PC-98 styling
- #98 d1fdfb1 feat: revert broken directive PRs, re-implement Lexical document orchestrator
The directive Document UI experiments produced fragile output and merge artifacts;
follow-up commits in this PR change orchestration to favor strictly linear DAGs and
add goal/conflict verification so future runs do not require this kind of cleanup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(directive): strict-linear-DAG planning + mandatory `directive verify`
Tightens directive orchestration so the final PR almost never needs a hand-merge:
1. Planning prompts now strongly bias toward strictly linear DAGs.
Parallel steps are reserved for genuinely independent work (e.g. disjoint
modules); the default for "in doubt" is sequential. Linear chains inherit
each previous step's worktree, so the final merge is typically just a
rebase against the base branch.
2. New CLI command `makima directive verify` does a local in-memory
`git merge-tree` of HEAD against `<remote>/<base>` and exits non-zero
with a list of conflicting files if the PR would not merge cleanly.
Pure-local — no API call, no working-tree mutation.
3. Completion / PR-creation prompts now mandate three pre-push checks:
a. build (`cargo check` and/or `tsc --noEmit`),
b. `makima directive verify --base <base_branch>` must exit 0, and
c. an explicit goal-alignment self-check against the diff.
The orchestrator is told NOT to push, create the PR, or call
`makima directive update` until all three pass. Skipping any of them
is documented as a directive failure.
The combination means that with a linear DAG the final PR-creation task
should almost never see a real conflict — when it does, that is treated as
a planning bug to escalate rather than something to paper over with
`-X theirs`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): TS errors pre-existing on master
- TaskSlideOutPanel: declare missing `selectedFileDiff` / `selectedFilePath`
state hooks that were referenced everywhere but never created, and
re-balance the JSX so the `<>...</>` fragment in the non-diff branch is
closed (the previous indentation/braces would not parse).
- api.ts: add a `getWorktreeDiff` thin wrapper around `getTaskDiff` so
TaskDetail's per-file click handler type-checks (the per-file slice is a
future improvement; today both return the full task diff).
- WorktreeFilesPanel: remove unused `isClickable` local; the gating already
reads `onFileClick` directly inline.
Run after revert: `npx tsc --noEmit` exits 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'makima/src/bin/makima.rs')
| -rw-r--r-- | makima/src/bin/makima.rs | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/makima/src/bin/makima.rs b/makima/src/bin/makima.rs index c4183f3..df3e8e7 100644 --- a/makima/src/bin/makima.rs +++ b/makima/src/bin/makima.rs @@ -864,6 +864,128 @@ async fn run_directive( let result = client.create_order(&req).await?; println!("{}", serde_json::to_string(&result.0)?); } + DirectiveCommand::Verify(args) => { + run_directive_verify(args).await?; + } + } + + Ok(()) +} + +/// Run `makima directive verify` — checks that the current HEAD merges cleanly +/// into `<remote>/<base>`. Prints a JSON result and exits non-zero on conflict. +/// +/// Implementation uses `git merge-tree --write-tree` (Git ≥ 2.38), which performs +/// the merge in-memory and lists conflicting paths without touching the working +/// tree or creating any commits. +async fn run_directive_verify( + args: makima::daemon::cli::directive::VerifyArgs, +) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { + use std::process::Command; + + fn git(args: &[&str]) -> std::io::Result<std::process::Output> { + Command::new("git").args(args).output() + } + + let head_ref = args.head.as_deref().unwrap_or("HEAD").to_string(); + let base_ref = format!("{}/{}", args.remote, args.base); + + if !args.skip_fetch { + eprintln!("Fetching {} {}...", args.remote, args.base); + let fetch = git(&["fetch", &args.remote, &args.base])?; + if !fetch.status.success() { + return Err(format!( + "git fetch {} {} failed: {}", + args.remote, + args.base, + String::from_utf8_lossy(&fetch.stderr) + ) + .into()); + } + } + + let head_rev = { + let out = git(&["rev-parse", &head_ref])?; + if !out.status.success() { + return Err(format!( + "git rev-parse {} failed: {}", + head_ref, + String::from_utf8_lossy(&out.stderr) + ) + .into()); + } + String::from_utf8_lossy(&out.stdout).trim().to_string() + }; + let base_rev = { + let out = git(&["rev-parse", &base_ref])?; + if !out.status.success() { + return Err(format!( + "git rev-parse {} failed (did you fetch?): {}", + base_ref, + String::from_utf8_lossy(&out.stderr) + ) + .into()); + } + String::from_utf8_lossy(&out.stdout).trim().to_string() + }; + + eprintln!("Verifying merge: {} ({}) <- {} ({})", base_ref, &base_rev[..7.min(base_rev.len())], head_ref, &head_rev[..7.min(head_rev.len())]); + + let merge = Command::new("git") + .args(["merge-tree", "--write-tree", "--name-only", "--no-messages", &base_rev, &head_rev]) + .output()?; + + let stdout = String::from_utf8_lossy(&merge.stdout).to_string(); + let stderr = String::from_utf8_lossy(&merge.stderr).to_string(); + let success = merge.status.success(); + + let conflicting_files: Vec<String> = if success { + Vec::new() + } else { + stdout + .lines() + .skip(1) + .filter(|l| !l.is_empty()) + .map(|l| l.to_string()) + .collect() + }; + + let result = serde_json::json!({ + "ok": success, + "base": base_ref, + "head": head_ref, + "baseSha": base_rev, + "headSha": head_rev, + "conflictingFiles": conflicting_files, + "goal": args.goal, + }); + println!("{}", serde_json::to_string(&result)?); + + if !success { + eprintln!("\n[FAIL] Merge would conflict in {} file(s):", conflicting_files.len()); + for f in &conflicting_files { + eprintln!(" - {}", f); + } + if !stderr.is_empty() { + eprintln!("\ngit stderr:\n{}", stderr); + } + eprintln!( + "\nFix the conflicts before pushing. Typical workflow:\n \ + git fetch {remote} {base}\n \ + git merge {remote}/{base}\n \ + # resolve conflicts, commit, then re-run `makima directive verify`", + remote = args.remote, + base = args.base, + ); + std::process::exit(1); + } + + if let Some(goal) = &args.goal { + eprintln!("\n[OK] No merge conflicts."); + eprintln!("Reminder — directive goal:\n {}\n", goal); + eprintln!("Confirm the diff (`git diff {}...HEAD`) actually delivers this goal before creating the PR.", base_ref); + } else { + eprintln!("[OK] No merge conflicts with {}.", base_ref); } Ok(()) |
