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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,15 @@ output-parent binding, and returns one out-of-band completion digest.

## Verification

See the [operational readiness guide](docs/readiness.md) for first-run setup and
remaining evidence gaps, and the
[suite Conductor guide](https://herdr.structupath.ai/docs/conductor/)
for cross-project documentation.

```bash
npm run check
npm run test:stage2
bash -n scripts/*.sh
npm run check:shell
shellcheck --shell=bash scripts/*.sh
python3 -m py_compile scripts/harness-fs-helper.py
go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7
Expand Down
21 changes: 14 additions & 7 deletions docs/herdr-plugins-cheatsheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

The canonical cross-plugin guide lives in the
[`herdr-suite-site` documentation](https://github.com/StructuPath/herdr-suite-site/tree/main/docs-src).
That separate site is not changed or promoted by Conductor Stage 2.
The suite guide summarizes the plugins; this repository defines Conductor's
runtime contract.

For Conductor `0.4.0`, use this repository's [README](../README.md) as the
operational authority. Its bounded support is exactly Herdr `0.7.5`, protocol
Expand All @@ -19,11 +20,17 @@ Key boundaries:
- reviewer/validator sources are distinct exact-integration-SHA snapshots with
separate writable outboxes and empty artifacts/source outputs;
- stand-down closes an exact deterministic pane prefix and retains all product
resources; and
- same-UID races, final-check/pane-close TOCTOU, cumulative retention, SHA-1-width
support, and no ambiguous recovery remain explicit.
resources;
- same-UID races, final-check/pane-close TOCTOU, cumulative retention, and
SHA-1-width support remain explicit;
- configuration v3 optionally names one existing local apply target ref;
- preview binds the exact integration and gates, an operator records the
approval receipt, and apply consumes it before any single-ref fast-forward;
- only an uncertain Stage 3 apply publication can be resolved by exact target
re-observation; ambiguous pane/agent operations still refuse recovery.

Historical designs under [`docs/history/`](history/) and retained Stage 1 evidence
are lineage records, not current operational guidance. Stage 2 does not add
preview, approval/apply, adapters, unattended launch, product cleanup, Browser or
site promotion, push, tag, or release automation.
are lineage records, not current operational guidance. Conductor does not add
suite adapters, unattended launch, product cleanup, Browser promotion, push,
tag, or release automation. See [operational readiness](readiness.md) for setup,
validation, and the remaining evidence gaps.
56 changes: 56 additions & 0 deletions docs/readiness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Conductor operational readiness

Conductor 0.4.0 supports attended task/report coordination and single-ref local
apply on exactly Herdr 0.7.5 (protocol 17, API schema 1). The manifest minimum
does not imply compatibility with newer Herdr releases. Seven installed actions
share one runtime; there is no compiled bundle or dependency installation step.

## Prepare a first run

1. Check `node --version`, `python3 --version`, `git --version`, and
`herdr --version` against the [README requirements](../README.md#supported-contract).
2. Run `npm run check` in this checkout. It validates scripts, the manifest,
documentation, historical evidence, and automated behavior. Shell validation
checks each entrypoint individually without executing it.
3. Follow the [configuration contract](../README.md#configuration) in the
intended invoking repository. Commit the configuration before assembly.
Start with configuration v2 or v3 with `"apply": null` when local apply is
not required. Choose explicit producer ownership and required commands.
4. Invoke the installed actions from the intended Herdr workspace. Inspect the
returned task/source/outbox paths and give workers their exact task-bound
publisher command. Every transition is attended.
5. Collect reports with harvest and inspect the exact integrated result. If
applying, use preview's exact approval command and receipt contract before
invoking apply. The target must be an existing local branch at the integration
base and not checked out in any worktree.
6. Stand down after inspection. Archive retains all product resources; plan
capacity for worktrees, branches, reports, and logs.

## Validation and remaining work

`npm run check` is the routine build/validation equivalent for this script-based
plugin. Before review also run `npm run test:stage2` (includes Stage 3),
`npm run check:shellcheck`, and `npm run check:workflow`. The latter validates
workflow syntax; it does not change CI configuration.

Automated tests use isolated Git repositories and a fake Herdr executable,
including real subprocess crashes, lock contention, rejected reports, gate
snapshots, and apply consumption/re-observation. Passing them is not a new live
installation or release attestation. Retained Stage 1/2 evidence remains bound
to its historical source commits. The opt-in live evidence workflow requires
the independent exact-source review described in [CONTRIBUTING](../CONTRIBUTING.md).

Priorities for subsequent work:

- Obtain candidate-specific live evidence for Stage 3 before making broader
deployment claims; preserve the separate independent review requirement.
- Test additional Herdr versions explicitly before widening the exact runtime
contract or the website's compatibility claims.
- Improve operator guidance for retained resources and uncertain pane/agent
operations without introducing automatic deletion or inferred recovery.
- Evaluate suite adapters as a separate design. Conductor currently does not
invoke Swarm, and Guard observations do not grant apply approval.

The [security boundaries](../SECURITY.md) continue to apply: coordination and
receipts are cooperative same-UID records, worker reports are assertions, and
only the Stage 3 apply publication has bounded uncertainty resolution.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"scripts": {
"test": "node --test",
"test:stage2": "node --test tests/task-report-schema.test.mjs tests/report-publisher.test.mjs tests/stage2-*.test.mjs tests/stage3-*.test.mjs tests/state-kernel.test.mjs tests/stage1-runtime-*.test.mjs",
"check:shell": "bash -n scripts/*.sh",
"check:shell": "for script in scripts/*.sh; do bash -n \"$script\" || exit; done",
"check:shellcheck": "shellcheck --shell=bash scripts/*.sh",
"check:python": "python3 -m py_compile scripts/harness-fs-helper.py",
"check:workflow": "go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7",
Expand Down
39 changes: 39 additions & 0 deletions tests/shell-validation.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import {
mkdtempSync,
mkdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from "node:test";

const command = JSON.parse(
readFileSync(new URL("../package.json", import.meta.url), "utf8"),
).scripts["check:shell"];

test("shell validation checks later scripts, handles spaces, and never executes them", () => {
const root = mkdtempSync(join(tmpdir(), "conductor-shell-check-"));
try {
mkdirSync(join(root, "scripts"));
writeFileSync(join(root, "scripts", "a.sh"), "exit 42\n");
writeFileSync(join(root, "scripts", "z later.sh"), "if then\n");
const invalid = spawnSync("/bin/sh", ["-c", command], {
cwd: root,
encoding: "utf8",
});
assert.notEqual(invalid.status, 0);
assert.match(invalid.stderr, /z later\.sh/);
writeFileSync(join(root, "scripts", "z later.sh"), "exit 43\n");
const valid = spawnSync("/bin/sh", ["-c", command], {
cwd: root,
encoding: "utf8",
});
assert.equal(valid.status, 0, valid.stderr);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
9 changes: 9 additions & 0 deletions tests/stage1-runtime-reconcile.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ test("report-first reconcile and stand-down retain Git and product inventory", a
"refs/heads",
);
const reconciled = await reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: fixture.fake.exec,
Expand Down Expand Up @@ -177,6 +178,7 @@ test("source, target, journal, registration, and lock failures perform zero CAS"
const before = updateRefCount(fixture);
const invocation = () =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: reconcileExec,
Expand All @@ -199,6 +201,7 @@ test("source and target races immediately before CAS perform zero CAS", async ()
let injected = false;
await assert.rejects(() =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: fixture.fake.exec,
Expand Down Expand Up @@ -229,6 +232,7 @@ test("post-CAS ref, tracked-worktree, and staged-index drift never publish recon
await assert.rejects(
() =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: fixture.fake.exec,
Expand Down Expand Up @@ -273,6 +277,7 @@ test("post-CAS ref, tracked-worktree, and staged-index drift never publish recon
const attempts = updateRefCount(fixture);
await assert.rejects(() =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: fixture.fake.exec,
Expand All @@ -296,6 +301,7 @@ test("post-CAS synchronization checkpoints preserve concurrent tracked edits and
await assert.rejects(
() =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
exec: fixture.fake.exec,
fault(name) {
Expand Down Expand Up @@ -349,6 +355,7 @@ test("CAS failure cannot synchronize the target worktree", async () => {
await assert.rejects(
() =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: racingExec,
Expand All @@ -373,6 +380,7 @@ test("cross-scope callers and conflicting complete producer sets perform zero CA
const before = updateRefCount(isolated);
await assert.rejects(() =>
reconcile({
herdrBin: "fake",
contextJson: context(repository, workspace),
stateRoot: isolated.stateRoot,
exec: isolated.fake.exec,
Expand Down Expand Up @@ -404,6 +412,7 @@ test("cross-scope callers and conflicting complete producer sets perform zero CA
}
await assert.rejects(() =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: fixture.fake.exec,
Expand Down
1 change: 1 addition & 0 deletions tests/stage1-runtime-stand-down-archive.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,7 @@ test("pre-intent integration failure is retryable while failed pane close is nev
operation === "merge"
? () =>
reconcile({
herdrBin: "fake",
contextJson: context(fixture.repository, fixture.workspace),
stateRoot: fixture.stateRoot,
exec: failingExec,
Expand Down
Loading