Skip to content
Draft
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
27 changes: 19 additions & 8 deletions .claude/commands/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,18 @@ When the user signals they're ready:

1. Re-read the spec file with the Read tool.
2. Collect all `> **Review:** ...` markers and note any direct edits.
3. Address review comments **one at a time** in document order:
a. Present your analysis of the comment — the trade-offs, your
recommendation, and why.
b. **PAUSE — wait for the user's decision before editing.**
c. Update the spec to reflect the resolved decision; remove the
review marker.
d. Tell the user what changed, then move to the next comment.
4. After all comments are resolved, invite another review pass.
3. Present the **first unresolved** review comment only:
- State the comment and its location.
- Give your analysis — trade-offs, recommendation, and why.
**PAUSE — wait for the user's decision before doing anything else.**
4. Once the user decides:
a. Update the spec to reflect the decision; remove the review marker.
b. Tell the user what changed.
c. Present the **next** unresolved review comment (go to step 3).
5. After all comments are resolved, invite another review pass.

**Never present more than one review comment at a time. Always pause for a
decision before moving to the next comment or making any edits.**

Repeat Phase 3 until the user says the document is ready.

Expand Down Expand Up @@ -119,6 +123,13 @@ When Phase 4 is complete:
- Exit criteria as a checkbox list; for tasks that include new E2E tests,
write those exit criteria as Gherkin-style acceptance scenarios
(`Given / When / Then`)

**Implementation ordering:** Order tasks so that primary consumers are
implemented before the dependencies they rely on. For each dependency that
isn't ready yet, introduce a stub that provides known-good static data
(e.g. hardcoded or serialized from the current implementation). Replace
stubs with real implementations one step at a time. Every task's exit
criteria must include: all existing unit and E2E tests pass.
3. If the spec has a `## Related Epics` section listing features to be
spec'd separately, add those as placeholder entries in `## Tasks` as
well — titled "Create epic: \<name\>" with a one-line scope description.
Expand Down
120 changes: 94 additions & 26 deletions .claude/commands/team-task.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ this task from spec to merged PR. Keep your own context lean:
- Direct sub-agents to read from and write to GitHub PR and Jira directly — do not relay
large payloads through yourself

**Before starting**, capture the current working branch — this is the **base branch**
(PR target, Developer branches off this):
**Before starting**, capture the base branch — this is the PR target and the branch the
Developer will branch off. The skill may be invoked on an ephemeral harness branch, so
find the upstream remote branch at the current commit rather than using `git branch --show-current`:

