Skip to content
Merged
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
12 changes: 6 additions & 6 deletions docs/ADRs/0024-harness-definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,12 @@ agents/ # Agent definitions (.md, following Claude standard)
arch-reviewer.md
docs-reviewer.md

skills/ # Skill definitions (SKILL.md, following AgentSkills standard)
triage-coordination/SKILL.md
detect-duplicates/SKILL.md
assess-completeness/SKILL.md
code-implementation/SKILL.md
testing-conventions/SKILL.md
skills/ # Skill directories (each contains SKILL.md + companion files)
triage-coordination/
detect-duplicates/
assess-completeness/
code-implementation/
testing-conventions/

env/ # Environment files delivered into the sandbox
gcp-vertex.env # May contain ${VAR} references expanded at bootstrap
Expand Down
37 changes: 26 additions & 11 deletions docs/ADRs/0038-universal-harness-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@ Extend every path field in the harness schema to support three forms:

1. **Absolute file path:** `/opt/fullsend/agents/code.md`
2. **Relative file path:** `agents/code.md` (resolved against `.fullsend` base)
3. **HTTP(S) URL:** `https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=abc123...`
3. **HTTP(S) URL:** `https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=abc123...` (single-file resources) or `https://github.com/fullsend-ai/library/tree/8cd3799.../skills/rust#sha256=<tree-hash>...` (directory resources like skills)

When the runner encounters a URL, it fetches the resource, caches it locally (content-addressed by SHA256), and validates its integrity before use. All referenced resources (skills, policies, scripts, binaries) support the same three forms, creating a uniform resolution model.

**Note on URL immutability:** Example URLs in this ADR use GitHub `raw.githubusercontent.com` URLs with commit SHAs (e.g., `8cd3799...`) to ensure immutability. Branch-based URLs like `https://github.com/fullsend-ai/library/blob/main/agents/code.md` point to mutable content—the branch advances as commits are added. For production use, always use commit-pinned URLs or rely on the mandatory `#sha256=...` integrity hash to detect changes.
**Skill directory model:** Skills are directories containing `SKILL.md` plus optional companion files (`scripts/`, `sub-agents/`, `assets/`). The entire directory tree is uploaded to the sandbox. When referenced via URL, skill URLs point to the directory (not the `SKILL.md` file) and use `github.com/.../tree/...` format. The integrity hash covers the entire directory tree (tree hash). The resolver uses forge APIs (GitHub Contents API, GitLab equivalent) to list directory contents, fetch all files, and reconstruct the directory tree in the local cache. Skills from non-forge HTTPS URLs are rejected because HTTP has no standard directory listing mechanism. Agents and policies remain single-file resources and work with any HTTPS URL.

**Note on URL immutability:** Example URLs in this ADR use GitHub commit-pinned URLs with commit SHAs (e.g., `8cd3799...`) to ensure immutability. For single-file resources (agents, policies), `raw.githubusercontent.com` URLs are used. For directory resources (skills), `github.com/.../tree/...` URLs are used. Branch-based URLs point to mutable content—the branch advances as commits are added. For production use, always use commit-pinned URLs or rely on the mandatory `#sha256=...` integrity hash to detect changes.

**Transitive closure:** A URL-referenced skill that itself references other skills via `dependencies:` in its frontmatter triggers a recursive fetch. The runner builds a complete dependency graph before sandbox creation. Skill-level `policy:` is deferred — the harness-level policy governs sandboxing, and there is no clear runtime semantic for a skill overriding or composing with the harness policy.

Expand All @@ -87,9 +89,11 @@ Like Option A, but all URLs must include an integrity hash:

```yaml
agent: https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=abc123...
skills:
- https://github.com/fullsend-ai/library/tree/8cd3799.../skills/rust#sha256=<tree-hash>...
```

