QA plan: Corpus license and attribution (#48)
Story: Corpus license and attribution (#48)
Capability: Licensing and attribution (#47)
Related: Legal and compliance (#46); security per-feature requirements (#46/#47/#48)
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| e2e / component | The Apache-2.0 attribution for neural-bridge/rag-dataset-12000 is rendered in the SPA (footer or About section); Playwright asserts the text is present in the DOM |
| e2e / component | The attribution text is present on the QA CloudFront URL (not just in the bundle, but as rendered content) |
| CI | SBOM generation step produces a CycloneDX SBOM (sbom.json or sbom.xml) on every PR |
| CI | SBOM license scan step flags any copyleft license (GPL, AGPL, LGPL) on a production runtime dependency as a blocking CI failure |
| CI | Permissive licenses (MIT, Apache-2.0, BSD) pass the SBOM scan without blocking |
| Documentation | The architecture overview or cost.md contains the attribution sentence (not just the SPA) |
Entry criteria
Section titled “Entry criteria”- Story #17 (React chat UI) merged — SPA must exist to add the attribution element
- Story #29 (CI pipeline) includes a SBOM generation step (
cyclonedx-bomorpip-licenses+cyclonedx-py)
Exit criteria
Section titled “Exit criteria”- Playwright: assertion that
data-testid="corpus-attribution"element is visible and contains “Apache-2.0” and “neural-bridge/rag-dataset-12000” (or equivalent text) - CI SBOM:
sbom.jsonis generated and committed as a CI artifact on every PR - CI license scan: a test PR with a fake GPL dependency in
pyproject.toml(dev group, not runtime) confirmed to either block or pass correctly based on the runtime vs. dev scope distinction - Documentation: grep for “Apache-2.0” in the architecture overview page passes
Environments
Section titled “Environments”Playwright: QA CloudFront URL. CI: GitHub Actions for SBOM generation and scan. Documentation check: local or CI grep.
Tooling
Section titled “Tooling”Playwright, cyclonedx-py or pip-licenses, a license-check script (or liccheck), grep.
How tests are written
Section titled “How tests are written”// Given the QA SPA is loaded in Playwrightawait page.goto(process.env.QA_SPA_URL);// When the page settlesawait page.waitForLoadState('networkidle');// Then the corpus attribution element is visible and contains the required textconst attribution = page.getByTestId('corpus-attribution');await expect(attribution).toBeVisible();await expect(attribution).toContainText('Apache-2.0');await expect(attribution).toContainText('neural-bridge/rag-dataset-12000');SBOM license check (CI):
# Given the SBOM for the current PR has been generated# When the license scanner parses it for copyleft licensespython3 scripts/check_licenses.py sbom.json --fail-on "GPL,AGPL,LGPL" --runtime-only# Then no runtime dependency carries a copyleft license- The corpus attribution requirement applies to the rendered SPA, not just a comment in the code. If the attribution element is conditionally rendered (e.g., only when the SPA is connected to the API), the Playwright test must trigger the condition first or render unconditionally.
pip-licensesmay misclassify transitive dependencies; use the--packagesflag restricted to direct runtime dependencies frompyproject.tomlproduction groups to reduce noise.
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”Security per-feature requirements (#46/#47/#48): Apache-2.0 attribution in SPA and architecture overview; CycloneDX SBOM in CI; copyleft licenses block merge. Exit criteria map to each requirement. No additional security threat model rows apply; the risk is supply-chain license exposure, not a STRIDE threat.
© 2026 Benjamin Arunski