Skip to content

test(e2e): tests/e2e/ skeleton + scenario 1 (#334 step 1) - #354

Merged
robotrocketscience merged 6 commits into
mainfrom
feat/issue-334-e2e-skeleton
May 2, 2026
Merged

test(e2e): tests/e2e/ skeleton + scenario 1 (#334 step 1)#354
robotrocketscience merged 6 commits into
mainfrom
feat/issue-334-e2e-skeleton

Conversation

@robotrocketscience

@robotrocketscience robotrocketscience commented May 2, 2026

Copy link
Copy Markdown
Owner

Step 1 of #334's rollout: land the tests/e2e/ skeleton + scenario #1
behind a CI job that runs only on PR with the e2e label, so we can
verify wall time and the install-method matrix before flipping the
if: to also run on push to main (step 4).

What lands

  • tests/e2e/conftest.py — three fixtures:
    • ephemeral_db (per-test SQLite under tmp_path)
    • installed_aelf (argv prefix; resolves AELFRICE_E2E_BIN >
      shutil.which('aelf') > uv run aelf so the suite runs in CI
      and locally without an explicit install step)
    • aelf_run (callable that pins AELFRICE_DB and captures output)
    • tiny_project (synthetic three-commit git repo with a
      distinctive token; generated inline — no on-disk fixture data,
      no directory-of-origin concerns)
  • tests/e2e/test_install_onboard_search.py — three test cases:
    • --version smoke check (binary on PATH, importable)
    • onboard --emit-candidates against tiny_project finds the
      distinctive token in the JSON sentences[] payload
    • search against an empty store exits 0 (catches the missing-
      migration / FTS5-unbuilt regression class)
  • tests/e2e/install-{uv-tool,pipx,venv-pip}.sh — three single-line
    shell scripts, one per matrix leg, per spec ("keep them dumb").
  • .github/workflows/e2e.yml — new workflow:
    • PR-label opt-in only this round (contains(... labels.*.name, 'e2e'))
    • Matrix install-method = [uv-tool, pipx, venv-pip]
    • fail-fast: false so legs don't mask each other
    • 8 min timeout per spec hard cap
    • harden-runner audit egress, pinned actions
  • .github/workflows/ci.ymlpytest now uses
    --ignore=tests/e2e so the unit job stays in-process and doesn't
    trip over the e2e suite (which expects the matrix install step to
    set AELFRICE_E2E_BIN).

Local verification

$ uv run pytest tests/e2e/ -q
3 passed in 1.51s

$ uv run pytest tests/ --ignore=tests/e2e -q
2051 passed, 20 skipped in 43.84s

What's deliberately not in this PR

Subsequent rollout steps (each a separate atomic PR referencing the
umbrella):

Verifying the PR-label trigger

After merging this PR, the next PR that wants e2e coverage adds the
e2e label and the workflow runs. Until then it is dormant — zero
CI cost on the existing PR queue.

Tracks #334.

Summary by Sourcery

Introduce an end-to-end testing skeleton and initial scenario, and wire it into a label-gated CI workflow while isolating it from the existing unit test job.

New Features:

  • Add an e2e test suite with fixtures and a tiny synthetic git project to exercise the installed aelf binary via subprocess.
  • Add the first e2e scenario covering version output, onboarding candidate emission, and search behavior against an empty store.

Enhancements:

  • Exclude the e2e tests from the standard pytest run in the main CI workflow to keep unit tests focused and fast.

CI:

  • Add a new GitHub Actions workflow that runs the e2e test matrix for different install methods when PRs are labeled with 'e2e'.
  • Add simple install scripts per install method (uv-tool, pipx, venv-pip) used by the e2e CI matrix.

Summary by CodeRabbit

  • Tests

    • Added comprehensive end-to-end test suite validating version reporting, project onboarding, and search behavior across multiple install methods.
    • Introduced fixtures and helpers to run the CLI against ephemeral projects and databases.
  • Chores

    • CI updated to run end-to-end tests in a dedicated workflow and exclude them from the primary test run.

@sourcery-ai

sourcery-ai Bot commented May 2, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces an end-to-end test skeleton and first scenario that exercise the installed aelf binary via subprocess, wires them into a new GitHub Actions E2E workflow gated by an e2e label and an install-method matrix, and updates the existing CI workflow to exclude the new E2E tests from the unit test job.

Sequence diagram for labeled PR triggering the new E2E workflow and tests

sequenceDiagram
  actor Developer
  participant GitHub
  participant E2EWorkflow as E2E_workflow_e2e_yml
  participant E2EJob as Job_e2e
  participant MatrixLeg as Matrix_leg_install_method
  participant InstallScript as Install_script
  participant Pytest as Pytest_tests_e2e
  participant AelfBinary as aelf_binary

  Developer->>GitHub: Open PR targeting main
  Developer->>GitHub: Add label e2e to PR

  GitHub-->>E2EWorkflow: pull_request labeled event
  E2EWorkflow->>E2EWorkflow: Check branch is main
  E2EWorkflow->>E2EWorkflow: Check contains labels name e2e
  E2EWorkflow-->>E2EJob: Start job e2e with matrix install_method=[uv-tool,pipx,venv-pip]

  loop For each install_method
    E2EJob-->>MatrixLeg: Create matrix leg with install_method
    MatrixLeg->>InstallScript: Run tests/e2e/install-install_method.sh
    InstallScript-->>MatrixLeg: aelf installed via chosen method
    MatrixLeg->>MatrixLeg: Determine aelf binary path
    MatrixLeg-->>Pytest: Run uv run pytest tests/e2e/ with AELFRICE_E2E_BIN

    Pytest->>AelfBinary: Invoke aelf --version
    AelfBinary-->>Pytest: Version output

    Pytest->>AelfBinary: Invoke aelf onboard --emit-candidates on tiny_project
    AelfBinary-->>Pytest: JSON sentences payload

    Pytest->>AelfBinary: Invoke aelf search on empty store
    AelfBinary-->>Pytest: Exit status 0
  end

  Pytest-->>E2EJob: Report test results
  E2EJob-->>GitHub: Job status per matrix leg
  GitHub-->>Developer: Show E2E checks success or failure
Loading

File-Level Changes

Change Details Files
Add shared E2E pytest fixtures to run the installed aelf binary against an ephemeral SQLite DB and a synthetic git project.
  • Introduce an ephemeral_db fixture that returns a per-test SQLite path under tmp_path for use via the AELFRICE_DB environment variable.
  • Implement an installed_aelf fixture that resolves the CLI invocation prefix from AELFRICE_E2E_BIN, falling back to a PATH lookup, then to uv run aelf for local runs without explicit installation.
  • Provide an aelf_run fixture that wraps subprocess.run, pinning AELFRICE_DB, propagating the ambient environment, and capturing output for assertions.
  • Create a tiny_project fixture that builds a small synthetic git repo with distinctive tokens and three commits, fully generated at runtime without on-disk fixture dependencies.
tests/e2e/conftest.py
Add E2E scenario #1 tests covering version reporting, onboarding candidate emission, and search behavior against an empty store.
  • Add a module-level pytest timeout marker to bound each test’s runtime.
  • Add a --version smoke test to ensure the installed binary executes and prints non-empty version output without coupling to a specific version string.
  • Add an onboard --emit-candidates test that runs against tiny_project, parses the JSON payload, asserts structural fields, and validates that the distinctive token appears in the sentences content.
  • Add a search test that queries an empty store for a distinctive token, asserting exit code 0 to guard against missing migrations or uninitialized FTS5 indices.
tests/e2e/test_install_onboard_search.py
Introduce a dedicated GitHub Actions E2E workflow triggered only for PRs labeled e2e, running against a matrix of installation methods and the new E2E test suite.
  • Define an E2E workflow that triggers on pull_request events to main (labeled, synchronize, reopened) and gates the job on the presence of the e2e label via an if: condition.
  • Configure workflow concurrency to cancel in-progress E2E runs for the same PR or ref.
  • Add a matrix job over install-method = [uv-tool, pipx, venv-pip] with fail-fast: false and an 8-minute timeout cap.
  • Set up hardened runners, checkout, Python 3.13, and uv with caching of uv.lock.
  • Install pipx conditionally for the pipx matrix leg, then call the corresponding tests/e2e/install-*.sh script to install the working tree.
  • Add a step to resolve the installed aelf binary path per install method, validating it is executable and exporting its path via step outputs.
  • Run uv sync --frozen --group dev and invoke pytest tests/e2e/ -q --maxfail=3 with AELFRICE_E2E_BIN set to the located binary.
.github/workflows/e2e.yml
tests/e2e/install-uv-tool.sh
tests/e2e/install-pipx.sh
tests/e2e/install-venv-pip.sh
Keep unit-test CI isolated from the new E2E suite by excluding tests/e2e from the default pytest invocation.
  • Update the main CI workflow’s pytest step to pass --ignore=tests/e2e so the unit test job remains in-process and does not depend on the E2E installation matrix or environment variables.
.github/workflows/ci.yml

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@robotrocketscience robotrocketscience added author-Setr PR coordination mutex attn:review Needs review (PR open, awaiting reviewer) labels May 2, 2026
@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@robotrocketscience has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 22 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 72d11a2e-f6c2-4f12-a073-319bc2bbfa6f

📥 Commits

Reviewing files that changed from the base of the PR and between 731f02d and f5cfa33.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • .github/workflows/e2e.yml
  • tests/e2e/__init__.py
  • tests/e2e/conftest.py
  • tests/e2e/install-pipx.sh
  • tests/e2e/install-uv-tool.sh
  • tests/e2e/install-venv-pip.sh
  • tests/e2e/test_install_onboard_search.py
📝 Walkthrough

Walkthrough

Adds an end-to-end test workflow and supporting tests/scripts: CI skips e2e, a new label-gated E2E GitHub Actions workflow installs the package three ways and runs pytest against tests/e2e/, and new e2e fixtures, install scripts, and three CLI end-to-end tests are added.

Changes

End-to-End Testing Infrastructure

Layer / File(s) Summary
CI Exclude E2E
.github/workflows/ci.yml
Updates pytest invocation to ignore tests/e2e by adding --ignore=tests/e2e to uv run pytest tests/ invocation.
E2E Workflow
.github/workflows/e2e.yml
Adds new E2E workflow (label-gated on PRs to main) with concurrency, read-only contents permissions, 8-minute timeout, and a matrix over install-method (uv-tool,pipx,venv-pip).
Runner Setup & Tooling
.github/workflows/e2e.yml
Workflow hardens runner, checks out code, sets up Python 3.13, installs uv with cache keyed to uv.lock, conditionally installs pipx for that matrix leg.
Install & Locate Binary
.github/workflows/e2e.yml, tests/e2e/install-*.sh
Runs ./tests/e2e/install-${{ matrix.install-method }}.sh per matrix; locate installed aelf per method, fail if not executable, and export path in AELFRICE_E2E_BIN.
Install Scripts
tests/e2e/install-uv-tool.sh, tests/e2e/install-pipx.sh, tests/e2e/install-venv-pip.sh
Three Bash scripts (strict mode) to install the project via uv tool install --force ., pipx install --force ., or a fresh venv + pip install ..
E2E Test Fixtures
tests/e2e/conftest.py
Adds ephemeral_db (per-test SQLite path), installed_aelf (argv prefix resolved from AELFRICE_E2E_BIN or fallbacks), aelf_run (callable wrapping subprocess.run with env AELFRICE_DB), and tiny_project (deterministic tiny git repo with distinctive tokens).
E2E Test Scenarios
tests/e2e/test_install_onboard_search.py
Adds three tests (module-level 120s timeout): --version prints non-empty version, onboard --emit-candidates returns JSON with session_id and sentences containing token quokka, and search quokka exits 0 against an empty store.
Test Execution
.github/workflows/e2e.yml
Workflow runs uv sync --frozen --group dev then pytest tests/e2e/ -q --maxfail=3.

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub Actions
    participant Runner as Ubuntu Runner
    participant Installer as Install Script (pipx/uv/venv)
    participant Aelf as installed `aelf` binary
    participant Pytest as pytest process

    GH->>Runner: start E2E job (label gated)
    Runner->>Runner: setup Python 3.13, install uv, cache uv.lock
    Runner->>Installer: run ./tests/e2e/install-<method>.sh
    Installer->>Runner: installs package and places `aelf` on PATH or known path
    Runner->>Runner: locate `aelf` binary, export AELFRICE_E2E_BIN
    Runner->>Runner: uv sync --frozen --group dev
    Runner->>Pytest: run pytest tests/e2e/
    Pytest->>Aelf: invoke `aelf` via subprocess with AELFRICE_DB set
    Aelf->>Pytest: stdout/stderr JSON/results
    Pytest->>Runner: test pass/fail
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: introduction of e2e tests skeleton and first scenario, referencing the tracking issue and step number.
Description check ✅ Passed The description provides a comprehensive summary covering what lands, linked issue (#334), local verification results, and intentional omissions. However, Type of change and Verification checkboxes required by the template are not checked.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-334-e2e-skeleton

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 33 minutes and 22 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread .github/workflows/e2e.yml Fixed
Comment thread .github/workflows/e2e.yml Fixed
Comment thread .github/workflows/e2e.yml Fixed
Comment thread .github/workflows/e2e.yml Fixed
Comment thread .github/workflows/e2e.yml Fixed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The aelf_run fixture’s return type is annotated via a string with type: ignore[name-defined]; consider importing and using collections.abc.Callable directly (e.g. Iterator[Callable[..., subprocess.CompletedProcess[str]]]) to avoid the ignore and keep typing clearer.
  • In test_aelf_version_prints the comment states the version format is aelfrice X.Y.Z but the assertion only checks for non-empty output; either tighten the assertion to check the expected prefix or relax/update the comment to match the behavior being tested.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `aelf_run` fixture’s return type is annotated via a string with `type: ignore[name-defined]`; consider importing and using `collections.abc.Callable` directly (e.g. `Iterator[Callable[..., subprocess.CompletedProcess[str]]]`) to avoid the ignore and keep typing clearer.
- In `test_aelf_version_prints` the comment states the version format is `aelfrice X.Y.Z` but the assertion only checks for non-empty output; either tighten the assertion to check the expected prefix or relax/update the comment to match the behavior being tested.

## Individual Comments

### Comment 1
<location path=".github/workflows/e2e.yml" line_range="45-54" />
<code_context>
+      - name: Locate installed aelf
+        id: locate
+        run: |
+          case "${{ matrix.install-method }}" in
+            uv-tool)   bin="$HOME/.local/bin/aelf" ;;
+            pipx)      bin="$HOME/.local/bin/aelf" ;;
+            venv-pip)  bin="$PWD/.e2e-venv/bin/aelf" ;;
+          esac
+          test -x "$bin" || { echo "::error::aelf binary not found at $bin"; exit 1; }
+          echo "bin=$bin" >> "$GITHUB_OUTPUT"
</code_context>
<issue_to_address>
**suggestion:** Handle unexpected `install-method` values explicitly in the `case` statement.

