Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:08:16 +08:00
commit fc569e5620
38 changed files with 4997 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
# Batch Operations on Multiple Revisions
## Problem
When you need to update descriptions for multiple revisions (e.g., replacing line number references with labels), bash syntax and piping can be tricky.
## Anti-Pattern
```bash
# ❌ This will fail with syntax errors
for rev in unxn mktt stnq; do
jj log -r $rev | sed 's/L123/label/' | jj desc -r $rev --stdin
done
# Issues:
# 1. Missing -n1 --no-graph -T description (gets full log output)
# 2. Unquoted variables ($rev) can break with special chars
# 3. Complex pipes in one-liners are fragile
```
## Pattern 1: Intermediate Files (Recommended)
```bash
# ✅ Robust pattern using temporary files
for rev in unxn mktt stnq rwyq roww; do
# Extract description to file
jj log -r "$rev" -n1 --no-graph -T description > /tmp/desc_${rev}_old.txt
# Transform using sed/awk/etc
sed -f /tmp/replacements.sed /tmp/desc_${rev}_old.txt > /tmp/desc_${rev}_new.txt
# Apply back to revision
jj desc -r "$rev" --stdin < /tmp/desc_${rev}_new.txt
done
echo "✅ All descriptions updated"
```
**Benefits:**
- Each step is visible and debuggable
- Can inspect intermediate files if something goes wrong
- Easy to retry individual revisions
- Works with complex transformations
## Pattern 2: One Command at a Time
```bash
# ✅ Alternative: Sequential approach
jj log -r unxn -n1 --no-graph -T description | \
sed 's/L123/@label/' > /tmp/desc_unxn.txt
jj desc -r unxn --stdin < /tmp/desc_unxn.txt
jj log -r mktt -n1 --no-graph -T description | \
sed 's/L123/@label/' > /tmp/desc_mktt.txt
jj desc -r mktt --stdin < /tmp/desc_mktt.txt
# etc.
```
**Benefits:**
- Even more explicit
- Easy to stop/resume
- Perfect for copy-paste execution
## Pattern 3: Using sed Script File
```bash
# Create reusable sed script
cat > /tmp/replacements.sed << 'EOF'
s/L596-617/@types-de-cartes/g
s/L1242-1253/@carte-eglise/g
s/L659-665/@couts-marche/g
EOF
# Apply to all revisions
for rev in unxn mktt stnq; do
jj log -r "$rev" -n1 --no-graph -T description | \
sed -f /tmp/replacements.sed | \
jj desc -r "$rev" --stdin
done
```
**Benefits:**
- Reusable transformation logic
- Easy to test sed script independently
- Cleaner loop body
## Common Mistakes
### 1. Missing Template Specification
```bash
# ❌ Wrong: gets formatted log output
jj log -r xyz | sed 's/old/new/'
# ✅ Correct: extract just description
jj log -r xyz -n1 --no-graph -T description | sed 's/old/new/'
```
### 2. Unquoted Variables
```bash
# ❌ Breaks with special characters in rev names
for rev in a b c; do
jj log -r $rev # Unquoted
done
# ✅ Always quote
for rev in a b c; do
jj log -r "$rev" # Quoted
done
```
### 3. Fragile One-Liners
```bash
# ❌ Hard to debug, fragile
for rev in a b c; do jj log -r $rev -n1 --no-graph -T description | sed 's/x/y/' | jj desc -r $rev --stdin; done
# ✅ Readable, debuggable
for rev in a b c; do
jj log -r "$rev" -n1 --no-graph -T description | \
sed 's/x/y/' > /tmp/desc_${rev}.txt
jj desc -r "$rev" --stdin < /tmp/desc_${rev}.txt
done
```
## Real-World Example
Replacing all line number references with Typst labels across 10 revisions:
```bash
# 1. Create sed replacement script
cat > /tmp/sed_replacements.txt << 'EOF'
s/5F\.typ L596-617/5F.typ @types-de-cartes/g
s/5F\.typ L1242-1253/5F.typ @carte-eglise-en-pierre/g
s/5F\.typ L659-665/5F.typ @couts-marche/g
# ... etc
EOF
# 2. Process each revision
for rev in unxn mktt stnq rwyq roww wltq syun zkru mszz ovrv; do
jj log -r "$rev" -n1 --no-graph -T description | \
sed -f /tmp/sed_replacements.txt > "/tmp/desc_${rev}_new.txt"
jj desc -r "$rev" --stdin < "/tmp/desc_${rev}_new.txt"
done
# 3. Verify one result
jj log -r mktt -n1 --no-graph -T description | head -5
```
## Verification
Always verify the results after batch operations:
```bash
# Quick check: first line of each description
for rev in unxn mktt stnq; do
echo "=== $rev ==="
jj log -r "$rev" -n1 --no-graph -T description | head -3
done
# Or use jj log with custom template
jj log -r 'unxn | mktt | stnq' -T 'change_id.shortest(4) ++ " " ++ description.first_line() ++ "\n"'
```

