jctanner/adlc-workflow

★ 0Forks 0PythonGitHub ↗Compare

README

adlc-workflow

Profile-driven ADLC workflow tooling for turning approved RHAI feature requests into reviewed, published strategy artifacts. It implements the workflow proposed in proposals/013-fullsend-strat-workflow/proposal.md, with runtime state kept private to tooling and publishable artifacts kept under the profile-defined artifact layout.

Three execution modes

The workflow deliberately supports three ways to run the same profile and task protocol. This is a core project feature, not transitional compatibility code. Different teams have different, reasonable perspectives on how agentic work should be controlled: an agent can own the whole run, an agent can hand work to a deterministic harness, or a harness can be invoked directly. ADLC keeps those choices comparable so evidence—not an assumed architecture—can determine which method is best for a workflow.

Mode Entry point Control boundary Useful when
Agent-led claude -p "/adlc-workflow:adlc-workflow RHAIRFE-1" The outer Claude session drives the workflow, skills, and protocol tools. Exploring the workflow, using adaptive judgment, or retaining the full native Claude session trace.
Skill → CLI handoff claude -p "/adlc-workflow:adlc-workflow --handoff --profile=rhai-feature-creator RHAIRFE-1" The outer skill performs one delegation to the deterministic controller, which launches bounded workers. Keeping a Claude-facing entry point while making scheduling, retries, parallelism, state, and artifact handling deterministic.
Direct CLI adlc-workflow handoff --profile rhai-feature-creator RHAIRFE-1 The deterministic controller owns the run directly and launches Claude only for bounded LLM tasks. Automation, batch execution, parallel item processing, structured observability, and minimal outer-agent context use.

All three modes use the same profile, core task/result contract, context preparation, private state, artifact layout, publication adapters, and bounded worker definitions. They differ only in who controls the workflow loop. The controller modes retain normalized event streams and raw worker transcripts; agent-led mode retains Claude's native stream. Evaluate modes on artifact quality, reliability, cost, latency, observability, and operator fit before standardizing on one.

Batch execution

A batch is one run with a frozen set of selected tickets, per-ticket tasks and artifacts, and a publication barrier after all selected tickets finish processing. The controller accepts explicit issue keys or discovers eligible tickets from the profile's initial gate. Discovery applies eligibility checks before --batch-offset and --batch-size; explicit keys preserve their supplied order after deduplication.

Batch size and concurrency are separate controls:

  • Agent-led: processes tickets and their reviewers serially, warning that reviewer parallelism is not enforced in this mode.
  • Skill → CLI handoff and direct CLI: process tickets serially by default; --item-parallelism N allows up to N processing tasks across tickets at once. Each ticket can advance from refinement to review independently—there is no batch-wide refinement barrier.
  • Reviewer concurrency: controlled separately by the profile for each review task. Concurrent tickets can each fan out reviewers, so item parallelism is not a global limit on model subprocesses.

For example, discover up to ten eligible tickets and process up to two tasks concurrently:

adlc-workflow handoff --profile rhai-feature-creator --workspace /workspace \
  --batch-size 10 --item-parallelism 2

A failed processing task prevents normal batch publication; completed artifacts remain available for inspection. See Batch selection and execution for selection rules, serial/concurrent execution diagrams, reviewer fan-out, and failure boundaries.

Intended shape

  • src/adlc_workflow/ — deterministic lifecycle, task protocol, state, and effect coordination.
  • skills/ and agents/ — agent-facing interfaces and runtime definitions.
  • plugins/ — versioned concern packages with manifests, prompts, schemas, and fixtures.
  • config/, policies/, and schemas/ — reviewed configuration and contracts.
  • adapters/ — production, local-emulator, and evaluation boundaries.
  • artifacts/ — the shared result-bundle layout.
  • scripts/ — launch and trusted validation entrypoints.
  • tests/ — contract, integration, and Fullsend acceptance-test locations.

See the proposal for the ownership, lifecycle, and acceptance requirements.

Reviewer agents

The feature profile declares five agent: reviewers. In agent-led mode, the review dispatch skill launches native Task/Agent calls serially; in controller modes, reviewer subprocesses can run concurrently according to the profile. Individual reviewers no longer use forked Skill calls. The adlc-review-plan helper supplies profile-selected paths and agent assignments. Dispatch waits for all agents to succeed and checks their output files before aggregation.

To test native reviewer concurrency without resetting Jira or running the whole workflow, run this inside the configured Claude container:

podman-compose exec claude python3 \
  /home/evaluator/.claude/plugins/adlc-workflow/scripts/test-reviewer-concurrency.py

This makes a live model call (budget cap $1) using isolated temporary fixture documents and a fixture rubric. It retains the JSONL log and asserts that all five reviewer executions start before the first completion, all succeed, and all output files exist. It tests native agent dispatch; a full workflow run is still needed to check the parent skill's orchestration and aggregation. The production config/rubrics/rhai-feature-review.yaml is a pinned, repository-local copy of the approved four-dimension strategy rubric.

Claude/Jira integration test

The end-to-end test starts the local checkouts/jctanner/jira-emulator checkout, creates RHAIRFE-1, copies this project into a temporary runtime workspace, and mounts the source at /tmp/adlc-workflow. The container copies that mount into /home/evaluator/.claude/plugins/adlc-workflow and invokes the installed plugin from /workspace.

Build the shared agent image when the launcher image is not already present:

