Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .ai-context/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ the canonical current command list.
fields against the Base Project schema.
- `basectl gh project configure --project <title>` - create or repair the
standard Project metadata schema; pass `--replace-project` with `--repo`
to archive and recreate a repo Project whose views are nonstandard.
to archive and recreate a repo Project whose views are nonstandard; Projects
that already have standard Base views are left intact.
- `basectl gh project issue set-fields <number>` - add an issue to the
Project if needed and update its metadata fields.
- `basectl clean` - remove old Base runtime logs, temp files, and cache entries.
Expand Down
1 change: 1 addition & 0 deletions .ai-context/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ creates it when missing from older repositories.
If a repo Project has GitHub's default `View 1` instead of the standard Base
views, use `basectl repo configure --replace-project` with `--repo`; Base
archives the old Project and recreates it from `base-project-template`.
Already-standard Projects are left intact and continue through metadata repair.

## Branch And Worktree Flow

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,9 @@ from `base-project-template`. Base renames and closes the old Project as a
legacy archive, creates a fresh Project with the original title, links it to the
repo, backfills repo issues, and copies missing issue item fields from the
legacy Project before applying repo defaults. The repaired Project gets a new
Project number and URL.
Project number and URL. If the existing Project already has the standard Base
views, `--replace-project` leaves it intact and continues normal metadata
repair.

Run a discovered project's declared test command with:

Expand Down
3 changes: 2 additions & 1 deletion cli/bash/commands/basectl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ such command directories exist. Optional utility CLIs such as `caff` and
remaining blanks in the repo Project. Use `--replace-project` when an
existing repo Project has nonstandard views; Base archives the old Project,
recreates it from `base-project-template`, backfills repository issues, and
preserves missing item field values where possible.
preserves missing item field values where possible. Already-standard Projects
are left intact.
`basectl repo agent-guidance [path]` seeds optional repo-local agent guidance
files and `basectl repo check [path] --agent-guidance` verifies that optional
layer for repos that opt in. Use `--pr` when generated guidance should land
Expand Down
1 change: 1 addition & 0 deletions cli/bash/commands/basectl/subcommands/gh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Notes:
- Project operations delegate to Base's Python Project engine.
- Use project issue set-fields to move issue cards through Backlog, In Progress, In Review, and Done.
- Use --replace-project to replace a nonstandard repo Project from base-project-template.
Already-standard Projects are left intact.
EOF
}

Expand Down
3 changes: 2 additions & 1 deletion cli/python/base_github_projects/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,8 @@ def replacement_plan_for_args(
raise ProjectError(f"Project '{args.project_title}' was not found for owner '{owner}'; cannot replace it.")
view_errors = standard_template_view_errors(fetch_project_views(project.project_id))
if not view_errors:
raise ProjectError(f"Project '{args.project_title}' already has standard Base views; refusing to replace it.")
print(f"INFO: Project '{args.project_title}' already has standard Base views; skipping replacement.")
return None
return ProjectReplacement(
legacy_project=project,
legacy_title=legacy_project_title(args.project_title or ""),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,13 @@ def test_configure_command_replace_project_requires_repo() -> None:
assert str(excinfo.value) == "--replace-project requires --repo."


def test_configure_command_replace_project_refuses_standard_views(monkeypatch: pytest.MonkeyPatch) -> None:
def test_configure_command_replace_project_skips_replacement_for_standard_views(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
linked: list[tuple[str, str]] = []
backfilled: list[tuple[str, str]] = []

monkeypatch.setattr(
engine,
"find_owner_and_project",
Expand All @@ -88,20 +94,47 @@ def test_configure_command_replace_project_refuses_standard_views(monkeypatch: p
"fetch_project_views",
lambda project_id: project_model.STANDARD_TEMPLATE_VIEWS,
)
monkeypatch.setattr(engine, "fetch_project_fields", lambda project_id: complete_project_fields())
monkeypatch.setattr(engine, "create_single_select_field", lambda project_id, spec: None)
monkeypatch.setattr(engine, "update_single_select_field", lambda field, spec: None)
monkeypatch.setattr(
engine,
"link_project_to_repository",
lambda project_id, repo: linked.append((project_id, repo)),
)
monkeypatch.setattr(
engine,
"backfill_repository_issues",
lambda project_id, repo: backfilled.append((project_id, repo)) or 3,
)
monkeypatch.setattr(
engine,
"update_project",
lambda project_id, title=None, closed=None: pytest.fail("standard project must not be renamed or closed"),
)
monkeypatch.setattr(
engine,
"copy_project",
lambda template_project_id, owner_id, title: pytest.fail("standard project must not be replaced"),
)

with pytest.raises(engine.ProjectError) as excinfo:
engine.configure_command(
engine.ProjectArguments(
area="project",
command="configure",
project_title="base-demo",
owner="codeforester",
repo="codeforester/base-demo",
replace_project=True,
)
status = engine.configure_command(
engine.ProjectArguments(
area="project",
command="configure",
project_title="base-demo",
owner="codeforester",
repo="codeforester/base-demo",
replace_project=True,
)
)

assert str(excinfo.value) == "Project 'base-demo' already has standard Base views; refusing to replace it."
assert status == 0
assert linked == [("project-id", "codeforester/base-demo")]
assert backfilled == [("project-id", "codeforester/base-demo")]
output = capsys.readouterr().out
assert "INFO: Project 'base-demo' already has standard Base views; skipping replacement." in output
assert "Configured GitHub Project base-demo" in output


def test_configure_command_replace_project_dry_run_reports_cutover_plan(
Expand Down
3 changes: 2 additions & 1 deletion docs/github-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,8 @@ If a repo Project exists with nonstandard views such as GitHub's default
archives the old Project by renaming and closing it, creates a fresh Project
from `base-project-template`, backfills repository issues, and copies missing
issue field values from the legacy Project. The new Project has a different
Project number and URL.
Project number and URL. If the Project already has the standard Base views,
`--replace-project` leaves it intact and continues normal metadata repair.

Repo Projects show workflow and prioritization. Milestones show release
grouping. Cross-repo portfolio Projects should be curated roll-ups rather than
Expand Down
3 changes: 2 additions & 1 deletion docs/repo-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ old Project, copies `base-project-template` into a new Project with the original
title, links the new Project to the repository, backfills repository issues,
copies missing issue field values from the legacy Project, and then applies repo
defaults. Replacement changes the Project number and URL, so keep the closed
legacy Project as the audit trail.
legacy Project as the audit trail. Already-standard Projects are left intact
and continue through normal metadata repair.
`basectl gh project` is the lower-level direct surface for Project inspection,
schema repair, and issue field updates.

Expand Down
Loading