View File

@@ -0,0 +1,211 @@
# JJ Command Syntax Reference
## The `-r` Flag Confusion
JJ commands are **inconsistent** with flag naming, which can be confusing:
### Commands Using `-r` (Most Common)
```bash
jj log -r <revset> # ✅ Short form only
jj desc -r <revset> # ✅ Short form only
jj show -r <revset> # ✅ Short form only
jj rebase -r <revset> # ✅ Short form only
jj edit -r <revset> # ✅ Short form only (no --revision)
```
**Rule:** For most commands, use `-r` and **never** `--revisions` or `--revision`.
### Why This Matters
```bash
# ❌ Common mistake: trying long form
jj desc --revisions xyz
# Error: unexpected argument '--revisions' found
jj log --revision xyz
# Error: unexpected argument '--revision' found
# ✅ Always use short form
jj desc -r xyz
jj log -r xyz
```
## Command Patterns
### Reading Revision Info
```bash
# Get description only (for processing)
jj log -r <rev> -n1 --no-graph -T description
# Get detailed info (human-readable)
jj log -r <rev> -n1 --no-graph -T builtin_log_detailed
# Get compact one-liner
jj log -r <rev> -T 'change_id.shortest(4) ++ " " ++ description.first_line()'
```
**Key flags:**
- `-n1`: Limit to 1 revision
- `--no-graph`: No ASCII art graph
- `-T <template>`: Output template
- `-r <revset>`: Which revision(s)
### Modifying Revisions
```bash
# Change description from string
jj desc -r <rev> -m "New description"
# Change description from stdin (for scripts)
echo "New description" | jj desc -r <rev> --stdin
# Change description from file
jj desc -r <rev> --stdin < /path/to/description.txt
# Pipeline pattern
jj log -r <rev> -n1 --no-graph -T description | \
sed 's/old/new/' | \
jj desc -r <rev> --stdin
```
**Key insight:** `--stdin` is essential for scripted modifications.
### Creating Revisions
```bash
# Create and edit immediately (moves @)
jj new <parent> -m "Description"
# Create without editing (@ stays put) - IMPORTANT for parallel branches
jj new --no-edit <parent> -m "Description"
# Create with multiple parents (merge)
jj new --no-edit <parent1> <parent2> -m "Merge point"
```
**Critical distinction:**
- Without `--no-edit`: Your working copy (@) moves to the new revision
- With `--no-edit`: New revision created, but @ stays where it was
## Revset Syntax
### Basic Revsets
```bash
@ # Working copy
<change-id> # Specific revision (e.g., abc, unxn)
<commit-id> # By commit hash
```
### Operators
```bash
<rev>::<rev> # Range (from..to, inclusive)
<rev>.. # All descendants
..<rev> # All ancestors
# Examples
zyxu::@ # All revisions from zyxu to current
roww:: # roww and all its descendants
::@ # All ancestors of @
```
### Functions
```bash
description(glob:"pattern") # Match description
description(exact:"text") # Exact match
mine() # Your commits
```
### Combining
```bash
# Union (OR)
rev1 | rev2
# Intersection (AND)
rev1 & rev2
# Example: Your changes in current branch
mine() & ::@
```
## Shell Quoting
Revsets often need quoting because they contain special characters:
```bash
# ❌ Shell interprets glob
jj log -r description(glob:"[todo]*")
# ✅ Single quotes (safest)
jj log -r 'description(glob:"[todo]*")'
# ✅ Double quotes with escaping
jj log -r "description(glob:\"[todo]*\")"
```
**Rule:** When in doubt, use single quotes around revsets.
## Common Patterns
### Update Multiple Revisions
```bash
# Pattern: Extract → Transform → Apply
for rev in a b c; do
jj log -r "$rev" -n1 --no-graph -T description > /tmp/desc.txt
# ... transform /tmp/desc.txt ...
jj desc -r "$rev" --stdin < /tmp/desc.txt
done
```
### Find and Update
```bash
# Find all [todo] revisions
jj log -r 'description(glob:"[todo]*")'
# Update specific one
jj log -r xyz -n1 --no-graph -T description | \
sed 's/\[todo\]/[wip]/' | \
jj desc -r xyz --stdin
```
### Create Parallel Branches
```bash
# All branch from same parent
parent=xyz
jj new --no-edit "$parent" -m "[todo] Branch A"
jj new --no-edit "$parent" -m "[todo] Branch B"
jj new --no-edit "$parent" -m "[todo] Branch C"
```
## Debugging
```bash
# Did my command work?
jj log -r <rev> -T 'change_id ++ " " ++ description.first_line()'
# View full description
jj log -r <rev> -n1 --no-graph -T description
# Check revision graph
jj log -r '<parent>::' -T builtin_log_compact
```
## Quick Reference Card
| Task | Command |
|------|---------|
| View description | `jj log -r <rev> -n1 --no-graph -T description` |
| Set description | `jj desc -r <rev> -m "text"` |
| Set from stdin | `jj desc -r <rev> --stdin` |
| Create (edit) | `jj new <parent> -m "text"` |
| Create (no edit) | `jj new --no-edit <parent> -m "text"` |
| Range query | `jj log -r '<from>::<to>'` |
| Find pattern | `jj log -r 'description(glob:"pat*")'` |