podman build -t adlc-claude-task-runner:local -f adlc-workflow/Dockerfile.claude adlc-workflow

Then run the opt-in test with Vertex settings exported, or with the supported values available in ~/bin/claude.vertex:

export ADLC_RUN_LIVE_AGENT=1
make -C adlc-workflow integration

The test mounts Google ADC read-only and never prints its contents. Override ADLC_AGENT_IMAGE or ADLC_CLAUDE_MODEL when using a different image/model. Set ADLC_INTEGRATION_CONTROLLER=handoff to exercise the Python handoff controller through the same plugin entrypoint; its timeout is increased for the child worker subprocesses.

Podman Compose

For a repeatable two-container run, use podman-compose.yaml:

cd adlc-workflow
cp .env.example .env
$EDITOR .env
podman-compose -f podman-compose.yaml up --build --abort-on-container-exit

The Claude service stays running idle so it can be inspected interactively. In another terminal, invoke the workflow with:

podman-compose exec -T claude claude --dangerously-skip-permissions \
  --model "$ADLC_CLAUDE_MODEL" \
  -p "/adlc-workflow:adlc-workflow $ADLC_ISSUE_KEY"

The compose entrypoint registers the direct mount before the container becomes idle, so claude plugin list should show adlc-workflow@adlc-local as enabled. --plugin-dir is only needed when running Claude outside this compose stack.

The plugin mount provides /home/evaluator/.claude/plugins/adlc-workflow/scripts/run-example-workflow.sh. It seeds an example RFE, invokes the workflow for the newly returned issue key, and tees combined Claude output to /workspace/adlc-workflow-run.log:

podman-compose exec -T claude \
  /home/evaluator/.claude/plugins/adlc-workflow/scripts/run-example-workflow.sh
podman-compose exec claude tail -f /workspace/adlc-workflow-run.log

Set ADLC_CONTROLLER_MODE=handoff to run the same example through the Python controller via the outer /adlc-workflow skill. The skill delegates once to adlc-workflow handoff and reports its terminal result. Set it to cli to invoke that deterministic controller directly, without an outer Claude session. ADLC_HANDOFF_DANGEROUSLY_SKIP_PERMISSIONS=1 is the explicit local-container policy that lets child workers write their assigned runtime artifacts. The default claude value is the agent-led mode.

In cli mode, set ADLC_CONTROLLER_JSON=1 for a live normalized JSONL stream on stdout. It includes deterministic controller events and tagged raw Claude worker stream events; a private copy is retained at .adlc/state/runs/<run-id>/events.jsonl. Without it, the same events are rendered for humans.

In cli and handoff modes, batch items remain sequential by default. Set ADLC_ITEM_PARALLELISM=2 to process up to two tasks across tickets concurrently; each ticket still honors the profile's reviewer parallelism, and publication remains a barrier after all selected tickets finish.

The controller can also be invoked without an outer Claude process when the plugin install is available:

/home/evaluator/.claude/plugins/adlc-workflow/scripts/adlc-workflow handoff \
  --workspace /workspace \
  --profile rhai-feature-creator \
  --item-parallelism 2 \
  --dangerously-skip-permissions \
  RHAIRFE-1

Or open a shell inside the agent container:

podman-compose exec claude bash

From the repository root, pass the environment file explicitly:

podman-compose --env-file adlc-workflow/.env \
  -f adlc-workflow/podman-compose.yaml up --build --abort-on-container-exit

The local .env file is gitignored; .env.example is the committed template. Compose loads .env automatically for interpolation, including ANTHROPIC_VERTEX_PROJECT_ID.

The ADLC workflow skills use the checked-in commands in scripts/ for Jira fetches, request creation, task advancement, result-envelope construction, and submission. Worker skills should write only the substantive markdown; they should not generate inline Python or heredoc commands for protocol operations.

The claude service receives ADLC_JIRA_URL=http://jira-emulator:8080, ADLC_JIRA_TOKEN, JIRA_TOKEN, and the read-only ADC mount. The emulator is started in strict authentication mode by default and shares the configured token with the agent. Set JIRA_TOKEN, JIRA_USER, ADLC_ISSUE_KEY, or ADLC_CLAUDE_MODEL to override the local defaults. Set ADLC_HOST_ADC_PATH if the ADC file is not at $HOME/.config/gcloud/application_default_credentials.json.

Seed the emulator with the example RFE about an RHOAI MCP server registry:

./scripts/seed-example-rfe.sh

The helper uses http://127.0.0.1:8080 by default and applies both rfe-creator-rubric-pass and strat-creator-3.6. From inside the Claude container, its compose-provided ADLC_JIRA_URL automatically points at http://jira-emulator:8080 instead.

The compose service mounts the project directly at /home/evaluator/.claude/plugins/adlc-workflow. On startup it uses Claude's normal local-marketplace commands to register and enable the plugin. Because the marketplace entry uses a command source in link mode, Claude loads the direct mount in place rather than copying it to the versioned cache. The runtime workspace is bind-mounted from ./workspace by default; override it with ADLC_WORKSPACE_DIR when needed. The workspace bind mount contains only runtime requests, artifacts, and private state; immutable code and configuration remain in the plugin mount. Jira data remains in a named volume. Remove the Jira volume when you want a fresh Jira database:

podman-compose -f adlc-workflow/podman-compose.yaml down -v

Contributors

jctanner

Issues