# BDD Acceptance Pack (Cucumber/Gherkin)
Applies to the FunctionGemma + AgentFS + Agent Skills runtime.
## 1. Test philosophy
These are black-box acceptance tests. They treat the model as a component and verify observable behavior: tool-call validation, sandbox isolation, and auditability.
## 2. Golden rules
- Write scenarios in business language; keep implementation detail out of Gherkin.
- One scenario = one behavior. Avoid long scripts.
- Assert on artifacts: RunTrace JSON, AgentFS audit log, and file diffs.
- Tag aggressively for fast suites (@smoke) vs full suites.
## 3. Feature inventory (included)
| File | Covers |
| :--- | :--- |
| **skill_discovery.feature** | Skill selection and minimal loading |
| **tool_validation.feature** | Unknown tools and schema validation |
| **agentfs_isolation.feature** | Copy-on-write isolation and audit log presence |
| **guard_tristate.feature** | Tri-state guards and failure behavior |
## 4. How to implement steps
Prefer calling the agent runtime via a CLI to avoid tight coupling. After each scenario, collect artifacts into a temporary directory and assert on them.
**Example CLI contract (suggested)**
```bash
agent run --request "<text>" --workspace "<path>" --artifact-dir "<path>"
```
**Expected outputs in `artifact-dir/`**
- `runtrace.json`
- `agentfs-session.db`
- `diff.patch` (optional)
- `cucumber-report.json` (from the test runner)
## 5. Determinism notes
- Pin model tag (e.g., functiongemma:latest) per test run.
- Pin skill versions (commit hash) and fail if mismatched.
- Use low temperature for tool selection to reduce variance.
## Appendix: Example step template
```typescript
import { Given, When, Then } from '@cucumber/cucumber';
import assert from 'node:assert/strict';
// This file is intentionally a template.
// Implement these steps by calling your runtime as a black box (CLI) and asserting on artifacts (RunTrace, AgentFS audit).
Given('a skills registry containing the skills:', function (dataTable) {
this.skills = dataTable.hashes();
});
Given('the user request is {string}', function (req) {
this.request = req;
});
When('the agent plans with FunctionGemma', async function () {
// TODO: call your planner (ModelGateway) with this.request and this.skills
// Save outputs on the World (this)
this.plan = { selectedSkills: [], guardDecision: 'abstain', explanation: '' };
});
Then('the selected skills should include:', function (dataTable) {
const required = dataTable.hashes().map(r => r.name);
for (const name of required) {
assert(this.plan.selectedSkills.includes(name), `missing skill: ${name}`);
}
});
Then('no other skills should be loaded', function () {
// TODO: compare selectedSkills set to expected set
});
Then('the guard decision should be {string}', function (decision) {
assert.equal(this.plan.guardDecision, decision);
});
```