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
7 changes: 7 additions & 0 deletions docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ run; releases require that result for their exact source commit.
| Go module dependencies, CI machinery or unclassified inputs | Full checks | Only if release tooling also changes |
| Nightly or manual CI | Full checks, platform matrix, packaging and Go vulnerability audit | Version tags / manual releases still use the release workflow |

PNG, JPEG and WebP screenshots under `docs/acceptance/`, `docs/evidence/` and
`docs/images/` count as documentation. Adding before/after screenshots to a
frontend PR keeps its frontend and workbench-journey checks without starting
unrelated backend/platform checks. Runtime images under `assets/branding/`,
`web/` or Go package test data retain their existing checks. Other files in
the evidence directories still use the normal classification rules.

The selection rules live in `scripts/ci/classify_changes.py`. Main uses the push
before/after commits, so merging a frontend change does not rerun the entire
backend matrix. Nightly CI runs at 19:23 UTC (03:23 Asia/Hong_Kong). The Actions
Expand Down
5 changes: 5 additions & 0 deletions scripts/ci/classify_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
CHECKS = ("full", "backend", "store", "web", "journeys", "lsp", "rust", "native",
"ui", "race", "audit", "packaging")
_FULL_PATHS = {"go.mod", "go.sum", ".github/workflows/ci.yml"}
_DOCUMENTATION_IMAGE_PREFIXES = ("docs/acceptance/", "docs/evidence/", "docs/images/")
_DOCUMENTATION_IMAGE_SUFFIXES = (".png", ".jpg", ".jpeg", ".webp")
_PACKAGING_PREFIXES = (
"packaging/", "assets/branding/", "scripts/build-desktop",
"scripts/release-desktop", "scripts/package-", "scripts/verify-",
Expand Down Expand Up @@ -84,8 +86,11 @@ def classify_paths(paths: Sequence[bytes], affected: Sequence[str] = ()) -> dict
"scripts/tests/prepare-windows-fixed-go")):
checks["native"] = True
elif (path.endswith(".md") or path.startswith("docs/convergence/")
or (path.startswith(_DOCUMENTATION_IMAGE_PREFIXES)
and path.lower().endswith(_DOCUMENTATION_IMAGE_SUFFIXES))
or path in {".gitignore", ".gitattributes", "LICENSE", "LICENSE.md"}
or path.startswith((".github/ISSUE_TEMPLATE/", ".github/PULL_REQUEST_TEMPLATE"))):
# Acceptance screenshots are documentation, not runtime assets.
# Generated registry/inventory and documentation contracts are
# checked in the always-on Go checks job.
pass
Expand Down
28 changes: 28 additions & 0 deletions scripts/ci/test_classify_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,34 @@ def test_go_dependencies_select_store_without_unrelated_platforms(self):
)
self.assertEqual(selected, {"backend", "store", "journeys"})

def test_documentation_screenshots_do_not_expand_frontend_checks(self):
for directory in ("docs/acceptance", "docs/evidence", "docs/images"):
for suffix in (".png", ".jpg", ".jpeg", ".webp", ".PNG"):
screenshot = f"{directory}/browser-viewer/before{suffix}".encode()
with self.subTest(screenshot=screenshot):
self.assertEqual(self.enabled([screenshot]), set())
for paths in ([screenshot, b"web/src/v2/components/agent-browser.tsx"],
[b"web/src/v2/components/agent-browser.tsx", screenshot]):
self.assertEqual(self.enabled(paths), {"web", "journeys"})

def test_screenshot_documentation_keeps_other_input_checks(self):
screenshot = b"docs/acceptance/browser-viewer/after.png"
for paths, affected in (
([b"assets/branding/traverse-board-mark.png"], ()),
([b"web/public/traverse-board-favicon-32.png"], ()),
([b"internal/workspace/testdata/reference.png"], ("project/internal/workspace",)),
([b"internal/domain/run.go"], ("project/internal/domain", "project/internal/store")),
([b"docs/openapi.json"], ()),
([b"go.mod"], ()),
([b"scripts/ci/classify_changes.py"], ())):
with self.subTest(paths=paths):
self.assertEqual(self.enabled([screenshot, *paths], affected), self.enabled(paths, affected))
for path in (b"docs/acceptance/verify.py", b"docs/evidence/trace.json",
b"docs/images/browser.svg", b"docs/images/browser.png.sh",
b"docs/acceptance-runtime/before.png", b"unknown-assets/before.png"):
with self.subTest(path=path):
self.assertEqual(self.enabled([path, screenshot]), set(CHECKS))

def test_product_journeys_follow_backend_consumers_without_native_matrix(self):
checks = self.enabled([b"internal/llm/model_catalog.go"],
["project/internal/llm", "project/internal/application"])
Expand Down
Loading