Skip to content

Commit d2f705a

Browse files
nikomatsakisclaude
andcommitted
Fix SUMMARY.md section insertion to place new timeframes before existing dated sections
New goal process sections are now inserted before the first existing dated section (⏳ or ⚙️) rather than after # Summary. This keeps the Introduction link at the top and groups all goal process sections together. Also adds support for year-only timeframes (e.g., 2027) without H1/H2 suffix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 8b0d78a commit d2f705a

File tree

1 file changed

+84
-26
lines changed
  • crates/rust-project-goals-cli/src

1 file changed

+84
-26
lines changed

crates/rust-project-goals-cli/src/cfp.rs

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ pub mod text_processing {
141141
let new_section = format!("\n{}", new_section_content);
142142

143143
// Find a good place to insert the new section
144-
// Look for the last timeframe section or insert at the beginning
145-
// Match both lowercase and uppercase H
146-
let re = Regex::new(r"# ⏳ \d{4}[hH][12] goal process").unwrap();
144+
// Look for the last timeframe section or insert after # Summary
145+
// Match both year-only (2027) and half-year (2026H1) formats
146+
let re = Regex::new(r"# ⏳ \d{4}([hH][12])? goal process").unwrap();
147147

148148
if let Some(last_match) = re.find_iter(&content).last() {
149149
// Find the end of this section (next section or end of file)
@@ -155,8 +155,20 @@ pub mod text_processing {
155155
new_content.push_str(&new_section);
156156
}
157157
} else {
158-
// No existing timeframe sections, insert at the beginning
159-
new_content = new_section + &content;
158+
// No existing ⏳ timeframe sections, find the first dated section (⏳ or ⚙️) and insert before it
159+
let dated_section_re = Regex::new(r"\n# (⏳|⚙️) \d{4}").unwrap();
160+
if let Some(first_dated) = dated_section_re.find(&content) {
161+
new_content.insert_str(first_dated.start(), &new_section);
162+
} else {
163+
// No dated sections at all, insert after "# Summary\n\n"
164+
if let Some(summary_pos) = content.find("# Summary") {
165+
let insert_pos = summary_pos + "# Summary\n\n".len();
166+
new_content.insert_str(insert_pos, &new_section);
167+
} else {
168+
// No # Summary found, prepend
169+
new_content = new_section + &content;
170+
}
171+
}
160172
}
161173

162174
new_content
@@ -170,28 +182,38 @@ pub mod text_processing {
170182
) -> String {
171183
let mut new_content = content.to_string();
172184

173-
// Extract year and half from timeframe
174-
let _year = &timeframe[0..4];
175-
let half = &timeframe[4..].to_lowercase();
185+
// Create the new section to add with capitalized H
186+
let capitalized_timeframe = normalize_timeframe(timeframe);
176187

177-
// Determine the months based on the half
178-
let (start_month, end_month) = if half == "h1" {
179-
("January", "June")
188+
// Check if this is a year-only timeframe (no H1/H2)
189+
let new_section = if timeframe.len() == 4 {
190+
// Year-only format - no date range
191+
format!(
192+
"\n## Next goal period ({})\n\n\
193+
The next goal period will be {}. \
194+
We are currently in the process of assembling goals. \
195+
[Click here](./{}/goals.md) to see the current list. \
196+
If you'd like to propose a goal, [instructions can be found here](./how_to/propose_a_goal.md).\n",
197+
capitalized_timeframe, capitalized_timeframe, lowercase_timeframe
198+
)
180199
} else {
181-
("July", "December")
200+
// Half-year format - include date range
201+
let half = &timeframe[4..].to_lowercase();
202+
let (start_month, end_month) = if half == "h1" {
203+
("January", "June")
204+
} else {
205+
("July", "December")
206+
};
207+
format!(
208+
"\n## Next goal period ({})\n\n\
209+
The next goal period will be {}, running from the start of {} to the end of {}. \
210+
We are currently in the process of assembling goals. \
211+
[Click here](./{}/goals.md) to see the current list. \
212+
If you'd like to propose a goal, [instructions can be found here](./how_to/propose_a_goal.md).\n",
213+
capitalized_timeframe, capitalized_timeframe, start_month, end_month, lowercase_timeframe
214+
)
182215
};
183216

184-
// Create the new section to add with capitalized H
185-
let capitalized_timeframe = normalize_timeframe(timeframe);
186-
let new_section = format!(
187-
"\n## Next goal period ({})\n\n\
188-
The next goal period will be {}, running from the start of {} to the end of {}. \
189-
We are currently in the process of assembling goals. \
190-
[Click here](./{}/goals.md) to see the current list. \
191-
If you'd like to propose a goal, [instructions can be found here](./how_to/propose_a_goal.md).\n",
192-
capitalized_timeframe, capitalized_timeframe, start_month, end_month, lowercase_timeframe
193-
);
194-
195217
// First check for an existing entry for this specific timeframe
196218
let this_period_pattern = Regex::new(&format!(
197219
r"## Next goal period(?:\s*\({}\))?\s*\n",
@@ -537,15 +559,39 @@ mod tests {
537559
}
538560

539561
#[test]
540-
fn test_process_summary_content_no_existing_section() {
541-
// Test adding a new section when no timeframe sections exist
542-
let content = "# Summary\n\n[Introduction](./README.md)\n";
562+
fn test_process_summary_content_new_section() {
563+
// Test adding a new section before an existing dated section
564+
let content = "# Summary\n\n[👋 Introduction](./README.md)\n\n# ⚙️ 2025H2 goal process\n\n- [Overview](./2025h2/README.md)\n";
543565
let result = process_summary_content(content, "2026h1", "2026h1");
544566

545567
assert!(result.contains("# ⏳ 2026H1 goal process"));
546568
assert!(result.contains("- [Overview](./2026h1/README.md)"));
547569
assert!(result.contains("- [Proposed goals](./2026h1/goals.md)"));
548570
assert!(result.contains("- [Goals not accepted](./2026h1/not_accepted.md)"));
571+
// Verify the section is inserted after Introduction, before the existing dated section
572+
let intro_pos = result.find("[👋 Introduction]").unwrap();
573+
let goal_process_pos = result.find("# ⏳ 2026H1 goal process").unwrap();
574+
let existing_section_pos = result.find("# ⚙️ 2025H2").unwrap();
575+
assert!(intro_pos < goal_process_pos, "Goal process section should come after Introduction");
576+
assert!(goal_process_pos < existing_section_pos, "New section should come before existing dated section");
577+
}
578+
579+
#[test]
580+
fn test_process_summary_content_year_only_timeframe() {
581+
// Test adding a new section with year-only timeframe (no H1/H2)
582+
let content = "# Summary\n\n[👋 Introduction](./README.md)\n\n# ⚙️ 2025H2 goal process\n\n- [Overview](./2025h2/README.md)\n";
583+
let result = process_summary_content(content, "2027", "2027");
584+
585+
assert!(result.contains("# ⏳ 2027 goal process"));
586+
assert!(result.contains("- [Overview](./2027/README.md)"));
587+
assert!(result.contains("- [Proposed goals](./2027/goals.md)"));
588+
assert!(result.contains("- [Goals not accepted](./2027/not_accepted.md)"));
589+
// Verify the section is inserted after Introduction, before the existing dated section
590+
let intro_pos = result.find("[👋 Introduction]").unwrap();
591+
let goal_process_pos = result.find("# ⏳ 2027 goal process").unwrap();
592+
let existing_section_pos = result.find("# ⚙️ 2025H2").unwrap();
593+
assert!(intro_pos < goal_process_pos, "Goal process section should come after Introduction");
594+
assert!(goal_process_pos < existing_section_pos, "New section should come before existing dated section");
549595
}
550596

551597
#[test]
@@ -645,4 +691,16 @@ mod tests {
645691
assert!(result.contains("## Next goal period (2026H1)"));
646692
assert!(result.contains("running from the start of January to the end of June"));
647693
}
694+
695+
#[test]
696+
fn test_process_readme_content_year_only() {
697+
// Test that year-only timeframes don't include date ranges
698+
let content = "# Project goals\n\n## Current goal period (2026)\n\nThe 2026 goal period.";
699+
let result = process_readme_content(content, "2027", "2027");
700+
701+
assert!(result.contains("## Next goal period (2027)"));
702+
assert!(result.contains("The next goal period will be 2027."));
703+
assert!(!result.contains("running from"));
704+
assert!(result.contains("[Click here](./2027/goals.md)"));
705+
}
648706
}

0 commit comments

Comments
 (0)