```bash
# Find the remote branch this harness branch was created from
git branch -r --contains HEAD | grep -v 'HEAD\|claude/' | head -1 | sed 's|.*origin/||' | xargs echo
```
git branch --show-current
```

If that returns nothing (no matching remote branch), fall back to `git branch --show-current`.

---

Expand Down Expand Up @@ -126,6 +130,11 @@ Developer instructions:
> **Task key:** TASK_KEY
> **Base branch:** BASE_BRANCH
>
> **Non-negotiable build rule:** After making any changes, you MUST run
> `scripts/validate-build.sh` before committing. Never run `dotnet build` directly.
> Never run `dotnet test`, `scripts/validate-tests.sh`, or any test command — a
> dedicated Tester agent handles all testing.
>
> **Task brief:**
> TASK_BRIEF
>
Expand Down Expand Up @@ -168,7 +177,7 @@ Developer instructions:
>
> **Step 4 — Build**
>
> Do not run tests — that is the Tester agent's responsibility. Only run the build:
> Run the build validation script and nothing else:
>
> ```
> scripts/validate-build.sh
Expand All @@ -177,6 +186,9 @@ Developer instructions:
> The script stages new files and cleans before building — do not run `git add -A`
> separately. Fix all warnings and errors and re-run until the build is clean.
>
> **NEVER run `dotnet test`, `scripts/validate-tests.sh`, or any other test command.**
> A dedicated Tester agent handles all testing.
>
> **Step 5 — Commit and push**
>
> Commit format: `feat: description [TASK_KEY]`
Expand Down Expand Up @@ -250,8 +262,8 @@ Tester instructions:
>
> Check out the branch and fix each failure. You may re-run individual tests to
> verify a specific fix (e.g. `dotnet test --filter "FullyQualifiedName~TEST_NAME"`),
> but do not run the full suite — that is the Tester agent's job. When done, confirm
> the build is still clean, then commit and push:
> but **NEVER run `scripts/validate-tests.sh` or the full test suite** — that is the
> Tester agent's job. When done, confirm the build is still clean, then commit and push:
>
> ```
> git checkout BRANCH_NAME
Expand All @@ -270,14 +282,18 @@ Re-run Tester. Repeat until no failures.

### Phase 4 — Create PR

Create the pull request. Substitute actual values for all placeholders:
Create the pull request **as a draft**. Use `mcp__github__create_pull_request` with `draft: true`.
Substitute actual values for all placeholders:

```
gh pr create \
--base BASE_BRANCH \
--head BRANCH_NAME \
--title "[TASK_KEY] <concise description from task brief>" \
--body "$(cat <<'EOF'
mcp__github__create_pull_request(
owner="jodavis",
repo="adaptiveremote",
base=BASE_BRANCH,
head=BRANCH_NAME,
title="[TASK_KEY] <concise description from task brief>",
draft=true,
body="""
Jira: https://jodasoft.atlassian.net/browse/TASK_KEY

## What changed
Expand All @@ -289,11 +305,11 @@ Jira: https://jodasoft.atlassian.net/browse/TASK_KEY
`scripts/validate-tests` passes — unit tests and headless E2E tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
"""
)
```

Capture the PR URL from the output.
Capture the PR URL and PR number from the output.

Optional (silent fail): `mcp__github__request_copilot_review`
Optional (silent fail): set Jira status to "In Review" via `mcp__jira__editJiraIssue`
Expand Down Expand Up @@ -338,12 +354,32 @@ Reviewer instructions:
> headless E2E coverage
> - `.editorconfig` compliance
>
> For each issue found, post a comment **directly to the PR**:
> ```
> gh pr review PR_URL --comment -b "path/to/File.cs:LINE — your comment"
> Collect all issues into a JSON array (schema below). If the array is non-empty,
> post them as a **single formal PR review** with line-anchored comments so each
> becomes a resolvable discussion thread:
>
> ```bash
> # Extract PR number from PR_URL (e.g. .../pull/171 → 171)
> PR_NUMBER=<number>
> COMMIT_SHA=$(git rev-parse HEAD)
>
> # Write review payload to a temp file
> cat > /tmp/review.json << EOF
> {
> "commit_id": "$COMMIT_SHA",
> "event": "COMMENT",
> "body": "Automated review — see inline comments.",
> "comments": [
> { "path": "relative/path/to/File.cs", "line": 42, "body": "your comment" }
> ]
> }
> EOF
>
> gh api repos/jodavis/adaptiveremote/pulls/$PR_NUMBER/reviews \
> -X POST --input /tmp/review.json
> ```
>
> After posting all comments, return a JSON array. Return only the JSON — no other text.
> Return a JSON array. Return only the JSON — no other text.
> An empty array means no issues.
>
> ```json
Expand Down Expand Up @@ -374,6 +410,11 @@ Developer instructions:
> **Branch:** BRANCH_NAME
> **PR URL:** PR_URL
>
> **Non-negotiable build rule:** After making any changes, you MUST run
> `scripts/validate-build.sh` before committing. Never run `dotnet build` directly.
> Never run `dotnet test`, `scripts/validate-tests.sh`, or any test command — a
> dedicated Tester agent handles all testing.
>
> **Task brief:**
> TASK_BRIEF
>
Expand All @@ -395,13 +436,16 @@ Developer instructions:
> gh pr review PR_URL --comment -b "File.cs:LINE — [your rebuttal]"
> ```
>
> **Step 4** — Build, commit, and push:
> **Step 4** — Build, commit, and push. Run only the build script — no test commands:
> ```
> scripts/validate-build.sh
> git commit -m "review: address feedback [TASK_KEY]"
> git push
> ```
>
> **NEVER run `dotnet test`, `scripts/validate-tests.sh`, or any other test command.**
> A dedicated Tester agent handles all testing.
>
> Return: `{ "status": "done", "branch": "BRANCH_NAME" }`

After Developer finishes, update `REVIEWER_BASELINE` to the current HEAD commit:
Expand Down Expand Up @@ -437,12 +481,27 @@ Then spawn **Tester and scoped Reviewer in parallel** and wait for both.
> accessibility regressions, or clear spec non-compliance. Do not raise style, naming,
> or minor cleanup issues.
>
> For each issue, post a comment directly to the PR:
> ```
> gh pr review PR_URL --comment -b "path/to/File.cs:LINE — your comment"
> Collect all issues into a JSON array (same schema as Phase 5). If non-empty,
> post them as a single formal PR review with line-anchored comments:
>
> ```bash
> PR_NUMBER=<number>
> COMMIT_SHA=$(git rev-parse HEAD)
> cat > /tmp/review.json << EOF
> {
> "commit_id": "$COMMIT_SHA",
> "event": "COMMENT",
> "body": "Follow-up review — see inline comments.",
> "comments": [
> { "path": "relative/path/to/File.cs", "line": 42, "body": "your comment" }
> ]
> }
> EOF
> gh api repos/jodavis/adaptiveremote/pulls/$PR_NUMBER/reviews \
> -X POST --input /tmp/review.json
> ```
>
> Return a JSON array (same schema as before). Return only the JSON — no other text.
> Return a JSON array. Return only the JSON — no other text.
> An empty array means all previous comments are resolved and no new significant issues exist.

**Routing after both complete:**
Expand All @@ -457,8 +516,17 @@ Loop, updating `REVIEWER_BASELINE` each time before the parallel spawn.

## Completion

When the Reviewer returns an empty array, tell the user:
When the Reviewer returns an empty array:

1. **Mark the PR ready for review** using `mcp__github__update_pull_request` with `draft: false`
(owner="jodavis", repo="adaptiveremote", pullNumber=PR_NUMBER).

2. **Request a review from @jodavis** using `mcp__github__update_pull_request` with
`reviewers: ["jodavis"]` (owner="jodavis", repo="adaptiveremote", pullNumber=PR_NUMBER).

3. Tell the user:

> Implementation complete.
> PR: PR_URL
> All tests pass and all review comments have been addressed or rebutted on the PR.
> The PR has been marked ready for review and @jodavis has been requested as a reviewer.
18 changes: 18 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,30 @@ mock verification. Group setup calls into `Expect_*` helper methods.
### E2E tests
Prefer the Headless host for new E2E tests — cross-platform, no display required:

**IMPORTANT:** Before running E2E tests for the first time, you must set up Playwright browsers.

**On developer machines (Windows/Mac/Linux with internet access):**
```bash
# Build the Headless host first (required to generate the Playwright installation script)
dotnet build src/AdaptiveRemote.Headless/AdaptiveRemote.Headless.csproj