View File

@@ -0,0 +1,168 @@
# Working with Git Remotes
JJ coexists with Git. The `.git` directory is the source of truth for remotes.
## Basic Workflow
```bash
# 1. Fetch latest from remotes
jj git fetch
# 2. Rebase your work onto updated main
jj rebase -d 'main@origin'
# 3. Make changes...
# 4. Point a bookmark at your work
jj bookmark set my-feature -r @
# 5. Push to remote
jj git push --bookmark my-feature
```
## Bookmarks vs Git Branches
JJ "bookmarks" = Git "branches". They're just named pointers to revisions.
```bash
jj bookmark list # List all bookmarks
jj bookmark create <name> -r <rev> # Create new bookmark
jj bookmark set <name> -r <rev> # Move existing bookmark
jj bookmark delete <name> # Delete bookmark
```
**Key insight:** Unlike Git, you don't need to "be on a branch" to work. Just edit any revision directly.
## Remote Bookmarks
Remote bookmarks have the form `name@remote`:
```bash
main@origin # Remote main on origin
feature@upstream # Remote feature on upstream
```
### Tracking
```bash
jj bookmark track main@origin # Start tracking remote bookmark
jj bookmark untrack main@origin # Stop tracking
```
Tracked bookmarks automatically update on `jj git fetch`.
### Local vs Remote
After fetch, `main` (local) and `main@origin` (remote) may differ:
```bash
# See the difference
jj log -r '::main ~ ::main@origin' # Local commits not in remote
jj log -r '::main@origin ~ ::main' # Remote commits not in local
# Update local to match remote
jj bookmark set main -r 'main@origin'
```
## Pushing
```bash
jj git push --bookmark <name> # Push specific bookmark
jj git push --all # Push all bookmarks
jj git push --change <rev> # Create/push bookmark for revision
```
### Push Errors
**"bookmark moved unexpectedly"**: Someone else pushed. Fetch and rebase:
```bash
jj git fetch
jj rebase -d 'main@origin'
jj git push --bookmark my-feature
```
**"would delete remote bookmark"**: Remote has bookmark you don't. Either:
```bash
jj git push --bookmark <name> --allow-delete # Delete remote
# or
jj bookmark track <name>@origin # Keep tracking it
```
## Fetching
```bash
jj git fetch # Fetch all remotes
jj git fetch --remote origin # Fetch specific remote
```
After fetch, rebase onto updated remote:
```bash
jj rebase -d 'main@origin'
```
## Cloning
```bash
jj git clone <url> [path] # Clone Git repo into JJ
jj git clone --colocate <url> # Colocated: .git + .jj together
```
### Colocated Repos
Colocated repos have both `.git` and `.jj` at the root. Git and JJ see the same history.
```bash
# Convert existing Git repo to colocated JJ
cd existing-git-repo
jj git init --colocate
```
## Import/Export
JJ auto-imports from Git on most operations. Manual control:
```bash
jj git import # Import Git refs → JJ
jj git export # Export JJ bookmarks → Git refs
```
## Common Patterns
### Start feature from latest main
```bash
jj git fetch
jj new 'main@origin' -m "Start feature X"
```
### Rebase feature onto updated main
```bash
jj git fetch
jj rebase -s <feature-root> -d 'main@origin'
```
### Push new feature for review
```bash
jj bookmark create my-feature -r @
jj git push --bookmark my-feature
```
### Update PR after review
```bash
# Make changes...
jj bookmark set my-feature -r @
jj git push --bookmark my-feature
```
### Delete remote branch after merge
```bash
jj bookmark delete my-feature
jj git push --bookmark my-feature --allow-delete
```

