diff --git a/src/repo_registry/web_ui/views.py b/src/repo_registry/web_ui/views.py
index 0776007..1e381e8 100644
--- a/src/repo_registry/web_ui/views.py
+++ b/src/repo_registry/web_ui/views.py
@@ -101,6 +101,15 @@ def page(title: str, body: str) -> HTMLResponse:
font: inherit;
}}
label {{ display: grid; gap: 5px; color: var(--muted); font-size: 12px; font-weight: 600; }}
+ label.checkbox {{
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ color: var(--text);
+ font-size: 13px;
+ font-weight: 500;
+ }}
+ label.checkbox input {{ width: auto; }}
button, .button {{
display: inline-flex;
align-items: center;
@@ -203,6 +212,7 @@ def render_repository_index(
+
Registering repository...
@@ -414,6 +424,7 @@ def create_repository_from_form(
branch: str = Form("main"),
access_username: str = Form(""),
access_password: str = Form(""),
+ explore_after_registration: str | None = Form(None),
service: RegistryService = Depends(get_service),
):
try:
@@ -429,6 +440,16 @@ def create_repository_from_form(
error_message=str(exc),
status_code=400,
)
+ if explore_after_registration:
+ summary = service.analyze_repository(
+ repository.id,
+ access_username=access_username or None,
+ access_password=access_password or None,
+ )
+ return RedirectResponse(
+ f"/ui/repos/{repository.id}/analysis-runs/{summary.analysis_run.id}",
+ status_code=303,
+ )
return RedirectResponse(f"/ui/repos/{repository.id}", status_code=303)
diff --git a/tests/test_web_api.py b/tests/test_web_api.py
index 322220f..a2f276e 100644
--- a/tests/test_web_api.py
+++ b/tests/test_web_api.py
@@ -1075,6 +1075,7 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
assert "Register Repository" in index_response.text
assert "Registering repository..." in index_response.text
assert "Password or access token" in index_response.text
+ assert "Explore after registration" in index_response.text
create_response = client.post(
"/ui/repos",
@@ -1083,6 +1084,7 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
"branch": "main",
"access_username": "",
"access_password": "",
+ "explore_after_registration": "",
},
follow_redirects=False,
)
@@ -1210,6 +1212,47 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
app.dependency_overrides.clear()
+def test_ui_register_and_explore_lands_on_analysis_result(tmp_path):
+ source = tmp_path / "explore-repo"
+ source.mkdir()
+ (source / "README.md").write_text("# Explore Repo\n", encoding="utf-8")
+ (source / "pyproject.toml").write_text(
+ "[project]\nname = \"explore-repo\"\ndescription = \"Explorable repo.\"\n",
+ encoding="utf-8",
+ )
+
+ def override_settings():
+ return Settings(
+ database_path=str(tmp_path / "ui-explore.sqlite3"),
+ checkout_root=str(tmp_path / "ui-explore-checkouts"),
+ )
+
+ app.dependency_overrides[get_settings] = override_settings
+ client = TestClient(app)
+ try:
+ response = client.post(
+ "/ui/repos",
+ data={
+ "url": str(source),
+ "branch": "main",
+ "access_username": "",
+ "access_password": "",
+ "explore_after_registration": "1",
+ },
+ follow_redirects=False,
+ )
+
+ assert response.status_code == 303
+ assert response.headers["location"].endswith("/analysis-runs/1")
+
+ result = client.get(response.headers["location"])
+ assert result.status_code == 200
+ assert "Candidate Graph" in result.text
+ assert "Observed Facts" in result.text
+ finally:
+ app.dependency_overrides.clear()
+
+
def test_ui_registration_failure_returns_feedback(tmp_path):
def override_settings():
return Settings(
diff --git a/workplans/RREG-WP-0003-automatic-repository-exploration.md b/workplans/RREG-WP-0003-automatic-repository-exploration.md
index 80b4429..b35bd06 100644
--- a/workplans/RREG-WP-0003-automatic-repository-exploration.md
+++ b/workplans/RREG-WP-0003-automatic-repository-exploration.md
@@ -27,7 +27,7 @@ configured trusted automation mode.
```task
id: RREG-WP-0003-T01
-status: in_progress
+status: done
priority: high
state_hub_task_id: "ab718ce7-d38f-4080-9385-99be1bf80475"
```
@@ -58,7 +58,28 @@ coordination should be visible as source-linked claims.
Acceptance: a self-analysis run produces non-trivial, source-linked candidate
abilities and capabilities that a curator would recognize as the repo's real
-purpose.
+purpose. A one-to-one correspondence of observed fact to generated feature is a
+quality smell: features should describe user-visible or operational behavior, with
+facts as drilldown evidence, not mirror individual scanner facts.
+
+## P0: Abstraction Strategy And LLM Boundary
+
+```task
+id: RREG-WP-0003-T06
+status: todo
+priority: high
+state_hub_task_id: "51890aff-511d-4635-85c4-fe4db0b7dd01"
+```
+
+Document and test how the registry should climb from deterministic observed facts
+to useful ability/capability/feature abstractions. Explicitly compare what can be
+done deterministically against what likely needs LLM assistance. Preserve the trust
+boundary: deterministic scanners establish facts; abstraction may propose claims;
+review or trusted automation decides approved registry truth.
+
+Acceptance: `docs/` contains an abstraction strategy note with examples from the
+three trial repos, and candidate generation has at least one regression guard
+against feature granularity collapsing into one feature per observed fact.
## P1: Guided Review To Populated Registry