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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,12 @@ evidence broadens to the full configured command or stops; it never silently
becomes a passing verification result.

Apache-2.0.

## Opsle Tasks capability package

The independently versioned [Tasks capability package](packages/tasks-capability/README.md)
ships the executable planning/capture adapter, AV runtime, and explicit schemas.
Build an installable artifact with `npm run pack:tasks-capability`. Generic trusted
discovery and operator project grants activate it; installation defaults to disabled.
AV remains independent and owns verification planning, while Tasks owns command
execution and release.
39 changes: 39 additions & 0 deletions docs/tasks-capability-compatibility.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"kind": "development-compatibility-check",
"package": "@opsle/affected-verification-tasks-capability",
"version": "0.1.0",
"artifact_sha256": "06a5184cccffb0081bedd80a2aab12f2ee574f6ee11079bd54460e5b0c7268bd",
"npm_integrity": "sha512-DdM6bJn/+TnwoA25VXHlUxAh6kw28ta0HpCWDc7tdQbv2+zcHb4cBjnTD4nx50kS2EjV5thTkY+Rhtetkg281Q==",
"tasks_revision": "e1207c5264c59e14efe9838bba3a33ba504665d2",
"tasks_capabilities_source_sha256": "d8568872a1d76e5ac78e134154683b43c7ed46c2b583eaef0e9aac3569a09f30",
"central_tasks_source_unchanged": true,
"node_version": "24.20.0",
"command": "OPSLE_TASKS_RUNTIME_ROOT=<pinned-checkout> node --test tests/tasks-capability.test.js",
"tests": { "passed": 13, "failed": 0, "skipped": 0 },
"synthetic_compatible_upgrade": {
"version": "0.1.1",
"artifact_sha256": "9a3c0425fec8b0154086213bf8821bb3f6a082b49a881e952c5dd9869b0c69f5",
"published": false
},
"lifecycle": [
"isolated npm pack and install after source export removal",
"discovery without activation",
"operator grant activation",
"planning and capture through real generic runtime",
"repository authority selection rejected",
"revoke and disable for new runtimes",
"duplicate identities rejected",
"mutated installation rejected",
"removal blocks required authority and preserves evidence",
"reinstall and explicit regrant",
"separately packed compatible upgrade and fresh-process restart",
"empty catalog forces full verification or stop through real Tasks selection"
],
"final_pipeline_checks_pending": [
"npm run verify",
"OPSLE_TASKS_RUNTIME_ROOT=<pinned-checkout> npm run verify:tasks-capability (includes existing Tasks regressions)",
"existing CI pinned gitleaks secret scan"
],
"release_authorized": false,
"historical_benchmark_artifacts_modified": false
}
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
"benchmark:av-exp-001": "./benchmark/av-exp-001/reproduce.sh",
"benchmark:av-exp-002": "./benchmark/av-exp-002/reproduce.sh",
"benchmark:av-exp-003": "./benchmark/av-exp-003/reproduce.sh",
"verify:av-exp-003": "node ./benchmark/av-exp-003/verify-results.mjs"
"verify:av-exp-003": "node ./benchmark/av-exp-003/verify-results.mjs",
"build:tasks-capability": "npm --prefix packages/tasks-capability run build",
"pack:tasks-capability": "npm pack ./packages/tasks-capability",
"verify:tasks-capability": "node tools/verify-tasks-capability.js"
},
"engines": {
"node": ">=20"
Expand Down
4 changes: 4 additions & 0 deletions packages/tasks-capability/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/adapter.js
/runtime/
/LICENSE
/*.tgz
172 changes: 172 additions & 0 deletions packages/tasks-capability/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Affected Verification capability for Opsle Tasks

`@opsle/affected-verification-tasks-capability` **0.1.0** is an independently
versioned, dependency-free capability owned and released by the public Affected
Verification repository. AV remains the verification planning authority. Tasks
owns operator grants, command execution, observed results, repair, and release.
The package neither runs catalog commands nor authorizes deployment.

The executable ESM entry point implements the generic
`opsle.capability-manifest.v1` / `opsle.capability-result.v1` contract. Both
`verification.plan` and `verification.capture` are **required deterministic
authorities**. `default_enabled` is **false**. No AV identity branches, new central
Tasks dependencies, private Tasks imports, sibling checkouts, Graphify dependency,
or structural-evidence contract are needed.

## Build and install

Build from an AV checkout with Node 20+ and npm:

```sh
npm run pack:tasks-capability
sha256sum opsle-affected-verification-tasks-capability-0.1.0.tgz
```

`npm pack` builds the adapter, includes the current AV core, runtime helpers,
JSON schemas, and licenses. Only the build reads AV source outside this package.
The installed artifact contains everything it needs. No install scripts or
network dependencies are required. Review the artifact hash and provenance
before an operator installs it outside repositories and agent-writable paths:

```sh
npm install --prefix /srv/opsle-capabilities/av-0.1.0 \
--ignore-scripts --no-audit --no-fund \
./opsle-affected-verification-tasks-capability-0.1.0.tgz
```

Set the operator-owned `OPSLE_CAPABILITY_PATH` to the installed directory:
`/srv/opsle-capabilities/av-0.1.0/node_modules/@opsle/affected-verification-tasks-capability`.
Tasks also accepts the generic `capabilityRoots` configuration. Multiple roots
use the platform path delimiter. Include the individually installed roots for
other required authorities and observers. **Replace** the bundled AV discovery
root; do not also discover Tasks' entire bundled `capabilities/` directory, which
would discover the same AV identity twice and correctly fail.

This is operator configuration, not a central runtime code change. Discovery
checks the manifest and executable path; it does not activate this package.
Grant the project capability using Task 15's operator project-grant mechanism:

```json
{"schema":"opsle.capability-grants.v1","allow":["opsle.affected-verification"]}
```

Persist this in the project's `capability_grants` / `capability_grants_json`
through the existing operator interface. Repository `.opsle/capabilities.json`
cannot enable or disable an authority, change its executable, or grant it trust.
An absent required authority blocks verification, including after a revoke.

## Compatibility and schemas

The real generic runtime compatibility target is Opsle Tasks revision
`e1207c5264c59e14efe9838bba3a33ba504665d2` (Node 24+ for its complete regression
suite). Its `src/capabilities.js` SHA-256 is
`d8568872a1d76e5ac78e134154683b43c7ed46c2b583eaef0e9aac3569a09f30`.
Tests import that unmodified runtime only as a compatibility test dependency;
the installed capability never imports Tasks source. Execution metadata comes
from generic `services.executionConfig`; project/task/attempt/execution bindings
come from the generic invocation services. All executable configuration remains
operator-owned.

| Hook | Request | Response |
| --- | --- | --- |
| `verification.plan` | `opsle.execution.verification-request.v1` | `opsle.execution.verification-analysis.v1` |
| `verification.capture` | `opsle.execution.change-capture-request.v1` | `opsle.execution.change-set.v1` |

All JSON Schemas are in `schemas/`. They include the immutable task manifest,
task request, task plan, plan v2, and retained evidence v1. Their existing schema
identities are preserved. `opsle.affected-verification.tasks-config.v2` explicitly
removes v1's configurable `repository` executable path: the resolved configuration
is empty. The generic repository configuration envelope may include only its
v2 schema identifier. Unsupported schemas, hooks, and extra request fields fail.
Structural validation supplements AV's native cross-field semantic validation;
it cannot establish completeness or provenance by itself.

The compatibility analysis envelope retains `change`, `decision`, `error`,
`evidencePath`, `inputPath`, `receiptPath`, and `record`. An `ok` capability envelope
means analysis completed, **not that verification passed**. A failed analysis has
`decision: null`, an explicit error, and `ANALYSIS_FAILED` evidence. The Tasks
consumer must use full configured verification or stop. Missing manifests use
only the operator's configured full command. Empty catalogs return an explicit
analysis error, so the compatible Tasks runtime cannot enter its legacy
`NO_AUTOMATED_VERIFICATION` completion path: it must run the full configured
command or stop. Capture/transport failures throw and block the required
hook. Unsupported/unsafe requests throw before staging or writing evidence.

Planning stages the retained worktree and binds binary diff, exact base commit,
and staged Git tree identities. The manifest is read from the immutable base,
not agent-modified content. Every action must match the exact immutable catalog
partition and command. The adapter validates canonical decision identity and
recomputes the AV decision to check provenance/completeness. Unknown, incomplete,
or opaque evidence cannot justify an unexplained skip. Tasks must compare the
post-verification capture tree with the planned tree before accepting changes;
the package never decides that command execution passed.

SSH target validation, argv quoting, strict host-key checking, connection and
remote process deadlines, bounded Git output, and private evidence are retained
from the compatibility adapter. Local project execution exists only under
`NODE_ENV=test`; production requires the configured SSH target. Runtime and schema
hashes are embedded in the entry point and checked before loading and before each
invocation, extending the generic runtime's manifest/entry-point byte checks to
all packaged files that affect planning. Operator-protected installation roots
remain the trust boundary; hashes do not make writable installations trustworthy.

OBSERVE/SHADOW observations and historical benchmarks retain their existing
limits. No observation is promoted to execution authority or production trust.
External structural evidence is not accepted by this interface and cannot narrow
verification; only AV's own provenance and completeness decision can justify
selection. The core remains separately usable without Tasks.

## Revoke, remove, reinstall, and upgrade

Remove this identity from the operator project grant to disable it for subsequent
execution runtimes. Quiesce/drain active executions and restart Tasks when changing
grants or installations: a runtime holds an execution-scoped grant snapshot.
Repository selection cannot revoke authority. Keep retained logs and capability
events outside the installation; never delete historical evidence on uninstall.

After revocation and draining, remove this package's discovery root and uninstall
its npm package. Missing required authority blocks new verification. To reinstall,
install a reviewed artifact in a fresh version directory, restore its discovery
root, restart, and explicitly regrant the project. Installation alone does not
restore a revoked grant.

For a compatible upgrade, build/review the new independently versioned artifact,
install into a new version directory, drain executions, atomically replace the
operator discovery-root configuration, and restart. Never discover both versions
with the same identity. Never mutate an active installation in place. Grants for
the same identity persist until explicitly revoked. Review compatibility before
retaining them. Rollback uses the previous reviewed artifact and the same restart
procedure. An incompatible contract requires a new explicit schema version.

## Verification and release

Public AV CI runs `npm run verify`, including standalone package/schema conformance
and negative tests, and the existing pinned gitleaks secret-scan job. Core sources
and historical benchmark artifacts are not rewritten by packaging.

The operator release pipeline must provision the trusted Tasks checkout at the
recorded revision, then run:

```sh
npm run verify
OPSLE_TASKS_RUNTIME_ROOT=/path/to/pinned/opsle-tasks npm run verify:tasks-capability
npm run pack:tasks-capability
sha256sum opsle-affected-verification-tasks-capability-0.1.0.tgz
```

The second command is mandatory for release: it refuses to skip if Tasks is
unavailable or at an unreviewed revision. It exercises isolated npm installation,
discovery without activation, grant, both hooks, repository authority protection,
revoke/disable, duplicate identities, byte mutation, removal with retained history,
reinstall/regrant, and compatible patch replacement/restart. It also runs Tasks'
existing capability, adapter, history, and AV regressions. The standalone suite
reports an explicit skip for the external lifecycle check when no Tasks checkout
is provisioned; that is not release evidence.

Keep pipeline output recording package version, artifact SHA-256/npm integrity,
Tasks revision/runtime hash, unchanged central source, conformance results, and
secret-scan results with the release. Increment this package's `package.json` and
`opsle-capability.json` together; AV core and capability versions have independent
release schedules. Update the pinned compatibility revision only after contract
review and passing tests. Publish only the reviewed npm tarball after these gates;
this task does not publish or deploy it.
24 changes: 24 additions & 0 deletions packages/tasks-capability/THIRD_PARTY_NOTICES
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
The execution and verification compatibility helpers are adapted from Opsle Tasks
revision e1207c5264c59e14efe9838bba3a33ba504665d2.

MIT License

Copyright (c) 2026 Opsle

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
53 changes: 53 additions & 0 deletions packages/tasks-capability/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { cpSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = dirname(fileURLToPath(import.meta.url));
const runtime = resolve(root, 'runtime');
const metadata = JSON.parse(readFileSync(resolve(root, 'package.json')));
const manifest = JSON.parse(readFileSync(resolve(root, 'opsle-capability.json')));
if (metadata.version !== manifest.version || manifest.default_enabled !== false) {
throw new Error('Package/manifest versions must agree and installation must default to disabled.');
}
rmSync(runtime, { recursive: true, force: true });
mkdirSync(runtime, { recursive: true });
cpSync(resolve(root, 'src'), runtime, { recursive: true });
cpSync(resolve(root, '../../src'), resolve(runtime, 'core'), { recursive: true });
cpSync(resolve(root, '../../schemas/plan-v2.schema.json'), resolve(root, 'schemas/plan-v2.schema.json'));
cpSync(resolve(root, '../../LICENSE'), resolve(root, 'LICENSE'));
const hashes = {};
function collect(directory) {
for (const entry of readdirSync(resolve(root, directory), { withFileTypes: true }).sort((a,b) => a.name.localeCompare(b.name))) {
const path = `${directory}/${entry.name}`;
if (entry.isDirectory()) collect(path);
else hashes[path] = createHash('sha256').update(readFileSync(resolve(root, path))).digest('hex');
}
}
collect('runtime'); collect('schemas');
const identity = `sha256:${createHash('sha256').update(JSON.stringify(hashes)).digest('hex')}`;
writeFileSync(resolve(root, 'adapter.js'), `// Generated by build.mjs. Runtime bytes are pinned to this trusted entry point.
import { readFileSync, realpathSync, lstatSync } from 'node:fs';
import { createHash } from 'node:crypto';
import { fileURLToPath } from 'node:url';
const hashes = ${JSON.stringify(hashes, null, 2)};
const packageIdentity = ${JSON.stringify(identity)};
function integrity() {
const root = realpathSync(fileURLToPath(new URL('.', import.meta.url)));
for (const [path, expected] of Object.entries(hashes)) {
const file = fileURLToPath(new URL(path, import.meta.url));
if (lstatSync(file).isSymbolicLink() || !realpathSync(file).startsWith(root + '/')
|| createHash('sha256').update(readFileSync(file)).digest('hex') !== expected) {
throw new Error('Capability installation changed: ' + path);
}
}
}
export async function createCapability(context) {
integrity();
const { createAdapter } = await import('./runtime/adapter.js');
const adapter = createAdapter(context, packageIdentity);
return {
health() { integrity(); return { available: true, detail: null }; },
invoke(hook, payload) { integrity(); return adapter.invoke(hook, payload); },
};
}
`);
28 changes: 28 additions & 0 deletions packages/tasks-capability/opsle-capability.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"schema": "opsle.capability-manifest.v1",
"id": "opsle.affected-verification",
"name": "Affected Verification",
"version": "0.1.0",
"adapter": "adapter.js",
"default_enabled": false,
"configuration_schema": "opsle.affected-verification.tasks-config.v2",
"configuration": {},
"hooks": [
{
"name": "verification.plan",
"input_schema": "opsle.execution.verification-request.v1",
"output_schema": "opsle.execution.verification-analysis.v1",
"role": "authority",
"execution": "deterministic",
"failure": "required"
},
{
"name": "verification.capture",
"input_schema": "opsle.execution.change-capture-request.v1",
"output_schema": "opsle.execution.change-set.v1",
"role": "authority",
"execution": "deterministic",
"failure": "required"
}
]
}
24 changes: 24 additions & 0 deletions packages/tasks-capability/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@opsle/affected-verification-tasks-capability",
"version": "0.1.0",
"description": "Independent Affected Verification authority for the generic Opsle Tasks capability contract",
"type": "module",
"license": "Apache-2.0",
"engines": {
"node": ">=20"
},
"exports": "./adapter.js",
"files": [
"adapter.js",
"runtime/",
"schemas/",
"opsle-capability.json",
"LICENSE",
"README.md",
"THIRD_PARTY_NOTICES"
],
"scripts": {
"build": "node build.mjs",
"prepack": "npm run build"
}
}
Loading
Loading