View File

@@ -0,0 +1,143 @@
# Revsets Reference
Revsets are JJ's query language for selecting revisions.
```bash
jj help -k revsets # Official documentation
```
## Basic Selectors
```bash
@ # Working copy
@- # Parent of @
@-- # Grandparent
root() # Root commit (empty ancestor)
<change-id> # By change ID (e.g., abc, xyzmno)
<commit-id> # By commit hash
```
## Ancestry Operators
```bash
::@ # All ancestors of @ (inclusive)
@:: # All descendants of @ (inclusive)
@-:: # Descendants of parent (siblings and their children)
<from>::<to> # Range from..to (inclusive both ends)
# Exclusive variants
@- # Immediate parents only
@+ # Immediate children only
```
## Filter Functions
```bash
mine() # Your changes (by author)
heads(all()) # All head revisions (no children)
roots(<revset>) # Roots of given revset
empty() # Empty revisions (no diff)
conflict() # Revisions with unresolved conflicts
immutable() # Immutable revisions (usually main, tags)
mutable() # Mutable revisions
# Text matching
description(substring:"text") # Match in description
description(exact:"text") # Exact description match
author(substring:"name") # Match author name/email
committer(substring:"name") # Match committer
# File-based
file("path/to/file") # Revisions that modified this file
file("glob:src/*.rs") # Glob pattern matching
```
## Set Operations
```bash
A | B # Union: revisions in A OR B
A & B # Intersection: revisions in A AND B
A ~ B # Difference: revisions in A but NOT in B
~A # Complement: all revisions NOT in A
```
## Useful Patterns
### Working with branches
```bash
# Your work on current line
mine() & ::@
# What's on this branch but not in main
::@ ~ ::main
# Heads of your work (tips)
heads(mine())
# All your unmerged work
mine() ~ ::main
```
### Finding specific changes
```bash
# Changes to a specific file
file("src/lib.rs")
# Your changes to src/ directory
file("src/") & mine()
# Empty TODO commits
empty() & description(substring:"[todo]")
# Commits with conflicts
conflict()
```
### Navigation
```bash
# Recent commits (last 10 by default in log)
@ | @- | @-- | @---
# All siblings (same parent as @)
@-:: ~ @::
# Common ancestor of two revisions
heads(::A & ::B)
```
### Remote tracking
```bash
# Remote main
main@origin
# What's in remote but not local
::main@origin ~ ::main
# What's local but not pushed
::main ~ ::main@origin
```
## Quoting in Shell
Revsets with special characters need shell quoting:
```bash
# ❌ Shell interprets parentheses and quotes
jj log -r description(substring:"[todo]")
# ✅ Single quotes protect everything
jj log -r 'description(substring:"[todo]")'
# ✅ Double quotes with escaping
jj log -r "description(substring:\"[todo]\")"
# ✅ Simple revsets don't need quotes
jj log -r mine
jj log -r @-
```
**Rule:** When in doubt, wrap the entire revset in single quotes.

View File