If `matrix.install-method` ever differs from the three handled values, `bin` stays empty and the job will fail later with a less clear `aelf binary not found at` message. Adding a default branch (e.g. `*) echo "::error::Unknown install-method: ..."; exit 1 ;;`) would make this misconfiguration fail fast with an explicit error.

```suggestion
      - name: Locate installed aelf
        id: locate
        run: |
          case "${{ matrix.install-method }}" in
            uv-tool)   bin="$HOME/.local/bin/aelf" ;;
            pipx)      bin="$HOME/.local/bin/aelf" ;;
            venv-pip)  bin="$PWD/.e2e-venv/bin/aelf" ;;
            *)
              echo "::error::Unknown install-method: '${{ matrix.install-method }}'"
              exit 1
              ;;
          esac
          test -x "$bin" || { echo "::error::aelf binary not found at $bin"; exit 1; }
          echo "bin=$bin" >> "$GITHUB_OUTPUT"
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/workflows/e2e.yml
Comment on lines +45 to +54
- name: Locate installed aelf
id: locate
run: |
case "${{ matrix.install-method }}" in
uv-tool) bin="$HOME/.local/bin/aelf" ;;
pipx) bin="$HOME/.local/bin/aelf" ;;
venv-pip) bin="$PWD/.e2e-venv/bin/aelf" ;;
esac
test -x "$bin" || { echo "::error::aelf binary not found at $bin"; exit 1; }
echo "bin=$bin" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Handle unexpected install-method values explicitly in the case statement.

If matrix.install-method ever differs from the three handled values, bin stays empty and the job will fail later with a less clear aelf binary not found at message. Adding a default branch (e.g. *) echo "::error::Unknown install-method: ..."; exit 1 ;;) would make this misconfiguration fail fast with an explicit error.

Suggested change
- name: Locate installed aelf
id: locate
run: |
case "${{ matrix.install-method }}" in
uv-tool) bin="$HOME/.local/bin/aelf" ;;
pipx) bin="$HOME/.local/bin/aelf" ;;
venv-pip) bin="$PWD/.e2e-venv/bin/aelf" ;;
esac
test -x "$bin" || { echo "::error::aelf binary not found at $bin"; exit 1; }
echo "bin=$bin" >> "$GITHUB_OUTPUT"
- name: Locate installed aelf
id: locate
run: |
case "${{ matrix.install-method }}" in
uv-tool) bin="$HOME/.local/bin/aelf" ;;
pipx) bin="$HOME/.local/bin/aelf" ;;
venv-pip) bin="$PWD/.e2e-venv/bin/aelf" ;;
*)
echo "::error::Unknown install-method: '${{ matrix.install-method }}'"
exit 1
;;
esac
test -x "$bin" || { echo "::error::aelf binary not found at $bin"; exit 1; }
echo "bin=$bin" >> "$GITHUB_OUTPUT"

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-05-02T20:54:59Z]

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/e2e/test_install_onboard_search.py (1)

59-61: 💤 Low value

Extract .text directly instead of json.dumps-ing the whole sentence dict.

The sentences payload is always a list of {"index", "text", "source"} dicts (confirmed by cli.py). json.dumps(s) happens to work because "quokka" appears in the text value, but it silently searches across all fields including index and source. Extracting s["text"] makes the intent explicit and prevents a false-positive if a field name ever coincidentally matches the token.

♻️ Proposed refactor
-    joined = " ".join(
-        s if isinstance(s, str) else json.dumps(s) for s in sentences
-    ).lower()
+    texts = [s["text"] if isinstance(s, dict) else s for s in sentences]
+    joined = " ".join(texts).lower()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/test_install_onboard_search.py` around lines 59 - 61, The test
currently concatenates sentence entries using json.dumps(s) which searches all
dict fields; change the generator to extract the text field explicitly by using
s["text"] when s is a dict (otherwise keep s if it's already a string) so the
line building joined uses s if isinstance(s, str) else s["text"] for s in
sentences, preserving .lower() afterwards; look for the variable joined and the
sentences iterable in test_install_onboard_search.py to update the expression.
tests/e2e/conftest.py (1)

50-52: 💤 Low value

callable (lowercase) is not a subscriptable generic — use Callable instead.

callable is the built-in predicate function, not a type alias. The string annotation silences the error via # type: ignore[name-defined], but the annotation itself is wrong. Callable is already available from collections.abc — just extend the import on line 21 and drop the suppressor.

♻️ Proposed fix
-from collections.abc import Iterator, Sequence
+from collections.abc import Callable, Iterator, Sequence
 ) -> Iterator[
-    "callable[..., subprocess.CompletedProcess[str]]"  # type: ignore[name-defined]
+    Callable[..., subprocess.CompletedProcess[str]]
 ]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/conftest.py` around lines 50 - 52, Replace the incorrect
string-quoted annotation "callable[..., subprocess.CompletedProcess[str]]" with
the proper typing generic Callable[..., subprocess.CompletedProcess[str]] and
remove the trailing "# type: ignore[name-defined]"; also add Callable to the
existing imports from collections.abc at the top of the module (so you import
Callable instead of relying on a string/ignore) to ensure the annotation is
valid and type-checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/e2e.yml:
- Around line 32-36: The workflow uses mutable tags for actions
(actions/checkout@v4, actions/setup-python@v5, astral-sh/setup-uv@v5); replace
each floating tag with the corresponding full commit SHA to hard-pin the exact
action revision and prevent supply-chain rewrites, and update astral-sh/setup-uv
to the current release line (e.g., v8.1.0) before pinning to that release's
commit SHA; locate the steps referencing actions/checkout, actions/setup-python,
and astral-sh/setup-uv in the e2e workflow and substitute the `@vN` refs with
their resolved commit SHAs (ensure you fetch the commit SHAs from the upstream
action repos) while keeping step-security/harden-runner unchanged.

---

Nitpick comments:
In `@tests/e2e/conftest.py`:
- Around line 50-52: Replace the incorrect string-quoted annotation
"callable[..., subprocess.CompletedProcess[str]]" with the proper typing generic
Callable[..., subprocess.CompletedProcess[str]] and remove the trailing "# type:
ignore[name-defined]"; also add Callable to the existing imports from
collections.abc at the top of the module (so you import Callable instead of
relying on a string/ignore) to ensure the annotation is valid and type-checks.

In `@tests/e2e/test_install_onboard_search.py`:
- Around line 59-61: The test currently concatenates sentence entries using
json.dumps(s) which searches all dict fields; change the generator to extract
the text field explicitly by using s["text"] when s is a dict (otherwise keep s
if it's already a string) so the line building joined uses s if isinstance(s,
str) else s["text"] for s in sentences, preserving .lower() afterwards; look for
the variable joined and the sentences iterable in test_install_onboard_search.py
to update the expression.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d7296a84-6f1d-49f0-b3c6-36ed988caaba

📥 Commits

Reviewing files that changed from the base of the PR and between 4094492 and aa9b627.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • .github/workflows/e2e.yml
  • tests/e2e/__init__.py
  • tests/e2e/conftest.py
  • tests/e2e/install-pipx.sh
  • tests/e2e/install-uv-tool.sh
  • tests/e2e/install-venv-pip.sh
  • tests/e2e/test_install_onboard_search.py

Comment thread .github/workflows/e2e.yml Outdated
Comment on lines +32 to +36
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- uses: astral-sh/setup-uv@v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Resolve the commit SHA for floating GitHub Actions version tags.
# Tags may point to annotated tag objects; de-reference with ^{} to get the commit SHA.
for spec in "actions/checkout:v4" "actions/setup-python:v5" "astral-sh/setup-uv:v5"; do
  repo="${spec%%:*}"
  tag="${spec##*:}"
  sha=$(gh api "repos/$repo/git/ref/tags/$tag" --jq '.object.sha')
  type=$(gh api "repos/$repo/git/ref/tags/$tag" --jq '.object.type')
  # If the tag is annotated, dereference to the underlying commit
  if [ "$type" = "tag" ]; then
    sha=$(gh api "repos/$repo/git/tags/$sha" --jq '.object.sha')
  fi
  latest_tag=$(gh api "repos/$repo/releases/latest" --jq '.tag_name' 2>/dev/null || echo "unknown")
  echo "$repo@$tag$sha  (latest release: $latest_tag)"
done

Repository: robotrocketscience/aelfrice

Length of output: 344


🏁 Script executed:

cat -n .github/workflows/e2e.yml | sed -n '25,40p'

Repository: robotrocketscience/aelfrice

Length of output: 687


🏁 Script executed:

#!/bin/bash
# Verify the correct SHA for astral-sh/setup-uv v8.1.0
gh api "repos/astral-sh/uv/git/ref/tags/v8.1.0" --jq '.object.sha, .object.type'

Repository: robotrocketscience/aelfrice

Length of output: 228


🏁 Script executed:

#!/bin/bash
# Query the correct repository: astral-sh/setup-uv for v8.1.0
gh api "repos/astral-sh/setup-uv/git/ref/tags/v8.1.0" --jq '.object'

Repository: robotrocketscience/aelfrice

Length of output: 247


🏁 Script executed:

#!/bin/bash
# Also check what the latest commit for v8.1.0 tag is
gh api "repos/astral-sh/setup-uv/releases/latest" --jq '.tag_name, .target_commitish'

Repository: robotrocketscience/aelfrice

Length of output: 119


Pin actions/checkout, actions/setup-python, and astral-sh/setup-uv to full commit SHAs.

Lines 32, 33, and 36 use floating @vN tags, which are mutable references. Git tags can be rewritten by anyone with write access to an action's repository. This was exploited in the tj-actions/changed-files incident (March 2025), where attackers rewrote all version tags on an action used by ~23,000 repos, exfiltrating CI secrets from every workflow that ran during the compromised window. step-security/harden-runner on line 29 is already pinned correctly — the same treatment is required for all three actions below it.

Additionally, astral-sh/setup-uv@v5 is significantly behind the current release (v8.1.0); consider upgrading while pinning.

🔒️ Proposed fix
      - uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
        with:
          egress-policy: audit
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
-      - uses: actions/setup-python@v5
+      - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
         with:
           python-version: '3.13'
-      - uses: astral-sh/setup-uv@v5
+      - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
         with:
           enable-cache: true
           cache-dependency-glob: "uv.lock"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- uses: astral-sh/setup-uv@v5
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.13'
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
🧰 Tools
🪛 GitHub Check: zizmor

[failure] 32-32:
unpinned action reference


[failure] 33-33:
unpinned action reference


[failure] 36-36:
unpinned action reference


[warning] 32-32:
credential persistence through GitHub Actions artifacts

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/e2e.yml around lines 32 - 36, The workflow uses mutable
tags for actions (actions/checkout@v4, actions/setup-python@v5,
astral-sh/setup-uv@v5); replace each floating tag with the corresponding full
commit SHA to hard-pin the exact action revision and prevent supply-chain
rewrites, and update astral-sh/setup-uv to the current release line (e.g.,
v8.1.0) before pinning to that release's commit SHA; locate the steps
referencing actions/checkout, actions/setup-python, and astral-sh/setup-uv in
the e2e workflow and substitute the `@vN` refs with their resolved commit SHAs
(ensure you fetch the commit SHAs from the upstream action repos) while keeping
step-security/harden-runner unchanged.

@robotrocketscience robotrocketscience added the e2e Trigger E2E workflow on this PR label May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

Reviewing as Gylf. Substantively clean: 4 commits all signed (G), discretion grep clean, required checks pass, diff scope is contained (308 insertions, e2e workflow + skeleton + scenario 1), spec-aligned per PR body.

Added e2e label to exercise the new workflow before merging — and it caught a real bug:

e2e (pipx) matrix leg failed at the Locate installed aelf step:

##[error]aelf binary not found at /home/runner/.local/bin/aelf

venv-pip and uv-tool legs both passed at that path. The pipx Install via pipx step itself succeeded ("installed package aelfrice 1.5.1, installed using Python 3.13.13 / These apps are now available"), so the binary exists — just not at the hardcoded path.

Likely cause: when pipx is bootstrapped via python -m pip install --user pipx without a subsequent pipx ensurepath, the ~/.local/bin/aelf symlink isn't guaranteed. The actual path is something like ~/.local/pipx/venvs/aelfrice/bin/aelf with ~/.local/bin/aelf only present if pipx's PATH wiring has run.

Fix options (one is enough):

  1. Run pipx ensurepath after install in install-pipx.sh, and in the workflow, echo "$HOME/.local/bin" >> "$GITHUB_PATH" for the pipx leg. Cheapest.
  2. Resolve the bin via command -v aelf rather than hardcoding the path in the Locate installed aelf step — works regardless of which install method is used and removes one source of drift.

Option 2 is more robust against future install-method additions; option 1 is less code change.

Releasing the review claim. Once the pipx leg goes green, this is a clean FF-merge for me.

— Gylf

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-05-02T21:07:04Z]

@robotrocketscience robotrocketscience removed the attn:review Needs review (PR open, awaiting reviewer) label May 2, 2026
@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 2, 2026
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-334-e2e-skeleton' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

@robotrocketscience
robotrocketscience force-pushed the feat/issue-334-e2e-skeleton branch from aa9b627 to 84279ad Compare May 2, 2026 21:20
@robotrocketscience robotrocketscience added attn:review Needs review (PR open, awaiting reviewer) and removed attn:merge-conflict PR branch needs rebase labels May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-05-02T21:21:10Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review (Kulili) — changes required before merge.

Blocking

1. e2e (pipx) fails — aelf not found at $HOME/.local/bin/aelf.
pipx reports "installed package aelfrice 1.5.1 / These apps are now available - aelf" but the locate step then errors with aelf binary not found at /home/runner/.local/bin/aelf (job 74071147804). On python -m pip install --user pipx (no pipx ensurepath), apps land under pipx environment --value PIPX_BIN_DIR which is not always ~/.local/bin on the GHA runner. Two clean fixes:

  • (preferred) Resolve the path from pipx itself: bin="$(pipx environment --value PIPX_BIN_DIR)/aelf".
  • Or run pipx ensurepath --force after install and locate via command -v aelf after re-sourcing PATH.

2. zizmor — 3 unpinned action references in .github/workflows/e2e.yml.
Lines 32 (actions/checkout@v4), 33 (actions/setup-python@v5), 36 (astral-sh/setup-uv@v5) need SHA pins to match the harden-runner pin already on line 31. Plus one warning at line 32 about credential persistence through artifacts (lower priority but worth a persist-credentials: false on the checkout if not needed).

Non-blocking observations

Releasing review claim. Reposting attn:review once the two items above are fixed.

@robotrocketscience robotrocketscience added attn:unblock Needs answer from another session and removed attn:review Needs review (PR open, awaiting reviewer) labels May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Kulili:2026-05-02T21:23:05Z]

@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 2, 2026
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-334-e2e-skeleton' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

@robotrocketscience
robotrocketscience force-pushed the feat/issue-334-e2e-skeleton branch from 84279ad to f2576cc Compare May 2, 2026 21:40
@robotrocketscience robotrocketscience added attn:review Needs review (PR open, awaiting reviewer) and removed attn:merge-conflict PR branch needs rebase attn:unblock Needs answer from another session labels May 2, 2026
@robotrocketscience robotrocketscience mentioned this pull request May 2, 2026
7 tasks
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-05-02T22:08:55Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review (Gylf) — both substantive blockers from the prior round are resolved.

Re-check vs. my 21:56 comment + Kulili's 21:21 comment:

  • e2e (pipx) ✅ — c834336 fix(e2e): resolve pipx bin via PIPX_BIN_DIR switches the pipx case to bin="$(pipx environment --value PIPX_BIN_DIR)/aelf". That's the preferred fix Kulili and I both named. Latest CI: e2e (pipx) SUCCESS.
  • zizmor ✅ — 731f02d ci(e2e): SHA-pin third-party actions SHA-pins actions/checkout@34e1148 # v4.3.1, actions/setup-python@a26af69 # v5.6.0, astral-sh/setup-uv@d4b2f3b # v5.4.2, and adds persist-credentials: false on checkout. All three flagged lines fixed; credential-persistence warning addressed. Latest CI: zizmor SUCCESS.

Other checks

  • Sigs: 6/6 commits show G.
  • Discretion grep on full diff vs github/main: clean.
  • All required CI green on the latest run (the CANCELLED rows are superseded by SUCCESS retries).

Remaining blocker — same as before, not author's diff

  • git merge-base --is-ancestor github/main github/feat/issue-334-e2e-skeleton → false. attn:merge-conflict correctly labeled. Once rebased onto current github/main and force-pushed (preserving the existing signed commits — same key), this is FF-mergeable as-is. No code changes needed.

Releasing review claim.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-05-02T22:10:20Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-05-02T22:18:57Z]

@github-actions github-actions Bot added the attn:merge-conflict PR branch needs rebase label May 2, 2026
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

This PR is now behind main. Rebase locally so your commit signatures stay intact:

git fetch origin && git checkout 'feat/issue-334-e2e-skeleton' && git rebase origin/main
# resolve conflicts if any, then
git push --force-with-lease

Auto-rebase was removed because the bot has no signing key; rebasing as the bot strips author signatures and the required_signatures rule on main then blocks the merge. See #341.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Gylf:2026-05-02T22:19:57Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Review (Kulili): diff is clean and scoped to #334 step 1 — e2e workflow on PR e2e label opt-in, install-method matrix scripts, fixtures (ephemeral_db, installed_aelf, aelf_run, tiny_project), and scenario 1 (version / onboard --emit-candidates / empty-store search). All 6 commits signed. CI green on the latest run (the earlier CANCELLED E2E rows are superseded by the action-pin commit's re-run). Discretion grep: empty.

One blocker: not fast-forwardable — main has advanced (v1.6.0 release + #288 rebuild_log instrumentation). Branch protection requires FF + signatures, and gh pr merge does not work on this repo, so this needs a rebase against github/main before I can FF-push.

Action: please rebase onto current github/main and force-push. No code changes requested. Re-flagging attn:review after the rebase will pick this back up.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Kulili:2026-05-02T22:20:09Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Toug:2026-05-02T22:20:13Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Toug:2026-05-02T22:20:18Z]

@robotrocketscience

Copy link
Copy Markdown
Owner Author

Reviewed by Gylf. Code/CI/signatures look good (all 6 commits G, e2e matrix green, discretion clean). But FF check fails after #358 merged — attn:merge-conflict already on PR. Please rebase onto current main and re-flag attn:review. Releasing review claim.

@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Gylf:2026-05-02T22:20:33Z]

@robotrocketscience robotrocketscience added attn:merge-conflict PR branch needs rebase and removed attn:review Needs review (PR open, awaiting reviewer) attn:merge-conflict PR branch needs rebase labels May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[claim:review:Kulili:2026-05-02T22:26:02Z]

Boundary rule: e2e tests invoke 'aelf' as installed (subprocess), never
via in-process imports. Three fixtures land:

- ephemeral_db: per-test SQLite path (tmp_path).
- installed_aelf: argv prefix; resolves AELFRICE_E2E_BIN > shutil.which
  > 'uv run aelf' fallback so the suite is runnable both in CI (per
  install-method matrix leg) and locally.
- aelf_run: convenience callable that pins AELFRICE_DB to the
  ephemeral DB and runs the binary with capture_output.
- tiny_project: synthetic git repo with three commits and a
  distinctive token ('quokka calibration'). Generated inline; no
  on-disk fixture data; no directory-of-origin concerns.

Tracks #334.
Three test cases that exercise the install -> ingest -> retrieve loop
through the real argv entry point and a real SQLite file:

- aelf --version: smoke check for the install matrix; non-empty output
  confirms the binary is on PATH and importable.
- onboard --emit-candidates: against a synthetic three-commit fixture
  containing the distinctive token 'quokka', expect that token to
  appear in the JSON sentences[] payload. Catches ingest-path drift.
- search against an empty store: exit 0 even with zero hits. Catches
  the regression class where a missing migration leaves FTS5 unbuilt.

pytestmark sets a 120s per-test timeout to override the project-wide
5s default — subprocess invocations of the installed binary are not
in-process tests.

Tracks #334.
Three single-purpose shell scripts, one per CI matrix leg. Each is one
substantive line (per spec: keep them dumb; reject anything that
branches on env state). The e2e workflow invokes whichever is named
by matrix.install-method.

- install-uv-tool.sh:  uv tool install --force .
- install-pipx.sh:     pipx install --force .
- install-venv-pip.sh: python -m venv .e2e-venv && pip install .

Tracks #334.
Initial trigger per spec rollout step 1: PR opt-in via the 'e2e' label
only. Step-4 of the rollout flips this to also run on push to main and
adds the attn:e2e-failure issue-opening step.

Workflow shape:
- Matrix over install-method = [uv-tool, pipx, venv-pip].
- Each leg invokes tests/e2e/install-<method>.sh, locates the
  resulting binary, and runs the e2e suite with AELFRICE_E2E_BIN set
  so the suite uses the freshly-installed binary (not 'uv run aelf').
- fail-fast: false so a single matrix-leg failure doesn't mask
  failures in the other legs.
- timeout-minutes: 8 per spec hard cap.
- harden-runner audit egress, pinned actions.

ci.yml: pytest now uses --ignore=tests/e2e so the unit job stays in
process and ignores the e2e suite (which would crash without the
matrix install step setting AELFRICE_E2E_BIN).

Tracks #334.
The pipx leg of the e2e matrix hardcoded $HOME/.local/bin/aelf, which
is not where pipx installs entry-points on the GitHub Actions ubuntu-
latest runner. `pipx environment --value PIPX_BIN_DIR` is the
authoritative source. Locate-aelf step now uses it for the pipx case;
uv-tool and venv-pip cases unchanged.

Refs: #334
zizmor flagged three tag-pinned actions in .github/workflows/e2e.yml
and the missing credential-persistence guard on checkout. Pins match
SHAs already used elsewhere in the repo (checkout v4.3.1, setup-uv
v5.4.2). setup-python v5 had no prior pin in the repo; pinned to
a26af69 (v5.6.0, current v5 head).

Refs: #334
@robotrocketscience
robotrocketscience force-pushed the feat/issue-334-e2e-skeleton branch from 731f02d to f5cfa33 Compare May 2, 2026 22:26
@robotrocketscience
robotrocketscience merged commit f5cfa33 into main May 2, 2026
21 checks passed
@robotrocketscience
robotrocketscience deleted the feat/issue-334-e2e-skeleton branch May 2, 2026 22:29
@robotrocketscience robotrocketscience removed the attn:merge-conflict PR branch needs rebase label May 2, 2026
@robotrocketscience

Copy link
Copy Markdown
Owner Author

[release:review:Kulili:2026-05-02T22:29:47Z]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author-Setr PR coordination mutex e2e Trigger E2E workflow on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants