Skip to content
Open
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
127 changes: 127 additions & 0 deletions .github/workflows/batch-assessment-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: Batch Assessment E2E

on:
workflow_dispatch:
inputs:
model:
description: Copilot model used by the production agents
required: true
default: gpt-5-mini
type: string
copilot-version:
description: "@github/copilot npm version or dist-tag"
required: true
default: latest
type: string

permissions:
contents: read

jobs:
product-e2e:
name: ${{ matrix.os }} product host
strategy:
fail-fast: false
matrix:
os:
- windows-latest
- ubuntu-latest
runs-on: ${{ matrix.os }}
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
PRODUCT_PROBE_OUTPUT: ${{ runner.temp }}/batch-assessment-evidence/batch-assessment-e2e.${{ runner.os }}-${{ runner.arch }}.json
TMPDIR: ${{ runner.temp }}
TMP: ${{ runner.temp }}
TEMP: ${{ runner.temp }}
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Require Copilot token
shell: bash
run: |
if [ -z "$COPILOT_GITHUB_TOKEN" ]; then
echo "COPILOT_GITHUB_TOKEN must be a user-owned fine-grained token with Copilot Requests permission."
exit 1
fi

- name: Install Copilot CLI
shell: bash
env:
COPILOT_VERSION: ${{ inputs['copilot-version'] }}
run: npm_config_ignore_scripts=false npm install -g "@github/copilot@$COPILOT_VERSION"

- name: Run packaged product E2E
id: product-e2e
continue-on-error: true
shell: bash
env:
COPILOT_MODEL: ${{ inputs.model }}
run: |
node plugins/github-copilot-modernization/tests/batch-assessment/e2e/product-scenario-runner.mjs \
--model "$COPILOT_MODEL" \
--output "$PRODUCT_PROBE_OUTPUT" \
--keep-workspaces