@@ -0,0 +1,177 @@
# Templates Reference
Templates control JJ's output formatting.
```bash
jj help -k templates # Official documentation
```
## Built-in Templates
```bash
# For jj log
builtin_log_compact # Default compact view
builtin_log_detailed # Full details with diff
builtin_log_oneline # Single line per revision
# For jj evolog
builtin_evolog_compact # Default evolution log
# For jj op log
builtin_op_log_compact # Operation log
```
## Context Separation
**Critical:** Different commands have different template contexts.
### Log Templates (revision context)
```bash
# Direct access to revision properties
change_id # Full change ID
change_id.short() # Short form (default 12 chars)
change_id.shortest() # Shortest unique prefix
commit_id # Git commit hash
description # Full description
description.first_line() # First line only
author # Author info
author.name() # Author name
author.email() # Author email
author.timestamp() # Author timestamp
committer # Committer info (same methods)
empty # Boolean: is empty?
conflict # Boolean: has conflicts?
```
### Evolog Templates (commit context)
```bash
# Must go through commit object
commit.change_id()
commit.commit_id()
commit.description()
commit.author()
# etc.
```
### Op Log Templates (operation context)
```bash
self.id() # Operation ID
self.id().short(12) # Short operation ID
self.description() # What the operation did
self.time() # When it happened
self.user() # Who did it
```
## Template Language
### String Concatenation
```bash
# Use ++ to join strings
change_id.shortest(8) ++ " " ++ description.first_line()
```
### Conditionals
```bash
# if(condition, then, else)
if(conflict, "⚠️ ", "")
if(empty, "(empty)", description.first_line())
```
### Methods
```bash
# Strings
description.first_line()
description.lines() # List of lines
"text".contains("x")
"text".starts_with("x")
# IDs
change_id.short() # Default length
change_id.short(6) # Specific length
change_id.shortest() # Minimum unique
change_id.shortest(4) # Minimum 4 chars
# Timestamps
timestamp.ago() # "2 hours ago"
timestamp.format("%Y-%m-%d") # Custom format
```
### Special Output
```bash
# JSON output (for scripting)
jj log -T "json(self)"
# Diff statistics
diff.stat(72) # Stat with max width
# Labels for coloring
label("keyword", "text")
```
## Useful Custom Templates
### Compact one-liner
```bash
jj log -T 'change_id.shortest(8) ++ " " ++ description.first_line() ++ "\n"'
```
### With status indicators
```bash
jj log -T '
change_id.shortest(8)
++ if(conflict, " ⚠️", "")
++ if(empty, " ∅", "")
++ " " ++ description.first_line()
++ "\n"
'
```
### Files changed
```bash
jj log -T 'change_id.shortest(8) ++ "\n" ++ diff.stat(72)'
```
### For scripting (parseable)
```bash
# Tab-separated
jj log -T 'change_id.short() ++ "\t" ++ description.first_line() ++ "\n"' --no-graph
# JSON
jj log -T "json(self)" --no-graph
```
### Operation IDs for checkpoints
```bash
jj op log -T 'self.id().short(12) ++ " " ++ self.description() ++ "\n"' --no-graph -n5
```
## Config File Templates
Define reusable templates in `~/.jjconfig.toml`:
```toml
[templates]
log = 'change_id.shortest(8) ++ " " ++ description.first_line()'
[template-aliases]
'format_short_id(id)' = 'id.shortest(8)'
```
Then use with:
```bash
jj log -T log
# or reference alias in other templates
```

View File

