Skip to content

Commit ed718cc

Browse files
durandomclaude
andcommitted
feat: add openspec CLI to code image and /fullsend skill
Image: install @fission-ai/openspec globally so agents can run openspec commands (list, change, archive) inside the sandbox. Skill: add /fullsend router with two subcommands: - validate: diffs customized harness/env files against upstream scaffold - inspect: investigates a fullsend agent run (status, timing, artifacts, logs) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6d797d4 commit ed718cc

4 files changed

Lines changed: 337 additions & 0 deletions

File tree

.claude/skills/fullsend/SKILL.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
description: "Fullsend harness validation, drift checking, sandbox debugging, and fullsend setup"
3+
---
4+
5+
# /fullsend
6+
7+
Tooling for managing fullsend sandbox configurations — validating customized harness/env files against the upstream scaffold, debugging sandbox issues, and managing fullsend setup.
8+
9+
## Essential Principles
10+
11+
1. **Customized files are full replacements.** When a repo places a file in `.fullsend/customized/harness/`, fullsend uses it *instead of* the scaffold version — not merged, not overlaid. Any field omitted from the customized file is silently dropped.
12+
2. **Upstream scaffold is source of truth.** The canonical harness and env definitions live in the scaffold repo. Customizations must track upstream changes or they drift.
13+
3. **Always diff before deploying.** Never commit a customized harness without comparing it field-by-field against the current upstream version.
14+
15+
## Setup Gates
16+
17+
**Scaffold directory** (required by `validate`):
18+
19+
1. Environment variable `FULLSEND_SCAFFOLD_DIR` (if set)
20+
2. Default relative path: `../asdlc-lab/resources/fullsend-ai/fullsend/internal/scaffold/fullsend-repo/`
21+
22+
If neither resolves to a readable directory, stop and ask the user to set `FULLSEND_SCAFFOLD_DIR` or clone `asdlc-lab`.
23+
24+
**Target repo** (used by `validate` and `inspect`):
25+
26+
1. Explicit argument (repo path or `--repo` flag)
27+
2. Default: `../rhdh-agentic`
28+
29+
## Commands
30+
31+
| Command | Description |
32+
|---------|-------------|
33+
| `validate [repo-path]` | Diff customized harness/env files against upstream scaffold |
34+
| `inspect <run-id \| #issue>` | Investigate a fullsend agent run — status, timing, output, logs |
35+
36+
## Routing
37+
38+
1. Parse the first word after `/fullsend` as the subcommand.
39+
2. If it matches a command above, read the corresponding reference file from `references/<command>.md` and follow its procedure.
40+
3. If no arguments are given, display the commands table above and ask which the user wants.
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# inspect
2+
3+
Investigate a fullsend agent run — pull together status, timing, agent output, and logs into a single report.
4+
5+
## Usage
6+
7+
```
8+
/fullsend inspect <run-id>
9+
/fullsend inspect #<issue-number>
10+
/fullsend inspect
11+
```
12+
13+
- `<run-id>`: GitHub Actions run ID (numeric)
14+
- `#<issue-number>`: find the latest fullsend run triggered by this issue
15+
- No argument: inspect the most recent fullsend run
16+
17+
Default repo: `../rhdh-agentic`. Override with `FULLSEND_REPO` env var or `--repo <owner/name>`.
18+
19+
## Procedure
20+
21+
### 1. Resolve the run
22+
23+
Determine the GitHub repo owner/name and the run ID.
24+
25+
**If run ID given** (bare number):
26+
```bash
27+
gh run view <run-id> --repo <owner/name> --json databaseId,status 2>&1
28+
```
29+
Verify it exists. If not, abort with "Run not found."
30+
31+
**If `#N` given** (issue number):
32+
```bash
33+
gh run list --repo <owner/name> --limit 10 --json databaseId,event,status,conclusion,createdAt \
34+
--jq '[.[] | select(.event == "issue_comment" or .event == "issues")] | .[0].databaseId'
35+
```
36+
Then cross-reference: fetch issue N's comments for `fullsend:agent-status:<run-id>` anchors. Use the latest matching run ID.
37+
38+
**If no argument**:
39+
```bash
40+
gh run list --repo <owner/name> --limit 5 --json databaseId,event,status,conclusion,createdAt \
41+
--jq '[.[] | select(.event == "issue_comment" or .event == "issues")] | .[0]'
42+
```
43+
Use the most recent fullsend-triggered run.
44+
45+
### 2. Gather run overview
46+
47+
```bash
48+
gh run view <run-id> --repo <owner/name> \
49+
--json databaseId,status,conclusion,event,createdAt,updatedAt,jobs,url
50+
```
51+
52+
Extract:
53+
- **Status/conclusion**: `completed/success`, `completed/failure`, `in_progress`, etc.
54+
- **Duration**: compute from `createdAt``updatedAt`
55+
- **Trigger event**: `issue_comment`, `issues`, `pull_request`
56+
- **Jobs table**: for each job, extract name, status, conclusion, duration (startedAt → completedAt). Skip jobs with conclusion `skipped` from the duration calculation.
57+
58+
Identify the **agent job** — the one that is NOT `Route` and NOT `stop-fix` and has conclusion != `skipped`. Its name (e.g., `dispatch / Code / Code`) tells you which agent ran.
59+
60+
### 3. Gather agent status comment
61+
62+
Find the triggering issue number. Strategy:
63+
1. If the user passed `#N`, use that.
64+
2. Otherwise, extract from the run's event payload — check the run's `headBranch` or use `gh api repos/<owner/name>/actions/runs/<run-id> --jq '.event'` combined with searching recent issue comments.
65+
66+
Then fetch the status comment:
67+
```bash
68+
gh api repos/<owner/name>/issues/<N>/comments \
69+
--jq '.[] | select(.body | test("fullsend:agent-status:<run-id>")) | {body, created_at}'
70+
```
71+
72+
Parse from the comment body:
73+
- Agent name and result (Success / Failure)
74+
- Commit SHA (from the `Commit:` backtick)
75+
- Timestamps
76+
77+
If no status comment found, note: "No agent status comment — the run may have failed before posting."
78+
79+
### 4. Check for PR or branch
80+
81+
If a commit SHA was found in step 3:
82+
83+
```bash
84+
# Find branches containing the commit (in the local clone)
85+
cd <repo-path> && git fetch --quiet && git branch -r --contains <sha> 2>/dev/null
86+
```
87+
88+
```bash
89+
# Check for PRs from that branch
90+
gh pr list --repo <owner/name> --state all --head <branch-name> \
91+
--json number,title,state,url --jq '.[] | "\(.number) \(.state) \(.title)"'
92+
```
93+
94+
Report:
95+
- Branch name and whether it was pushed
96+
- PR number, state (OPEN/CLOSED/MERGED), title, URL
97+
- If no PR exists despite a successful commit: flag as `⚠️ No PR created`
98+
99+
### 5. Download and summarize artifact
100+
101+
```bash
102+
gh api repos/<owner/name>/actions/runs/<run-id>/artifacts \
103+
--jq '.artifacts[] | {name, size_in_bytes, expired, archive_download_url}'
104+
```
105+
106+
If an artifact exists and `expired == false`:
107+
108+
```bash
109+
# Download to temp dir
110+
TMPDIR=$(mktemp -d)
111+
gh run download <run-id> --repo <owner/name> --name <artifact-name> --dir "$TMPDIR"
112+
```
113+
114+
**Parse `output.jsonl`** (at `$TMPDIR/iteration-1/output.jsonl` or similar):
115+
- Count total conversation turns
116+
- Count tool_use calls by tool name
117+
- Find any `error` or `failure` messages
118+
- Extract the model ID used
119+
120+
**Check sandbox logs** (at `$TMPDIR/logs/`):
121+
- `openshell-sandbox.log`: look for ERROR, FATAL, OOM, timeout
122+
- `openshell-gateway.log`: look for TLS errors, connection failures
123+
124+
Clean up: `rm -rf "$TMPDIR"` after reading.
125+
126+
If artifact expired or missing, note it and skip. If the run is still `in_progress`, artifacts won't be available yet — note this.
127+
128+
### 6. Report
129+
130+
Output a structured report:
131+
132+
```
133+
## Fullsend Run Inspection: <run-id>
134+
135+
### Overview
136+
| Field | Value |
137+
|-------|-------|
138+
| Run | [<run-id>](<url>) |
139+
| Trigger | issue_comment on #<N> |
140+
| Status | <status> / <conclusion> |
141+
| Duration | <Xm Ys> |
142+
| Agent | <agent-name> |
143+
144+
### Jobs
145+
| Job | Status | Duration |
146+
|-----|--------|----------|
147+
| Route | success | 12s |
148+
| Code | success | 2m 42s |
149+
| Review | skipped | — |
150+
| ... | ... | ... |
151+
152+
### Agent Output
153+
- Commit: `<sha>` — <commit message>
154+
- Branch: <branch-name>
155+
- PR: #<N> (<state>) / none
156+
- Status comment: ✅ Success / ❌ Failure
157+
158+
### Artifact Summary
159+
- Turns: <N> | Tool calls: <N> | Errors: <N>
160+
- Model: <model-id>
161+
- Sandbox logs: clean / ⚠️ <issue summary>
162+
163+
### Issues Found
164+
- ⚠️ <any anomalies detected>
165+
```
166+
167+
### Issue detection heuristics
168+
169+
Flag these automatically:
170+
171+
| Condition | Flag |
172+
|-----------|------|
173+
| Run succeeded but no commit SHA in status comment | ⚠️ Success with no output |
174+
| Commit exists but no PR was created | ⚠️ No PR created |
175+
| Run duration > `timeout_minutes` from harness | ⚠️ May have hit timeout |
176+
| Sandbox log contains ERROR/FATAL | ⚠️ Sandbox errors (show excerpts) |
177+
| Artifact is expired | ℹ️ Artifact expired, no transcript available |
178+
| Run is still in progress | ℹ️ Run still in progress, partial data |
179+
| Multiple agent jobs ran (not just one + Route) | ℹ️ Multiple agents dispatched |
180+
181+
If no issues found, end with: **No anomalies detected.**
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# validate
2+
3+
Diff customized harness and env files against the upstream scaffold to catch drift.
4+
5+
## Usage
6+
7+
```
8+
/fullsend validate [repo-path]
9+
```
10+
11+
- `repo-path`: path to the repo with `.fullsend/customized/` overrides. Default: `../rhdh-agentic`
12+
13+
## Procedure
14+
15+
### 1. Resolve paths
16+
17+
```
18+
SCAFFOLD_DIR = $FULLSEND_SCAFFOLD_DIR or ../asdlc-lab/resources/fullsend-ai/fullsend/internal/scaffold/fullsend-repo/
19+
TARGET_DIR = <repo-path>/.fullsend/customized/
20+
```
21+
22+
Verify both directories exist. Abort with a clear message if either is missing.
23+
24+
### 2. Validate harness files
25+
26+
For each `*.yaml` in `TARGET_DIR/harness/`:
27+
28+
1. Read the customized file.
29+
2. Read the upstream file at `SCAFFOLD_DIR/harness/<same-name>.yaml`.
30+
- If the upstream file does not exist, report it as **WARNING: no upstream counterpart** (may be a repo-specific harness).
31+
3. Run a structured diff. Classify each difference:
32+
33+
#### Error (blocks deployment)
34+
35+
- **Missing top-level field**: a field present in upstream but absent in the customized file. Because customized files are full replacements, this means the field is silently dropped at runtime.
36+
- Known critical fields: `policy`, `agent`, `pre_script`, `post_script`
37+
- **Stale paths**: any `host_files[].dest` containing `/tmp/workspace/` — this was replaced by `/sandbox/workspace/` in OpenShell 0.0.54.
38+
39+
#### Review (needs human judgement)
40+
41+
- **Changed paths**: `host_files[].dest` values that differ from upstream but are not stale (may be intentional).
42+
- **Changed values**: fields like `image`, `timeout_minutes`, `model` that differ from upstream — these are often intentional overrides but should be confirmed.
43+
- **Missing list items**: entries in upstream `host_files`, `skills`, or `plugins` arrays that are absent in the customized version.
44+
45+
#### Info (expected differences)
46+
47+
- **Intentional overrides**: fields that differ but are clearly repo-specific customizations (e.g., different `image` tag, different `timeout_minutes` for debugging).
48+
- **Added fields**: fields in the customized file that don't exist in upstream (repo-specific extensions).
49+
- **Comment differences**: comment-only changes.
50+
51+
### 3. Validate env files
52+
53+
For each file in `TARGET_DIR/env/`:
54+
55+
1. Read the customized file.
56+
2. Read the upstream file at `SCAFFOLD_DIR/env/<same-name>`.
57+
- If no upstream counterpart exists, report as **INFO: repo-specific env file**.
58+
3. Compare exported variable names:
59+
- Variables in upstream but missing from customized → **ERROR: missing variable**.
60+
- Variables in customized but not in upstream → **INFO: repo-specific variable**.
61+
4. For shared variables, flag value differences as **REVIEW** (may be intentional overrides like different `GIT_AUTHOR_NAME`).
62+
63+
### 4. Cross-check for orphaned customizations
64+
65+
List any files in `TARGET_DIR/harness/` or `TARGET_DIR/env/` that have no upstream counterpart — these may be leftover from a renamed or removed upstream file.
66+
67+
### 5. Report
68+
69+
Output a structured report grouped by severity:
70+
71+
```
72+
## Fullsend Validation: <repo-name>
73+
74+
### Errors (must fix before deploying)
75+
- harness/code.yaml: missing field `doc` (present in upstream)
76+
- harness/code.yaml: missing field `plugins` (present in upstream)
77+
78+
### Review (confirm these are intentional)
79+
- harness/code.yaml: `timeout_minutes` changed: 35 → 5
80+
- harness/code.yaml: `image` changed: ghcr.io/fullsend-ai/... → ghcr.io/redhat-developer/...
81+
82+
### Info
83+
- harness/code.yaml: comment added at line 1 (repo-specific)
84+
85+
### Summary
86+
- Files checked: 2 harness, 0 env
87+
- Errors: 2 | Review: 2 | Info: 1
88+
```
89+
90+
If there are zero errors, end with: **All customizations are consistent with upstream.**
91+
92+
## Special Checks
93+
94+
These are run regardless of whether a field is classified above:
95+
96+
| Check | Condition | Severity |
97+
|-------|-----------|----------|
98+
| Stale workspace path | Any `dest` containing `/tmp/workspace/` | ERROR |
99+
| Missing policy | `policy` field absent from harness | ERROR |
100+
| Missing agent | `agent` field absent from harness | ERROR |
101+
| Dropped plugins | `plugins` list shorter than upstream | REVIEW |
102+
| Dropped skills | `skills` list shorter than upstream | REVIEW |
103+
| host_files dest changed | `dest` differs from upstream | REVIEW |
104+
| runner_env subset | Customized `runner_env` keys are subset of upstream | REVIEW |
105+
106+
## Implementation Notes
107+
108+
- Use `diff -u` for a quick visual diff, but also do field-level YAML comparison for the structured report. Read both files and compare key-by-key.
109+
- YAML keys are unordered — don't flag reordering as drift.
110+
- When in doubt about whether a difference is intentional, classify as REVIEW, not ERROR.

images/code/Containerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@ RUN mkdir -p "$COREPACK_HOME" \
3636
&& corepack prepare yarn@stable --activate \
3737
&& yarn --version
3838

39+
# ---------------------------------------------------------------------------
40+
# openspec CLI — spec-driven development tooling.
41+
# Used by agents working on repos with openspec/ directories.
42+
RUN npm install -g @fission-ai/openspec \
43+
&& openspec --version
44+
3945
USER sandbox

0 commit comments

Comments
 (0)