summaryrefslogtreecommitdiff
path: root/makima/src/bin/makima.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-04-28 21:26:11 +0100
committerGitHub <noreply@github.com>2026-04-28 21:26:11 +0100
commit5bde7c2d7e099fd9c8b2615602ab1d096bd9b6be (patch)
treed605f7c02472f67a88f1c71c9258c1bf0823b44a /makima/src/bin/makima.rs
parentd1fdfb140cc440664f77a24886172f9976a05a31 (diff)
downloadsoryu-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.rs122
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(())