Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5f4687e
docs(plan): Plan v1 milestones, decisions D11-D18, scope and open que…
Oct 30, 2026
77d344c
docs(design): central server, review_by, 13-tool surface, hooks and CLI
Oct 30, 2026
5ca5df6
docs(contract): note the Plan v1 surface changes planned for M1
Oct 30, 2026
489ef7d
docs(contributing): add the Plan v1 engineering rules
Oct 30, 2026
b18efa9
docs(plan): correct the milestone-to-phase mapping
Oct 30, 2026
ff5a734
docs: correct built-state claims and Plan v1 review warnings
Oct 30, 2026
a5051ff
docs: milestone marker and status lines name M1 as current
Oct 30, 2026
1460f8e
docs(plan): milestones replace phases in §3 to §7
Oct 30, 2026
53d54b1
docs(design): central server, milestones and cut surface
Oct 30, 2026
bfa2ef4
docs(contract): milestone references replace phases
Oct 30, 2026
7127de4
docs(readme): operating record category, server required, rename planned
Oct 30, 2026
84f5a21
docs(wiki): milestones, decisions D11 to D18, one-server architecture
Oct 30, 2026
3e0e97b
github: milestone labels m1 to m8 and later; phase labels are history
Oct 30, 2026
c3fb79f
github: roadmap milestones M1 to M8 and Later, with issue mapping
Oct 30, 2026
b74e5e8
github: bootstrap sets a Milestone board field and reads phases from …
Oct 30, 2026
829c1ae
ci: comment names Later for release builds, not phase 10
Oct 30, 2026
27b8e9a
CHANGELOG: link the Plan v1 entry to PR #65
Oct 30, 2026
884f5ef
github: milestone is the built-in board field, not a custom one
Oct 30, 2026
cdc7e50
issue forms: milestone dropdown, decisions D11 to D18, bug environment
Oct 30, 2026
b51f599
ci: docs job named for what it checks
Oct 30, 2026
8ff21ce
docs(wiki): area:server description matches labels.json
Oct 30, 2026
59ba89d
docs: category line is an operating record for coding agents
Oct 30, 2026
af0ab09
docs(readme): serve, mcp and the CLI marked as planned with milestones
Oct 30, 2026
ba67e7d
docs(design): §5 intro points at §5.3 "The server"
Oct 30, 2026
677e12d
docs(wiki): M4, M7 and M8 have no epic yet
Oct 30, 2026
6803709
github: roadmap decision on placing cairn export project
Oct 30, 2026
53d9d6a
Roadmap: M1 children, stale comments for step 9
Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
github: bootstrap sets a Milestone board field and reads phases from …
…labels

Adds the Milestone single-select field, with options from the roadmap's
milestone keys, and fills it on each roadmap issue when empty. Phase
options come from the phase: labels, not a hard-coded range. A roadmap
issue with no phase, such as every Plan v1 issue, sets no Phase. Fill
only, as before: nothing existing is changed.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
  • Loading branch information