# Install Playwright browsers (one-time setup)
pwsh src/AdaptiveRemote.Headless/bin/Debug/net10.0/playwright.ps1 install chromium # if tests crash at startup with a JSON-RPC disconnect

dotnet test --filter "FullyQualifiedName~Host.Headless"
```

**In Claude Code cloud sandbox environments** (where `cdn.playwright.dev` is blocked by network
policy): browsers are pre-installed at `/opt/pw-browsers` and the environment is configured to point
Playwright there automatically. No extra setup is required:
```bash
dotnet test --filter "FullyQualifiedName~Host.Headless"
```

If Headless E2E tests fail with JSON-RPC connection errors in a cloud sandbox environment, this indicates
the environment configuration is broken — stop and report the problem rather than working around it with
the setup script. The goal is to be alerted when the environment stops working, not to silently fall back.

## Documentation

### `_spec_*.md` — pre-implementation design docs
Expand Down
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<ItemGroup>
<!-- Enforce code analysis with latest analyzers -->
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" PrivateAssets="all" />
<PackageReference Include="Nerdbank.MessagePack" />
</ItemGroup>

<PropertyGroup>
Expand Down
5 changes: 3 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
<PackageVersion Include="Microsoft.Extensions.Configuration.Ini" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
<!-- OpenTelemetry -->
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.15.3" />
<!-- Third-party Libraries -->
<PackageVersion Include="I8Beef.TiVo" Version="1.0.0.14" />
Expand All @@ -30,6 +30,7 @@
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<!-- Development Tools -->
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.9.50" />
<PackageVersion Include="Nerdbank.MessagePack" Version="1.1.62" />
<!-- Code Analysis -->
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.103" />
<PackageVersion Include="Microsoft.VisualStudio.Threading" Version="17.14.15" />
Expand Down
2 changes: 1 addition & 1 deletion scripts/validate-tests.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
pushd %~dp0..
dotnet test --no-build "%~dp0validate-unit-tests.proj"
if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
dotnet test --no-build "%~dp0validate-e2e-tests.proj"
dotnet test --no-build "%~dp0validate-e2e-tests.proj" -m:1
if %ERRORLEVEL% neq 0 ( popd & exit /b %ERRORLEVEL% )
popd
2 changes: 1 addition & 1 deletion scripts/validate-tests.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ cd "$SCRIPT_DIR/.."
echo 'Testing unit test projects...'
dotnet test --no-build "$SCRIPT_DIR/validate-unit-tests.proj"
echo 'Testing E2E test projects...'
dotnet test --no-build "$SCRIPT_DIR/validate-e2e-tests.proj"
dotnet test --no-build "$SCRIPT_DIR/validate-e2e-tests.proj" -m:1
11 changes: 6 additions & 5 deletions src/AdaptiveRemote.App/Components/BlazorAppScope.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using AdaptiveRemote.Services.Lifecycle;
using AdaptiveRemote.Services.Lifecycle;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.JSInterop;

namespace AdaptiveRemote.Components;

Expand Down Expand Up @@ -47,12 +49,11 @@ public Task InvokeInScopeAsync(Func<IServiceProvider, CancellationToken, Task> w
return workItem(_serviceProvider, cancellationToken);
}

public Task RecycleAsync()
public async Task RecycleAsync()
{
_logger.LogInformation("Recycling Blazor application scope.");

// In a real implementation, this would refresh the browser which should result
// in a new scope
throw new NotImplementedException();
IJSRuntime jsRuntime = _serviceProvider.GetRequiredService<IJSRuntime>();
await jsRuntime.InvokeVoidAsync("location.reload");
}
}
5 changes: 5 additions & 0 deletions src/AdaptiveRemote.App/Components/Remote.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
@inject Services.IRemoteDefinitionService RemoteDefinitions
@inject Services.IDynamicStylesheetProvider Stylesheet

@if (Stylesheet.GetCss() is { } css)
{
<style>@((MarkupString)css)</style>
}
<ModalMessageUI />
<RemoteLayout LayoutElement="@RemoteDefinitions.RemoteRoot" ProgramMode="@ProgramMode" />

Expand Down
Loading
Loading