The runner verifies the fetched content matches the declared hash before using it. This prevents TOCTOU attacks at the cost of requiring hash management for every remote resource.
The runner verifies the fetched content matches the declared hash before using it. For single-file resources (agents, policies), the hash covers the file content. For directory resources (skills), the hash covers the entire directory tree. This prevents TOCTOU attacks at the cost of requiring hash management for every remote resource.

**Trade-offs:**
- **Pros:** Eliminates silent substitution attacks. Makes dependency versions explicit.
Expand Down Expand Up @@ -143,6 +147,7 @@ With the hybrid approach (URL support for declarative resources, local files for
### What changes

- **Harness schema:** Declarative resource path fields (`agent`, `policy`, `skills[]`) accept URLs. Executable resource fields (`pre_script`, `post_script`) and configuration files (`host_files[].src`) must be local paths (see "Security implications" section for rationale).
- **Skill resolution model:** Skills referenced via URL point to directories, not individual `SKILL.md` files. The resolver uses forge APIs (GitHub Contents API, GitLab equivalent) to list directory contents, fetch all files, and reconstruct the directory tree in the local cache. Skills from non-forge HTTPS URLs are rejected because HTTP has no standard directory listing mechanism. Agents and policies remain single-file resources and work with any HTTPS URL.
- **Resolution logic:** The runner resolves URLs by fetching, caching (content-addressed), and validating before use.
- **Transitive closure (Phase 2 feature):** URL-referenced resources can themselves reference other resources via URL, creating a dependency tree. Phase 1 implementation limits URL references to single-level only (harness can reference URL-based resources, but those resources cannot reference additional URLs). Phase 2 adds full transitive resolution with:
- **Visited node tracking:** The resolver maintains a set of already-visited URLs. If a URL is encountered twice in the same dependency chain, the resolver returns an error indicating a circular dependency.
Expand Down Expand Up @@ -201,7 +206,9 @@ See `docs/plans/universal-harness-access.md` for detailed implementation plan. K
3. **Transitive resolver (new package `internal/resolve/`):** Build dependency graph for harnesses, recursively fetch and validate.
4. **Access policy enforcement (`internal/security/`):** Validate fetched resources against org-level and harness-level policies.
5. **Schema extension:** Add `allowed_remote_resources[]` to harness YAML.
6. **CLI flag:** `fullsend run --offline` to disable all network fetches (fail if harness references a URL).
6. **Forge interface extension (`internal/forge/`):** Add `ListDirectoryContents` and `GetFileContentAtRef` to support skill directory listing and file retrieval at specific refs.
7. **Directory cache (`internal/fetch/cache.go`):** Add `CachePutDir`, `CacheGetDir`, and `ComputeTreeHash` for caching directory trees.
8. **CLI flag:** `fullsend run --offline` to disable all network fetches (fail if harness references a URL).

### Differences from traditional package management

Expand Down Expand Up @@ -229,7 +236,7 @@ To support community sharing and provide a trusted source for harness components

Instead, the model applies **uniform security to all remote resources:**

- **All remote resources require hash pinning**, regardless of source. `https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=abc123...` and `https://example.com/my-skill.md#sha256=def456...` have the same verification requirements.
- **All remote resources require hash pinning**, regardless of source. `https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=abc123...` (single-file) and `https://github.com/fullsend-ai/library/tree/8cd3799.../skills/rust#sha256=<tree-hash>...` (directory) have the same verification requirements. For directory resources, the hash covers the entire directory tree.

- **User-controlled allowlist with sensible defaults.** Organizations configure allowed URL prefixes in `config.yaml`:
```yaml
Expand Down Expand Up @@ -281,7 +288,7 @@ The following design questions have been resolved as part of this ADR:

**Rationale:** Explicit URLs make dependencies auditable and prevent dependency confusion attacks. Version resolution requires a central registry (complexity, availability, trust) or org-level alias files (indirection that obscures actual dependencies). Full URLs are verbose but clear.

**Alternative for ergonomics:** Organizations can use shell aliases or wrapper scripts if they frequently reference the same base URLs. Example: `fullsend run $LIBRARY/harness/rust-linter.yaml#sha256=...` where `LIBRARY=https://raw.githubusercontent.com/fullsend-ai/library/8cd3799...`
**Alternative for ergonomics:** Organizations can use shell aliases or wrapper scripts if they frequently reference the same base URLs. Example: `fullsend run $LIBRARY/harness/rust-linter.yaml#sha256=...` where `LIBRARY=https://github.com/fullsend-ai/library/tree/8cd3799...`

#### 5. Offline mode

Expand Down Expand Up @@ -309,15 +316,23 @@ harnesses:
- field: agent
url: https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/rust.md
sha256: def456...
type: file
fetched_at: "2026-05-12T10:00:00Z"
- field: skills[0]
url: https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../skills/cargo-check/SKILL.md
sha256: ghi789...
url: https://github.com/fullsend-ai/library/tree/8cd3799.../skills/cargo-check
sha256: <tree-hash>...
type: directory
fetched_at: "2026-05-12T10:00:00Z"
files:
- path: SKILL.md
sha256: abc123...
- path: scripts/check.sh
sha256: def456...
transitive_deps:
- field: skills[dep0]
url: https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../policies/rust-sandbox.yaml
sha256: jkl012...
type: file
fetched_at: "2026-05-12T10:00:00Z"
```

Expand All @@ -333,9 +348,9 @@ harnesses:

**VCS-specific schemes trade-off:** Structured references like `git+https://` enable automation (dependabot-style updates that understand git semantics), make VCS coupling explicit, and provide stable API via tags/commits. However, they increase complexity (multiple URL parsers, VCS-specific logic) and reduce portability (what if a resource moves from GitHub to GitLab?).

**Future enhancement:** Phase 2/3 could add opt-in support for structured references as an alternative to bare URLs. The implementation plan would translate `github:org/repo/path@ref` to a raw.githubusercontent.com URL with commit SHA lookup, then apply the same fetch/cache/validate logic. Both URL forms would coexist.
**Future enhancement:** Phase 2/3 could add opt-in support for structured references as an alternative to bare URLs. The implementation plan would translate `github:org/repo/path@ref` to the appropriate GitHub URL with commit SHA lookup, then apply the same fetch/cache/validate logic. Both URL forms would coexist.

**Current recommendation:** Use commit-pinned raw.githubusercontent.com URLs (e.g., `https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=...`) for GitHub-hosted resources. The commit SHA in the URL path provides immutability at the URL level, and the `#sha256=...` fragment provides content integrity. This achieves the same goals as `git+https://` without requiring VCS-specific logic.
**Current recommendation:** Use commit-pinned URLs for GitHub-hosted resources. For single-file resources (agents, policies), use `raw.githubusercontent.com` URLs (e.g., `https://raw.githubusercontent.com/fullsend-ai/library/8cd3799.../agents/code.md#sha256=...`). For directory resources (skills), use `github.com/.../tree/...` URLs (e.g., `https://github.com/fullsend-ai/library/tree/8cd3799.../skills/rust#sha256=<tree-hash>...`). The commit SHA in the URL path provides immutability at the URL level, and the `#sha256=...` fragment provides content integrity.

## Related Work

Expand All @@ -349,4 +364,4 @@ The proposed model follows the GitHub Actions approach: URL-based references wit

## Implementation Plan

See `docs/plans/universal-harness-access.md` for full implementation details, security analysis, and migration path. See `docs/plans/universal-harness-access-phase1.md` for the phased PR breakdown (Phase 1 MVP) and `docs/plans/universal-harness-access-phase2.md` for Phase 2 (transitive dependency resolution).
See `docs/plans/universal-harness-access.md` for full implementation details, security analysis, and migration path. See `docs/plans/universal-harness-access-phase1.md` for the phased PR breakdown (Phase 1 MVP), `docs/plans/universal-harness-access-phase2.md` for Phase 2 (transitive dependency resolution), `docs/plans/universal-harness-access-phase3.md` for Phase 3 (lock files), and `docs/plans/universal-harness-access-phase4.md` for Phase 4 (runtime dependency loading).
2 changes: 1 addition & 1 deletion docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ See [architecture.md](architecture.md) and [#101](https://github.com/fullsend-ai

### Skill

A markdown file (optionally with a `scripts/` directory) that gives an agent context and tool authorizations for a specific task. Skills are not general "agent capabilities" — they are concrete, scoped instruction sets. A skill can declare which tools it is authorized to use; when a user or system approves the skill, they implicitly authorize those tools. Skills are assembled by the [harness](#harness) and are the primary mechanism for encoding agent behavior.
A directory containing a `SKILL.md` file and optional companion files (scripts, sub-agents, assets) that gives an agent context and tool authorizations for a specific task. Skills are not general "agent capabilities" — they are concrete, scoped instruction sets. A skill can declare which tools it is authorized to use; when a user or system approves the skill, they implicitly authorize those tools. Skills are assembled by the [harness](#harness) and are the primary mechanism for encoding agent behavior.
See [architecture.md](architecture.md) and [codebase-context.md](problems/codebase-context.md).

### Stage
Expand Down
15 changes: 8 additions & 7 deletions docs/guides/user/customizing-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,11 @@ To add a custom skill to the code agent's harness:
- skills/my-custom-validation # Your custom skill
```

3. **Add your custom skill file**:
3. **Add your custom skill directory**:
```bash
# Create your custom skill
cat > .fullsend/customized/skills/my-custom-validation.md <<'EOF'
mkdir -p .fullsend/customized/skills/my-custom-validation
cat > .fullsend/customized/skills/my-custom-validation/SKILL.md <<'EOF'
# My Custom Validation Skill

[Your skill content...]
Expand All @@ -174,7 +175,7 @@ To add a custom skill to the code agent's harness:
- Copies upstream defaults to `harness/`, `skills/`, etc.
- Copies your `customized/` files on top, **replacing** any files with matching names
- The harness loads `harness/code.yaml` (now your customized version)
- Your skill at `skills/my-custom-validation.md` is available
- Your skill at `skills/my-custom-validation/` is available

**Important:** You must maintain the full harness structure. You cannot add just a `skills:` field—the entire YAML file must be present and valid.

Expand Down Expand Up @@ -212,7 +213,7 @@ Each agent role has its own identity, permissions, and purpose:

### Adding a Custom Skill

Create `.fullsend/customized/skills/my-skill.md` in your config repo:
Create `.fullsend/customized/skills/my-skill/SKILL.md` in your config repo:

```markdown
# My Custom Skill
Expand All @@ -224,7 +225,7 @@ Custom domain knowledge for this organization.
...
```

The skill will be automatically available to all agents that include `skills/my-skill.md` in their harness configuration.
The skill will be automatically available to all agents that include `skills/my-skill/` in their harness configuration.

### Overriding an Agent Definition

Expand Down Expand Up @@ -280,7 +281,7 @@ runner_env:
REPO_DIR: "${GITHUB_WORKSPACE}/target-repo"
```

Then create your custom skill at `.fullsend/customized/skills/my-custom-linting.md`.
Then create your custom skill at `.fullsend/customized/skills/my-custom-linting/SKILL.md`.

### Per-Repo Overrides

Expand All @@ -291,7 +292,7 @@ my-repo/
├── .fullsend/
│ └── customized/
│ ├── agents/code.md # Repo-specific agent instructions
│ ├── skills/repo-skill.md # Repo-specific skill
│ ├── skills/repo-skill/ # Repo-specific skill (contains SKILL.md)
│ └── harness/code.yaml # Repo-specific harness config
```

Expand Down
Loading
Loading