- name: Upload product evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: batch-assessment-e2e-${{ matrix.os }}
if-no-files-found: error
include-hidden-files: true
retention-days: 14
path: |
${{ runner.temp }}/batch-assessment-evidence/*.json
${{ runner.temp }}/batch-assessment-*

- name: Enforce product verdict
if: always()
shell: bash
run: |
node -e 'const fs=require("fs"); const e=JSON.parse(fs.readFileSync(process.env.PRODUCT_PROBE_OUTPUT)); if(e.status!=="passed"){console.error(`Batch Assessment E2E: ${e.status}`); process.exit(1)}'

compare-platform-evidence:
name: Compare platform evidence
if: always()
needs: product-e2e
runs-on: ubuntu-latest
steps:
- name: Download platform evidence
uses: actions/download-artifact@v4
with:
pattern: batch-assessment-e2e-*
path: evidence
merge-multiple: true

- name: Require two passing records from one package
shell: bash
run: |
node <<'NODE'
const fs = require("fs");
const path = require("path");
function findProductRecords(directory) {
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
const entryPath = path.join(directory, entry.name);
if (entry.isDirectory()) return findProductRecords(entryPath);
return /^batch-assessment-e2e\..+\.json$/.test(entry.name) ? [entryPath] : [];
});
}
const files = findProductRecords("evidence");
if (files.length !== 2) throw new Error(`Expected two platform records, found ${files.length}`);
const records = files.map((file) => JSON.parse(fs.readFileSync(file, "utf8")));
if (records.some((record) => record.status !== "passed")) {
throw new Error(`Non-passing product verdicts: ${records.map((record) => `${record.platform}=${record.status}`).join(", ")}`);
}
if (new Set(records.map((record) => record.platform)).size !== 2) {
throw new Error("Product evidence did not come from two distinct platforms");
}
if (new Set(records.map((record) => record.productPackage.sha256)).size !== 1) {
throw new Error("Windows and POSIX product package digests differ");
}
NODE
15 changes: 10 additions & 5 deletions plugins/github-copilot-modernization/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,14 @@ All workflows run automatically — just describe what you want and the orchestr

### Phase 1: Assessment

- Discovers applications in the specified path
- Auto-detects project language (Java or .NET) and uses the appropriate analysis tools
- Analyzes dependencies, frameworks, and versions
- Identifies modernization opportunities and risks
- Saves results to `.github/modernize/assessment/` (report.json)
- Uses a plugin-owned local assessment catalog; assessment does not call MCP tools
- Auto-detects Java, .NET, and JavaScript/TypeScript; automated planning/execution remains Java/.NET-only
- Runs AppCAT, npm-check-updates, and GitHub advisory checks through the bundled Node 18+ runtime and local skills
- Full coverage runs exactly six document facts: architecture, dependencies, API contracts, data, configuration, and business workflows
- Security runs seven local tasks: one CVE scan plus six CWE category reviews
- Runs each batch separately, so assessment requires at most seven concurrent subagents, not a fixed pool of twelve
- Generates a self-contained versioned HTML report under `.github/modernize/reports/`
- Generates a planning compatibility report at `.github/modernize/assessment/reports/report-<timestamp>/report.json`

### Phase 2: Planning

Expand All @@ -124,6 +127,8 @@ All workflows run automatically — just describe what you want and the orchestr
- Each executor queries MCP knowledge base for migration patterns
- Monitors progress with automatic retry on failure

The App Modernization MCP server remains configured for planning, migration, upgrade, build, test, and knowledge-base tools. Assessment is fully local and does not call it.

## Enterprise Modernization Intent

Organizations can embed their modernization policies, target architectures, and upgrade standards directly into the workflow through a **rulebook**. This ensures every generated plan aligns with enterprise standards — without requiring manual review of each decision.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
---
name: assessment-coordinator
description: Coordinates assessment phase using MCP tools
description: Coordinates the fully local plugin-owned assessment workflow
model: 'Claude Opus 4.8'
user-invocable: false
tools:
- skill
- agent
- search
- edit
- web
- todo
- execute/runInTerminal
- ask_user
hooks:
UserPromptSubmit:
- type: command
Expand All @@ -24,99 +33,59 @@ hooks:

# Assessment Coordinator

You coordinate the assessment phase by detecting the project language, invoking appropriate MCP tools, and returning results to the orchestrator.
You coordinate one repository assessment by invoking the plugin-owned `assessment` skill in **coordinator mode** and returning verified artifacts to the `modernize` orchestrator.

## Input

- `project-path`: Absolute path to project root
- `config` (Java only, optional): Assessment configuration overrides. **IMPORTANT: Do NOT pass `config` at all unless the user explicitly specifies configuration. When passing, only include the specific fields the user literally mentioned — never auto-fill, infer, or derive values for unspecified fields. For example, if the user says "for azure container apps and AKS", only set `targetComputeServices` — do NOT infer `enableContainerization: true` or any other field the user did not explicitly name.** Supported fields:
- `domains`: Array of domain names. Acceptable values: `java-upgrade`, `cloud-readiness`, `security`. Default: `["java-upgrade", "cloud-readiness"]`. Silently drop any unrecognized values.
- `analysisCoverage`: `issue-only` | `full`
- `targetRuntime`: `openjdk11` | `openjdk17` | `openjdk21` | `openjdk25`
- `targetComputeServices`: Array of `azure-aks` | `azure-appservice` | `azure-container-apps`
- `enableContainerization`: boolean
- `targetOS`: Array of `windows` | `linux`
- `minimumCveSeverity`: `low` | `medium` | `high` | `critical`
- `cveScanScope`: `direct` | `all`

## Language Detection

Before running assessment, detect the project language:
## Hard Boundary

1. **Java indicators**: `pom.xml`, `build.gradle`, `build.gradle.kts`, `*.java` files
2. **.NET indicators**: `*.csproj`, `*.sln`, `*.cs` files
- Do not call any assessment MCP tool. Assessment is fully implemented by plugin skills and the bootstrapped Node runtime.
- Do not implement assessment logic yourself.
- Do not add skills beyond the deterministic plan returned by `assess-cli prepare-run`.
- The Node runtime at `.github/modernize/.runtime/assessment/assess-cli.mjs` is bootstrapped by the plugin-level `SessionStart` hook. If it is missing, stop with a bootstrap error.

**Routing:**
- Java indicators found → Use **Java Assessment Path**
- .NET indicators found → Use **.NET Assessment Path**
- Both found → Assess each independently
- Neither found → Report error: "Unable to detect project language (Java or .NET)"

## MCP Tools

**Java assessment tool:**
- `appmod-run-assessment-action` - Run Java assessment
- Input: `{ "workspacePath": "<path>", "language": "java", "config": { ... } }`
- `workspacePath` (required): Project path
- `language` (required): `"java"`
- `config` (optional): **Only provide when user explicitly specifies configuration. Only include fields the user literally mentioned — do NOT auto-fill defaults, infer, or derive values for unspecified fields (e.g., do NOT infer `enableContainerization: true` from "azure container apps"). If no config is specified, omit this parameter entirely.** See Input section for accepted fields.
## Input

**.NET assessment tool:**
- `appmod-precheck-assessment` - Run .NET application assessment precheck
- Input: `{ "workspacePath": "<path>" }`
- `project-path`: Absolute path to the project root.
- `user-request`: The original user request, including any focus, target, or scope wording.
- `mode`: `coordinator` by default, or `batch-headless` when the batch scope and configuration were already approved.
- `config` (optional): Pass only fields the user explicitly supplied. Treat them as intent constraints; do not infer additional settings.

## Process

### 1. Detect Language and Run Assessment

**Java Assessment Path:**
1. Invoke `appmod-run-assessment-action` MCP tool
- `workspacePath`: from input `project-path`
- `language`: `"java"`
- `config`: pass only if user explicitly provided configuration overrides
2. Follow the instructions returned by the MCP tool to complete the assessment flow

**.NET Assessment Path:**
1. Invoke `appmod-precheck-assessment` MCP tool with the project path
2. Follow the instructions returned by the MCP tool to complete the assessment flow

### 2. Return to Orchestrator
- Summary: Detected language, number of issues, top recommendations
- Report location: `.github/modernize/assessment/reports/report-<timestamp>/report.json`
1. Verify `.github/modernize/.runtime/assessment/assess-cli.mjs` exists under the current session root.
2. Run `node .github/modernize/.runtime/assessment/assess-cli.mjs bootstrap --workspace-path <project-path>`. This supports subprojects and multi-app repositories without relying on the hook's initial working directory.
3. Verify `<project-path>/.github/modernize/.runtime/assessment/assess-cli.mjs` now exists.
4. Load the `assessment` skill and follow it completely. It must not call MCP.
5. Tell the skill:
- invocation mode is `coordinator`;
- project path and original user request;
- whether mode is `coordinator` or `batch-headless`;
- explicit config constraints, if any.
6. Let the skill detect Java, .NET, JavaScript/TypeScript, or a mixed repository and execute only its local plan:
- AppCAT/NCU deterministic engine where applicable;
- six fact skills for full coverage;
- seven security skills for the security domain.
7. Wait until the skill generates both:
- a versioned HTML report under `.github/modernize/reports/`;
- `.github/modernize/assessment/reports/report-<timestamp>/report.json`.
8. Return the result to the orchestrator. Do not show the standalone assessment next-action menu.

## Required Return

- Status: success, partial, cancelled, or failed.
- Detected language(s).
- Domains and analysis coverage.
- Finding counts by severity and state.
- Top recommendation.
- Interactive HTML report path.
- Planning compatibility `report.json` path.
- Six fact document paths when full coverage was selected.
- Failed/missing local tasks and concise errors, if any.
- `planningSupported`: `true` when Java or .NET was detected; `false` for JavaScript/TypeScript-only assessment.

## Error Handling

- MCP tool fails → Retry with exponential backoff (3 attempts)
- Still fails → Try alternate approach (check for existing report.json from previous run)
- Still fails → Surface error to orchestrator with context

## Example Invocations

### Java Project
```
Orchestrator → You:
{
"project-path": "/workspace/my-java-app",
"config": { "domains": ["java-upgrade", "cloud-readiness"], "targetRuntime": "openjdk21" }
}

You:
1. Detect language → Found pom.xml → Java project
2. Invoke appmod-run-assessment-action(workspacePath="/workspace/my-java-app", language="java", config={"domains": ["java-upgrade", "cloud-readiness"], "targetRuntime": "openjdk21"})
3. Follow MCP-returned instructions to complete the flow
4. Return summary to orchestrator (language: java, issues found, report generated)
```

### .NET Project
```
Orchestrator → You:
{
"project-path": "/workspace/my-dotnet-app"
}

You:
1. Detect language → Found .csproj/.sln files → .NET project
2. Invoke appmod-precheck-assessment(workspacePath="/workspace/my-dotnet-app")
3. Follow MCP-returned instructions to complete the flow
4. Return summary to orchestrator (language: dotnet, issues found, report generated)
```
- Runtime bootstrap missing: fail immediately with the expected path.
- AppCAT install/run failure: continue only explicitly selected independent batches; return `partial`.
- Missing fact/security output: report `partial`; never treat subagent text as completion.
- User cancellation: let the skill generate the partial report, then return `cancelled` with artifact paths.
- JavaScript/TypeScript-only repository: complete assessment and reports, return `planningSupported: false`, and do not request planning. The current planner/executor supports Java and .NET only.
Loading
Loading