Donovan Maidens and claude committed Oct 30, 2026
commit b74e5e8c9e30dd895c831d8a8297e1673f576d2d
40 changes: 24 additions & 16 deletions scripts/github/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,15 @@
# A body without exactly one start line and one end line, in
# that order, is warned about and left alone. A body that
# would not change is not written.
# ensure_field creates a single-select field (Status, Phase, Area) when the
# ensure_field creates a single-select field (Status, Phase, Milestone, Area) when the
# project lacks it, with its options. An existing field is
# never changed; missing options are reported with manual
# steps.
# sync_project creates the "Cairn Memory Roadmap" project when none exists
# and links it to the repository when it is not linked.
# sync_items adds a roadmap issue to the board when it is not there, and
# sets its Phase or Area only when that value is empty.
# sets its Phase, Milestone or Area only when that value is
# empty. Phase is history for work from before Plan v1.
#
# Nothing is deleted, closed or reopened. Roadmap issues are matched by the
# exact marker line <!-- cairn-roadmap: KEY -->. A missing marker is never
Expand Down Expand Up @@ -626,7 +627,7 @@ sync_children() {
Q_OWNER='query($login: String!) { repositoryOwner(login: $login) { id ... on ProjectV2Owner { projectsV2(first: 100) { nodes { id title number closed } } } } }'
Q_REPO='query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { id projectsV2(first: 100) { nodes { id } } } }'
Q_FIELDS='query($id: ID!) { node(id: $id) { ... on ProjectV2 { fields(first: 100) { nodes { ... on ProjectV2Field { id name } ... on ProjectV2SingleSelectField { id name options { id name color description } } } } } } }'
Q_ITEMS='query($id: ID!, $after: String) { node(id: $id) { ... on ProjectV2 { items(first: 100, after: $after) { pageInfo { hasNextPage endCursor } nodes { id content { ... on Issue { number repository { nameWithOwner } } } phase: fieldValueByName(name: "Phase") { ... on ProjectV2ItemFieldSingleSelectValue { name } } area: fieldValueByName(name: "Area") { ... on ProjectV2ItemFieldSingleSelectValue { name } } } } } } }'
Q_ITEMS='query($id: ID!, $after: String) { node(id: $id) { ... on ProjectV2 { items(first: 100, after: $after) { pageInfo { hasNextPage endCursor } nodes { id content { ... on Issue { number repository { nameWithOwner } } } phase: fieldValueByName(name: "Phase") { ... on ProjectV2ItemFieldSingleSelectValue { name } } milestone: fieldValueByName(name: "Milestone") { ... on ProjectV2ItemFieldSingleSelectValue { name } } area: fieldValueByName(name: "Area") { ... on ProjectV2ItemFieldSingleSelectValue { name } } } } } } }'
M_CREATE_PROJECT='mutation($o: ID!, $t: String!) { createProjectV2(input: {ownerId: $o, title: $t}) { projectV2 { id number } } }'
M_LINK='mutation($p: ID!, $r: ID!) { linkProjectV2ToRepository(input: {projectId: $p, repositoryId: $r}) { repository { id } } }'
M_CREATE_FIELD='mutation($p: ID!, $n: String!, $o: [ProjectV2SingleSelectFieldOptionInput!]!) { createProjectV2Field(input: {projectId: $p, dataType: SINGLE_SELECT, name: $n, singleSelectOptions: $o}) { projectV2Field { ... on ProjectV2SingleSelectField { id } } } }'
Expand Down Expand Up @@ -744,7 +745,10 @@ sync_project() {
# Status is built in, so this only ever reports missing options. Matching is
# case-insensitive, so "In Progress" matches "In progress" without a rename.
ensure_field "Status" '["Todo","In progress","In review","Done"]'
ensure_field "Phase" "$(jq -cn '[range(0;11) | tostring]')"
# Phase options come from the phase: labels, and Milestone options from the
# roadmap's milestone keys (the phase-N entries are history and are left out).
ensure_field "Phase" "$(jq -c '[.[] | select(.name | startswith("phase:")) | .name | ltrimstr("phase:")]' "$LABELS_FILE")"
ensure_field "Milestone" "$(jq -c '[.milestones[].key | select(startswith("phase-") | not)]' "$ROADMAP_FILE")"
ensure_field "Area" "$(jq -c '[.[] | select(.name | startswith("area:")) | .name]' "$LABELS_FILE")"

if [ "$APPLY" = 1 ]; then
Expand All @@ -760,7 +764,7 @@ sync_project() {
# on the board yet and fill their Phase and Area. A value that is already set,
# by this script or by hand, is never overwritten.
sync_items() {
local after resp more item_id cur_phase cur_area want_phase want_area item
local after resp more item_id cur_phase cur_area cur_ms want_phase want_area want_ms item
local key number node line field fid oid

: > "$WORK/items.jsonl"
Expand All @@ -776,8 +780,9 @@ sync_items() {

while IFS= read -r issue; do
key="$(printf '%s' "$issue" | jq -r '.key')"
want_phase="$(printf '%s' "$issue" | jq -r '.phase | tostring')"
want_area="$(printf '%s' "$issue" | jq -r '.area')"
want_phase="$(printf '%s' "$issue" | jq -r '(.phase // "") | tostring')"
want_area="$(printf '%s' "$issue" | jq -r '.area // ""')"
want_ms="$(printf '%s' "$issue" | jq -r '.mkey // ""')"

line="$(awk -F '\t' -v k="$key" '$1 == k { print; exit }' "$WORK/index.tsv")"
if [ -z "$line" ]; then
Expand All @@ -797,6 +802,7 @@ sync_items() {
item_id="$(printf '%s' "$resp" | jq -r '.data.addProjectV2ItemById.item.id')"
cur_phase=""
cur_area=""
cur_ms=""
log " added #$number $key to the board"
else
log " would add #$number $key to the board"
Expand All @@ -806,16 +812,18 @@ sync_items() {
item_id="$(printf '%s' "$item" | jq -r '.id')"
cur_phase="$(printf '%s' "$item" | jq -r '.phase.name // ""')"
cur_area="$(printf '%s' "$item" | jq -r '.area.name // ""')"
cur_ms="$(printf '%s' "$item" | jq -r '.milestone.name // ""')"
fi

for field in Phase Area; do
if [ "$field" = "Phase" ]; then
want="$want_phase"
cur="$cur_phase"
else
want="$want_area"
cur="$cur_area"
fi
for field in Phase Area Milestone; do
case "$field" in
Phase) want="$want_phase"; cur="$cur_phase" ;;
Area) want="$want_area"; cur="$cur_area" ;;
*) want="$want_ms"; cur="$cur_ms" ;;
esac
# Nothing to set when the roadmap gives no value, as for a pre-Plan v1
# issue with no milestone key.
[ -n "$want" ] || continue
[ "$cur" = "$want" ] && continue

# Only an empty value is filled. A value someone set by hand stays.
Expand Down Expand Up @@ -855,7 +863,7 @@ print_views() {
Views cannot be created through the API. Do these once, by hand, in the
"$PROJECT_TITLE" project:
1. New view > Board. Group by Status.
2. New view > Table. Group by Phase.
2. New view > Table. Group by Milestone.
3. New view > Roadmap. Show the milestones. The roadmap layout is driven by
date fields; add Start and Target date fields if you want bars.
EOF
Expand Down