@@ -0,0 +1,294 @@
# TODO Commit Workflow
Empty revisions as TODO markers enable structured development with clear milestones. Descriptions act as specifications for what to implement.
## Core Concept
```bash
# Create empty TODO (stays on current @)
jj-todo-create @ "Feature X" "Detailed specs of what to implement"
# Later, work on it
jj edit <todo-change-id>
# Update status as you progress
jj-flag-update @ wip
```
## Status Flags
Use description prefixes to track status at a glance:
| Flag | Meaning |
|------|---------|
| `[todo]` | Not started, empty revision |
| `[wip]` | Work in progress |
| `[untested]` | Implementation done, tests missing |
| `[broken]` | Tests failing, needs fixing |
| `[review]` | Needs review (tricky code, design choice) |
| (none) | Complete |
### Updating Flags
```bash
# Using script (auto-detects current flag)
jj-flag-update @ wip
jj-flag-update @ untested
jj-flag-update @ done # "done" removes the flag
# Manual (what the script does)
jj log -r @ -n1 --no-graph -T description | sed 's/\[todo\]/[wip]/' | jj desc -r @ --stdin
```
### Finding Flagged Revisions
```bash
jj-find-flagged # All flagged
jj-find-flagged todo # Only [todo]
jj-find-flagged wip # Only [wip]
# Manual
jj log -r 'description(substring:"[todo]")'
```
## Basic Workflow
### 1. Plan: Create TODO Chain
```bash
# Create linear chain of tasks
jj-todo-create @ "Task 1: Setup data model"
jj-todo-create <T1-id> "Task 2: Implement core logic"
jj-todo-create <T2-id> "Task 3: Add API endpoints"
jj-todo-create <T3-id> "Task 4: Write tests"
```
### 2. Work: Edit Each TODO
```bash
# Read the specs
jj-show-desc <task-id>
# Start working on it
jj edit <task-id>
jj-flag-update @ wip
# ... implement ...
# Mark progress
jj-flag-update @ untested
```
### 3. Complete and Move to Next
```bash
# After validation passes, complete current and start next TODO
jj-todo-done
# If there are multiple next TODOs (parallel branches), it will list them:
# Multiple next TODOs available. Choose one:
# abc123 [todo] Widget A
# def456 [todo] Widget B
#
# Then specify which one:
jj-todo-done abc123
```
The script handles the full transition: marks current as done, edits the next revision, sets it to `[wip]`, and prints its description so you can start working.
## Parallel Tasks (DAG)
Create branches that can be worked independently:
```bash
# Linear foundation
jj-todo-create @ "Task 1: Core infrastructure"
jj-todo-create <T1-id> "Task 2: Base components"
# Parallel branches from Task 2
jj-parallel-todos <T2-id> "Widget A" "Widget B" "Widget C"
# Merge point (all three must complete first)
jj new --no-edit <A-id> <B-id> <C-id> -m "[todo] Integration"
```
**Result:**
```
[todo] Integration
/ | \
Widget A Widget B Widget C
\ | /
Task 2: Base
|
Task 1: Core
```
No rebasing needed - parents specified directly!
## Writing Good TODO Descriptions
### Structure
```
[todo] Short title (< 50 chars)
## Context
Why this task exists, what problem it solves.
## Requirements
- Specific requirement 1
- Specific requirement 2
## Implementation notes
Any hints, constraints, or approaches to consider.
## Acceptance criteria
How to know when this is done.
```
### Example
```
[todo] Implement user authentication
## Context
Users need to log in to access their data. Using JWT tokens
for stateless auth.
## Requirements
- POST /auth/login accepts email + password
- Returns JWT token valid for 24h
- POST /auth/refresh extends token
- Invalid credentials return 401
## Implementation notes
- Use bcrypt for password hashing
- Store refresh tokens in Redis
- See auth.md for token format spec
## Acceptance criteria
- All auth endpoints return correct status codes
- Tokens expire correctly
- Rate limiting prevents brute force
```
## Documenting Implementation Deviations
When implementation differs from specs, document it:
```bash
# After implementing, add implementation notes
jj desc -r @ -m "$(jj-show-desc @)
## Implementation
- Used argon2 instead of bcrypt (more secure)
- Added /auth/logout endpoint (not in original spec)
- Rate limit: 5 attempts per minute (was unspecified)
"
```
This creates an audit trail of decisions.
## AI-Assisted TODO Workflow
TODOs work great with AI assistants:
### Setup Phase (Human)
```bash
# Human creates the plan
jj-todo-create @ "Refactor auth module" "
## Requirements
- Extract auth logic from handlers
- Create AuthService class
- Add unit tests
- Update API docs
"
```
### Execution Phase (AI)
```bash
# AI reads the task
jj-show-desc <todo-id>
# AI checkpoints before starting
jj-checkpoint "before-auth-refactor"
# AI edits the revision
jj edit <todo-id>
jj-flag-update @ wip
# ... AI implements ...
# AI marks complete
jj-flag-update @ untested
```
### Review Phase (Human)
```bash
# Human reviews what AI did
jj evolog -r <todo-id> --git
# If bad, restore checkpoint
jj op restore <checkpoint-op-id>
# If good but needs splitting
jj split -r <todo-id>
```
## Tips
### Keep TODOs Small
Each TODO should be completable in one focused session. If it's too big, split into multiple TODOs.
### Use `--no-edit` Religiously
When creating TODOs, always use `jj-todo-create` or `jj new --no-edit`. Otherwise @ moves and you lose your place.
### Validate Between Steps
After completing each TODO, run your project's validation (typecheck, lint, tests) before moving to the next:
```bash
# Verify current work (use your project's commands)
make check # or: cargo build, pnpm tsc, go build, etc.
# Then complete and move to next
jj-todo-done
```
This catches errors early when context is fresh, rather than debugging cascading failures at the end.
### Watch for Hidden Dependencies
When planning TODOs that touch service/module layers (especially with dependency injection), dependencies between components may not be obvious until you validate. A component might require a service you're modifying or replacing.
If a later TODO fails due to missing dependencies from an earlier one, don't forget to edit the description to make clear the extra work you had to do which wasn't in the specs.
The upfront planning helps surface these, but some will only appear at validation time.
### Check DAG Before Starting
```bash
# Visualize the plan
jj log -r '<first-todo>::'
```
### Reorder if Needed
If you realize task order is wrong:
```bash
# Move Task B to be after Task C instead of Task A
jj rebase -r <B-id> -d <C-id>
```
### Abandon Obsolete TODOs
```bash
# If a TODO is no longer needed
jj abandon <todo-id>
```