summaryrefslogtreecommitdiff
path: root/makima/src
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
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')
-rw-r--r--makima/src/bin/makima.rs122
-rw-r--r--makima/src/daemon/cli/directive.rs26
-rw-r--r--makima/src/daemon/cli/mod.rs7
-rw-r--r--makima/src/db/models.rs32
-rw-r--r--makima/src/db/repository.rs85
-rw-r--r--makima/src/orchestration/directive.rs190
-rw-r--r--makima/src/server/mod.rs11
7 files changed, 317 insertions, 156 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(())
diff --git a/makima/src/daemon/cli/directive.rs b/makima/src/daemon/cli/directive.rs
index cc7b224..0f04720 100644
--- a/makima/src/daemon/cli/directive.rs
+++ b/makima/src/daemon/cli/directive.rs
@@ -177,6 +177,32 @@ pub struct CreateOrderArgs {
pub labels: Option<String>,
}
+/// Arguments for verify command — checks the current worktree can merge into the
+/// directive's base branch with no conflicts. Runs entirely locally; no API call.
+#[derive(Args, Debug)]
+pub struct VerifyArgs {
+ /// Base branch to attempt merging into (e.g., "master", "main").
+ #[arg(long, default_value = "master")]
+ pub base: String,
+
+ /// Remote name to fetch from (default: "origin").
+ #[arg(long, default_value = "origin")]
+ pub remote: String,
+
+ /// Head ref to verify (default: current HEAD).
+ #[arg(long)]
+ pub head: Option<String>,
+
+ /// Skip the `git fetch` step (use already-fetched remote ref).
+ #[arg(long, default_value = "false")]
+ pub skip_fetch: bool,
+
+ /// Optional directive goal text. When provided the goal is echoed back as a
+ /// reminder so the calling orchestrator can self-check goal alignment.
+ #[arg(long)]
+ pub goal: Option<String>,
+}
+
/// Arguments for update command.
#[derive(Args, Debug)]
pub struct UpdateArgs {
diff --git a/makima/src/daemon/cli/mod.rs b/makima/src/daemon/cli/mod.rs
index af6f885..7affc55 100644
--- a/makima/src/daemon/cli/mod.rs
+++ b/makima/src/daemon/cli/mod.rs
@@ -255,6 +255,13 @@ pub enum DirectiveCommand {
/// Create an order for future work (spike or chore only)
CreateOrder(directive::CreateOrderArgs),
+
+ /// Verify the current worktree merges cleanly into the directive's base branch.
+ ///
+ /// Mandatory pre-flight before creating or pushing a directive PR — fails
+ /// with a non-zero exit code (and a list of conflicting files) if the merge
+ /// would conflict with the base branch.
+ Verify(directive::VerifyArgs),
}
impl Cli {
diff --git a/makima/src/db/models.rs b/makima/src/db/models.rs
index c11150f..97657dc 100644
--- a/makima/src/db/models.rs
+++ b/makima/src/db/models.rs
@@ -3050,36 +3050,4 @@ pub struct DirectiveOrderGroupListResponse {
pub total: i64,
}
-// =============================================================================
-// User Settings Types
-// =============================================================================
-
-/// A user setting (feature flag / preference) stored in the database.
-#[derive(Debug, Clone, FromRow, Serialize, Deserialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct UserSetting {
- pub id: Uuid,
- pub owner_id: Uuid,
- pub key: String,
- #[sqlx(json)]
- pub value: serde_json::Value,
- pub created_at: DateTime<Utc>,
- pub updated_at: DateTime<Utc>,
-}
-
-/// Request to upsert (create or update) a user setting.
-#[derive(Debug, Deserialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct UpsertUserSettingRequest {
- pub key: String,
- pub value: serde_json::Value,
-}
-
-/// Response wrapping a list of user settings.
-#[derive(Debug, Serialize, ToSchema)]
-#[serde(rename_all = "camelCase")]
-pub struct UserSettingsResponse {
- pub settings: Vec<UserSetting>,
-}
-
diff --git a/makima/src/db/repository.rs b/makima/src/db/repository.rs
index 5a912e4..57e8a78 100644
--- a/makima/src/db/repository.rs
+++ b/makima/src/db/repository.rs
@@ -6698,88 +6698,3 @@ pub async fn get_available_orders_for_dog_pickup(
.await
}
-// =============================================================================
-// User Settings
-// =============================================================================
-
-/// Get all settings for a user.
-pub async fn get_user_settings(
- pool: &PgPool,
- owner_id: Uuid,
-) -> Result<Vec<UserSetting>, sqlx::Error> {
- sqlx::query_as::<_, UserSetting>(
- r#"
- SELECT id, owner_id, key, value, created_at, updated_at
- FROM user_settings
- WHERE owner_id = $1
- ORDER BY key ASC
- "#,
- )
- .bind(owner_id)
- .fetch_all(pool)
- .await
-}
-
-/// Get a specific setting by key for a user.
-pub async fn get_user_setting(
- pool: &PgPool,
- owner_id: Uuid,
- key: &str,
-) -> Result<Option<UserSetting>, sqlx::Error> {
- sqlx::query_as::<_, UserSetting>(
- r#"
- SELECT id, owner_id, key, value, created_at, updated_at
- FROM user_settings
- WHERE owner_id = $1 AND key = $2
- "#,
- )
- .bind(owner_id)
- .bind(key)
- .fetch_optional(pool)
- .await
-}
-
-/// Upsert (create or update) a user setting.
-pub async fn upsert_user_setting(
- pool: &PgPool,
- owner_id: Uuid,
- key: &str,
- value: &serde_json::Value,
-) -> Result<UserSetting, sqlx::Error> {
- sqlx::query_as::<_, UserSetting>(
- r#"
- INSERT INTO user_settings (owner_id, key, value)
- VALUES ($1, $2, $3)
- ON CONFLICT (owner_id, key) DO UPDATE SET
- value = EXCLUDED.value,
- updated_at = NOW()
- RETURNING id, owner_id, key, value, created_at, updated_at
- "#,
- )
- .bind(owner_id)
- .bind(key)
- .bind(value)
- .fetch_one(pool)
- .await
-}
-
-/// Delete a user setting by key. Returns true if a row was deleted.
-pub async fn delete_user_setting(
- pool: &PgPool,
- owner_id: Uuid,
- key: &str,
-) -> Result<bool, sqlx::Error> {
- let result = sqlx::query(
- r#"
- DELETE FROM user_settings
- WHERE owner_id = $1 AND key = $2
- "#,
- )
- .bind(owner_id)
- .bind(key)
- .execute(pool)
- .await?;
-
- Ok(result.rows_affected() > 0)
-}
-
diff --git a/makima/src/orchestration/directive.rs b/makima/src/orchestration/directive.rs
index 1e025c8..8b3ae7e 100644
--- a/makima/src/orchestration/directive.rs
+++ b/makima/src/orchestration/directive.rs
@@ -1604,11 +1604,21 @@ Because of this, you MUST chain steps using dependsOn whenever one step's work b
If step B modifies files created/changed by step A, step B MUST list step A in its dependsOn — otherwise
step B will start from a blank worktree and won't see step A's changes at all.
-Guidelines:
-- For sequential work, create a linear chain: step1 → step2 → step3 (each depends on the previous).
-- Only omit dependsOn for truly independent steps that can start from a fresh checkout.
-- Parallel steps that share no files can omit mutual dependencies, but if they both build on a prior step
- they should BOTH list that prior step in dependsOn.
+Guidelines (DAG SHAPE — READ CAREFULLY):
+- DEFAULT TO STRICTLY LINEAR CHAINS: step1 → step2 → step3 → … each step depends on the previous one.
+ This is the right shape for almost every directive. A linear chain inherits each previous step's
+ worktree, so later steps can see and build on earlier work, and the final merge is just a fast-forward
+ with at most a rebase against the base branch.
+- ONLY use parallel steps (same orderIndex, no mutual dependsOn) when the work is GENUINELY independent:
+ the steps modify completely disjoint files or modules AND neither needs the other's output.
+ Pure-frontend vs pure-backend work in separate folders is the prototypical example. If you can name
+ even one shared file or one shared concept, the steps must be sequential.
+- WHEN IN DOUBT, MAKE IT SEQUENTIAL. The cost of unnecessary serialization is one extra step run.
+ The cost of unnecessary parallelism is merge conflicts, lost work, and a final PR that has to be
+ hand-reconciled — exactly the failure mode this rule exists to prevent.
+- A directive with N parallel branches is suspicious; a directive with N+1 sequential steps is the norm.
+ If you find yourself drawing a diamond (A → {{B, C}} → D), strongly reconsider whether B and C are
+ actually independent or whether one should follow the other.
IMPORTANT: Each step's taskPlan must be self-contained. The executing instance won't have your planning context.
@@ -1709,6 +1719,11 @@ Goal: {goal}
Steps completed:
{step_summary}
+NOTE: This directive was planned with the strict-linear-DAG rule, so the step branches
+should generally merge cleanly. If a merge produces meaningful conflicts, that is a
+signal the plan was wrong, not routine work — prefer asking for help over papering
+over conflicts with `-X theirs`.
+
1. Clear the old PR URL:
```
makima directive update --pr-url ""
@@ -1720,7 +1735,6 @@ git fetch origin
NEW_BRANCH="{directive_branch}-v$(date +%s)"
git checkout -b "$NEW_BRANCH" origin/{base_branch}
{merge_commands}
-git push -u origin "$NEW_BRANCH"
```
For each step branch merge above, if a merge fails with conflicts:
@@ -1728,7 +1742,47 @@ For each step branch merge above, if a merge fails with conflicts:
2. If that also fails, manually resolve the conflicts, `git add .`, and `git commit --no-edit`
3. Continue with remaining merges
-3. Generate a descriptive PR title and create a new PR:
+## Step 2.5: MANDATORY pre-push verification
+
+Before pushing or creating the PR, you MUST run all three of these checks. Skipping any
+of them is a directive failure.
+
+a) Build check — make sure the combined branch compiles:
+ - Rust backend (if any backend files changed): `cd makima && cargo check`
+ - Frontend (if any frontend files changed): `cd makima/frontend && npm install && npx tsc --noEmit`
+ Fix any errors before continuing. Do NOT push broken code.
+
+b) Merge-conflict check — MANDATORY:
+```
+makima directive verify --base {base_branch}
+```
+ This must exit 0. If it exits non-zero, the branch will not merge cleanly into
+ `{base_branch}` and the PR is not ready. Resolve by:
+```
+ git fetch origin {base_branch}
+ git merge origin/{base_branch}
+ # resolve any conflicts, then `git add . && git commit --no-edit`
+ makima directive verify --base {base_branch}
+```
+ Re-run until verify exits 0. Do NOT push, create a PR, or call `makima directive update`
+ until verify passes.
+
+c) Goal-alignment self-check:
+ Run `git diff origin/{base_branch}...HEAD --stat` and review the file list. Confirm
+ the diff actually delivers the directive goal:
+
+ {goal}
+
+ If the diff is missing work the goal requires, finish the work or call
+ `makima directive ask "<question>" --phaseguard` for guidance. Do NOT push a PR that
+ does not deliver the goal.
+
+3. Push the branch:
+```
+git push -u origin "$NEW_BRANCH"
+```
+
+4. Generate a descriptive PR title and create a new PR:
Based on the steps completed above, generate a descriptive PR title that summarizes the actual changes (not just the directive name "{title}"). The title should:
- Be concise (under 72 characters)
@@ -1741,13 +1795,13 @@ gh pr create --title "<YOUR_GENERATED_TITLE>" --body "{pr_body}" --head "$NEW_BR
```
Replace <YOUR_GENERATED_TITLE> with the concise descriptive title you generated.
-4. Store the new PR URL:
+5. Store the new PR URL:
```
makima directive update --pr-url "<URL_FROM_GH_PR_CREATE>"
```
Replace the URL with the actual PR URL from the `gh pr create` output. This step is CRITICAL.
-5. Update the directive pr_branch to the new branch name:
+6. Update the directive pr_branch to the new branch name:
```
makima directive update --pr-branch "$NEW_BRANCH"
```
@@ -1759,14 +1813,13 @@ The PR is still open. Merge new step branches into the existing PR branch.
Steps completed:
{step_summary}
-Run these commands:
+Run these commands to update the branch (note: do NOT push yet — verification comes first):
```
git fetch origin
git checkout {directive_branch}
git pull origin {directive_branch}
git merge origin/{base_branch} --no-edit
{merge_commands}
-git push origin {directive_branch}
```
Already-merged branches will be a no-op. If a merge fails with conflicts:
@@ -1774,6 +1827,33 @@ Already-merged branches will be a no-op. If a merge fails with conflicts:
2. If that also fails, manually resolve the conflicts, `git add .`, and `git commit --no-edit`
3. Continue with remaining merges
+## MANDATORY pre-push verification (also applies to PR updates)
+
+Before `git push`, run all three checks. Skipping any of them is a directive failure.
+
+a) Build check — Rust: `cd makima && cargo check`. Frontend (if changed):
+ `cd makima/frontend && npm install && npx tsc --noEmit`. Do NOT push broken code.
+
+b) Merge-conflict check — MANDATORY:
+```
+makima directive verify --base {base_branch}
+```
+ Must exit 0. If not, merge `origin/{base_branch}` in, resolve, commit, re-verify.
+ Do NOT push until verify passes.
+
+c) Goal-alignment self-check — review `git diff origin/{base_branch}...HEAD --stat`
+ and confirm it still delivers the directive goal:
+
+ {goal}
+
+ If the goal has drifted (e.g., new step branches changed scope), update the PR
+ description after pushing or call `makima directive ask` for guidance.
+
+Then push:
+```
+git push origin {directive_branch}
+```
+
If you encounter issues you cannot resolve (e.g., persistent merge conflicts, PR update failures), you can ask for help:
makima directive ask "Your question" --phaseguard
"#,
@@ -1800,17 +1880,65 @@ Goal: {goal}
Steps completed:
{step_summary}
-Run these commands to create a combined branch and PR:
+NOTE: This directive was planned with the strict-linear-DAG rule, so the step branches
+should generally merge cleanly. If a merge produces meaningful conflicts, that is a
+signal the plan was wrong, not routine work — prefer asking for help over papering
+over conflicts with `-X theirs`.
+
+## Step 1: Build the combined branch (do NOT push yet)
```
git fetch origin
git checkout -b {directive_branch} origin/{base_branch}
{merge_commands}
-git push -u origin {directive_branch}
```
-Then generate a descriptive PR title and create the PR:
+For each step branch merge, if a merge fails with conflicts:
+1. First try: `git merge --abort` then retry with `git merge <the-failing-branch> -X theirs --no-edit`
+2. If that also fails, manually resolve the conflicts, `git add .`, and `git commit --no-edit`
+3. Continue with remaining merges
-Based on the steps completed above, generate a descriptive PR title that summarizes the actual changes (not just the directive name "{title}"). The title should:
+## Step 2: MANDATORY pre-push verification
+
+Before pushing or creating the PR, you MUST run all three of these checks. Skipping any
+of them is a directive failure.
+
+a) Build check — make sure the combined branch compiles:
+ - Rust backend (if any backend files changed): `cd makima && cargo check`
+ - Frontend (if any frontend files changed): `cd makima/frontend && npm install && npx tsc --noEmit`
+ Fix any errors before continuing. Do NOT push broken code.
+
+b) Merge-conflict check — MANDATORY:
+```
+makima directive verify --base {base_branch}
+```
+ This must exit 0. If it exits non-zero, the branch will not merge cleanly into
+ `{base_branch}` and the PR is not ready. Resolve by:
+```
+ git fetch origin {base_branch}
+ git merge origin/{base_branch}
+ # resolve any conflicts, then `git add . && git commit --no-edit`
+ makima directive verify --base {base_branch}
+```
+ Re-run until verify exits 0. Do NOT push, create a PR, or call `makima directive update`
+ until verify passes.
+
+c) Goal-alignment self-check:
+ Run `git diff origin/{base_branch}...HEAD --stat` and review the file list. Confirm
+ the diff actually delivers the directive goal:
+
+ {goal}
+
+ If the diff is missing work the goal requires, finish the work or call
+ `makima directive ask "<question>" --phaseguard` for guidance. Do NOT push a PR that
+ does not deliver the goal.
+
+## Step 3: Push and create the PR
+```
+git push -u origin {directive_branch}
+```
+
+Generate a descriptive PR title that summarizes the actual changes (not just the directive
+name "{title}"). The title should:
- Be concise (under 72 characters)
- Describe WHAT was done, not just the project name
- Use conventional commit style if appropriate (feat:, fix:, refactor:, etc.)
@@ -1821,21 +1949,17 @@ gh pr create --title "<YOUR_GENERATED_TITLE>" --body "{pr_body}" --head {directi
```
Replace <YOUR_GENERATED_TITLE> with the concise descriptive title you generated.
-IMPORTANT: After creating the PR, you MUST store the PR URL so the directive system can track it.
+## Step 4: Store the PR URL (CRITICAL)
+
+After creating the PR, you MUST store the PR URL so the directive system can track it.
-1. Run `gh pr create` as shown above and capture its output
-2. The output will contain the PR URL (e.g., https://github.com/owner/repo/pull/123)
-3. Then run this command to store the URL:
+1. The `gh pr create` output contains the PR URL (e.g., https://github.com/owner/repo/pull/123)
+2. Run:
```
makima directive update --pr-url "https://github.com/..."
```
Replace the URL with the actual PR URL from the `gh pr create` output. This step is CRITICAL — the PR will not be tracked by the directive system without it.
-For each step branch merge, if a merge fails with conflicts:
-1. First try: `git merge --abort` then retry with `git merge <the-failing-branch> -X theirs --no-edit`
-2. If that also fails, manually resolve the conflicts, `git add .`, and `git commit --no-edit`
-3. Continue with remaining merges
-
If you encounter issues you cannot resolve (e.g., persistent merge conflicts, PR creation failures), you can ask for help:
makima directive ask "Your question" --phaseguard
"#,
@@ -2195,11 +2319,19 @@ Because of this, you MUST chain steps using dependsOn whenever one step's work b
If step B modifies files created/changed by step A, step B MUST list step A in its dependsOn — otherwise
step B will start from a blank worktree and won't see step A's changes at all.
-Guidelines:
-- For sequential work, create a linear chain: step1 → step2 → step3 (each depends on the previous).
-- Only omit dependsOn for truly independent steps that can start from a fresh checkout.
-- Parallel steps that share no files can omit mutual dependencies, but if they both build on a prior step
- they should BOTH list that prior step in dependsOn.
+Guidelines (DAG SHAPE — READ CAREFULLY):
+- DEFAULT TO STRICTLY LINEAR CHAINS: step1 → step2 → step3 → … each step depends on the previous one.
+ This is the right shape for almost every directive. A linear chain inherits each previous step's
+ worktree, so later steps can see and build on earlier work, and the final merge is just a fast-forward
+ with at most a rebase against the base branch.
+- ONLY use parallel steps (same orderIndex, no mutual dependsOn) when the work is GENUINELY independent:
+ the steps modify completely disjoint files or modules AND neither needs the other's output.
+ Pure-frontend vs pure-backend work in separate folders is the prototypical example. If you can name
+ even one shared file or one shared concept, the steps must be sequential.
+- WHEN IN DOUBT, MAKE IT SEQUENTIAL. The cost of unnecessary serialization is one extra step run.
+ The cost of unnecessary parallelism is merge conflicts, lost work, and a final PR that has to be
+ hand-reconciled — exactly the failure mode this rule exists to prevent.
+- A directive with N parallel branches is suspicious; a directive with N+1 sequential steps is the norm.
IMPORTANT: Each step's taskPlan must be self-contained. The executing instance won't have your planning context.
diff --git a/makima/src/server/mod.rs b/makima/src/server/mod.rs
index 99dc1f3..b382f04 100644
--- a/makima/src/server/mod.rs
+++ b/makima/src/server/mod.rs
@@ -281,7 +281,7 @@ pub fn make_router(state: SharedState) -> Router {
.route("/timeline", get(history::get_timeline))
// Contract type templates (built-in only)
.route("/contract-types", get(templates::list_contract_types))
- // Settings endpoints (static routes first to avoid conflict with /settings/{key})
+ // Settings endpoints
.route(
"/settings/repository-history",
get(repository_history::list_repository_history),
@@ -294,15 +294,6 @@ pub fn make_router(state: SharedState) -> Router {
"/settings/repository-history/{id}",
axum::routing::delete(repository_history::delete_repository_history),
)
- // User settings (feature flags / preferences)
- .route(
- "/settings",
- get(settings::list_settings).put(settings::upsert_setting),
- )
- .route(
- "/settings/{key}",
- get(settings::get_setting).delete(settings::delete_setting),
- )
.with_state(state);
let swagger = SwaggerUi::new("/swagger-ui")