runward 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/dist/commands/characterize.js +2 -2
- package/dist/commands/check.js +2 -2
- package/dist/commands/compliance.js +2 -2
- package/dist/commands/doctor.js +2 -1
- package/dist/commands/init.js +5 -0
- package/dist/lib/compliance.js +11 -3
- package/dist/lib/conformance.js +1 -0
- package/dist/lib/constants.js +7 -4
- package/dist/lib/evidence.js +25 -4
- package/dist/lib/mission.js +4 -1
- package/dist/lib/paths.js +1 -0
- package/dist/lib/styles.js +13 -0
- package/dist/lib/tools.js +15 -0
- package/examples/request-triage/AGENTS.md +33 -0
- package/examples/request-triage/runward/handover.md +47 -0
- package/package.json +2 -1
- package/regimes/eu-ai-act@2024-1689.json +3 -3
- package/templates/adapters/README.md +8 -0
- package/templates/adapters/bmad-review-layer.toml +32 -0
- package/templates/adapters/kiro-hooks.json +12 -0
- package/templates/mission/handover.md +52 -0
- package/templates/rules/handover-agents-charter-final.md +45 -0
- package/templates/rules/handover-redone-task-proof.md +46 -0
- package/templates/rules/handover-runbook-executable.md +50 -0
- package/templates/rules/handover-succession-named.md +46 -0
- package/templates/targets/AGENTS.md +1 -0
- package/templates/workflows/handover.md +7 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Spec-driven frameworks answered the first half of that problem: write the right
|
|
|
22
22
|
|
|
23
23
|
Agentic systems break four assumptions of classical distributed engineering. The core component is **non-deterministic**: same input, different output, by design. **Input is indistinguishable from instruction**: anything the model reads can try to command it, so prompt injection is structural, not a bug to patch. **Forgetting becomes an engineering problem**: memory that only grows drowns the signal, so decay, invalidation and consolidation have to be designed, not hoped for. And the **blast radius is unprecedented**: an agent with tools acts on the world, so a bad output is no longer just a bad answer.
|
|
24
24
|
|
|
25
|
-
Runward is the **FDE method** made executable. Its guiding principle: the architecture frames the model, never the other way around — the model and the infrastructure are adapter decisions, taken behind stable contracts. From there, five architecture gestures: boundaries before the stack; start simple, isolate by contract, grow on evidence; keep the deterministic out of the model; explicit state and governed memory; govern, trace and evaluate from day zero. And a delivery spine: six gated phases (a gate: a checkpoint you cross on evidence, never on assertion), each with a Definition of Ready and a Definition of Done, a 22-arbitration decision matrix, and
|
|
25
|
+
Runward is the **FDE method** made executable. Its guiding principle: the architecture frames the model, never the other way around — the model and the infrastructure are adapter decisions, taken behind stable contracts. From there, five architecture gestures: boundaries before the stack; start simple, isolate by contract, grow on evidence; keep the deterministic out of the model; explicit state and governed memory; govern, trace and evaluate from day zero. And a delivery spine: six gated phases (a gate: a checkpoint you cross on evidence, never on assertion), each with a Definition of Ready and a Definition of Done, a 22-arbitration decision matrix, and 64 craft rules. The phases, gestures and rules are what carry it.
|
|
26
26
|
|
|
27
27
|
## Who it's for — and when
|
|
28
28
|
|
|
@@ -63,14 +63,14 @@ New here? Follow [your first mission in 15 minutes](docs/first-mission.md).
|
|
|
63
63
|
| `runward check` | **Gate audit**: which deliverable, expected at which phase, is missing, started, or filled — exit code 1 on gaps |
|
|
64
64
|
| `runward check --strict` | **Conformance gate**: verifies each phase's rule-conformance manifest — every CRITICAL/HIGH craft rule mapped to the phase is accounted for (applied with evidence, deviated with an ADR, or reasoned `n/a`) — **and verifies the evidence itself**: typed pointers (`file:PATH[:LINE][#SYMBOL]`, `test:PATH[::NAME]`, `adr:NNNN`) must resolve to real, non-empty content; a signed rule's evidence must match the rule's `signature:`; a stale pointer (drift) fails the gate. **Deterministic and zero-LLM**: it proves a decision was traced and points at something real, never the code's quality, and cannot be jailbroken by injection — rerun it and get the same verdict |
|
|
65
65
|
| `runward check --freeze` | **Seal a green gate**: hashes every resolvable evidence file into `runward/evidence-lock.json` (SHA-256, committed). A sealed file that later changes or disappears fails `check --strict` until you re-verify and re-seal — silent post-merge erosion becomes loud. Refuses to seal a red gate |
|
|
66
|
-
| `runward manifest` | **Conformance-table plumbing**: overview of the
|
|
66
|
+
| `runward manifest` | **Conformance-table plumbing**: overview of the five gated manifests; `--sync` scaffolds the *form* — missing rows appended with an empty status (the gate refuses them until you decide), renamed rules migrated in place — and never touches the *content*: no status set, no evidence written, no row deleted |
|
|
67
67
|
| `runward rules` | **The rule set as data**: the effective set (your mission's copy, else the package's) with impact, phases, OWASP ASI and signatures; `--json` is a stable, versioned machine contract for dashboards, editor extensions and CI annotations |
|
|
68
68
|
| `runward explain <rule>` | **The why, inline**: a rule's contract (impact, phases, ASI, signature, rationale) and its full text — no doctrine excavation; renamed slugs answer with their migration |
|
|
69
69
|
| `runward status` | Mission snapshot: current gate, decision journal (ADRs), workflows |
|
|
70
70
|
| `runward doctor` | Environment and installation checks |
|
|
71
71
|
| `runward update` | Refresh `runward/workflows/` from the package — mission state never touched, local edits preserved unless `--force` |
|
|
72
72
|
| `runward characterize` | **Read-only inventory** of an existing codebase (brownfield / retro-documentation) → `runward/characterization.md`: dependencies and lockfiles, entrypoints, CI, tests, git-log shape. Deterministic and zero-LLM; it parses artifacts at rest and never runs, builds, or writes to your code. Facts, not decisions — reconstructing the *why* stays yours |
|
|
73
|
-
| `runward compliance <regime>` | **Assemble a regime-framed evidence pack** (`iso-42001`, `nist-ai-rmf`, `eu-ai-act`) from the mission's artifacts → `runward/compliance/…`: a human readiness *draft* (OWASP ASI coverage, rule-conformance status, ADR journal, with the operator-only sections flagged) **and a machine-readable OSCAL** component-definition to feed your GRC/auditor tool. Deterministic, read-only, zero-LLM — supporting evidence, never a compliance claim. The regime mapping is **versioned data** (`--regime-version` pins the lens; the pack stamps which version produced it) |
|
|
73
|
+
| `runward compliance <regime>` | **Assemble a regime-framed evidence pack** (`iso-42001`, `nist-ai-rmf`, `eu-ai-act`) from the mission's artifacts → `runward/compliance/…`: a human readiness *draft* (OWASP ASI coverage, rule-conformance status, ADR journal, with the operator-only sections flagged) **and a machine-readable OSCAL** component-definition to feed your GRC/auditor tool. Deterministic, read-only, zero-LLM — supporting evidence, never a compliance claim. The regime mapping is **versioned data** (`--regime-version` pins the lens; the pack stamps which version produced it). The mapping itself is a published, citable mini-spec: [docs/spec/runward-oscal-mapping.md](docs/spec/runward-oscal-mapping.md) |
|
|
74
74
|
|
|
75
75
|
Global flags: `--yes`, `--dry-run`, `--verbose`, `--no-color`. Exit codes: 0 success, 1 gaps/warnings, 2 missing prerequisite.
|
|
76
76
|
|
|
@@ -85,19 +85,21 @@ your-project/
|
|
|
85
85
|
│ ├── framing.md # problem, value, observable success criterion, floor vs target
|
|
86
86
|
│ ├── mission-contract.md # one-page steering contract: engagements, DoD, gates
|
|
87
87
|
│ ├── architecture.md # boundaries, ports, integration protocol — stack stays open
|
|
88
|
+
│ ├── execution-topology.md # per-port placement: location family, data class, sovereignty (gated)
|
|
88
89
|
│ ├── decision-matrix.md # 22 arbitrations: one sober default + one explicit trigger each
|
|
89
90
|
│ ├── reference-stack.md # default adapter kit per layer, with evolution triggers
|
|
90
91
|
│ ├── shared-bricks.md # bricks beyond the app: placement families, brick matrix, sovereignty by data class
|
|
91
92
|
│ ├── floor.md # the smallest system that proves value on real traffic
|
|
93
|
+
│ ├── handover.md # the succession, proven: kit index, redone-task proof, named owner
|
|
92
94
|
│ ├── adr/ # one ADR per structural decision, with re-evaluation trigger
|
|
93
|
-
│ ├── rules/ #
|
|
95
|
+
│ ├── rules/ # 64 craft rules your agent applies while building
|
|
94
96
|
│ ├── governance/
|
|
95
97
|
│ │ ├── threat-model.md # lethal trifecta, 2-of-3 rule on the context window
|
|
96
98
|
│ │ ├── evaluation-rubric.md # test the deterministic, evaluate the non-deterministic
|
|
97
99
|
│ │ └── observability-schema.md
|
|
98
100
|
│ ├── contracts/ # port contracts (versioned, additive, tolerant reader)
|
|
99
101
|
│ ├── runbook.md # recovery runbook for the team that inherits the system
|
|
100
|
-
│ ├── adapters/ # inert samples that run the gate at each harness seam (git, GitHub/GitLab CI, Claude Code)
|
|
102
|
+
│ ├── adapters/ # inert samples that run the gate at each harness seam (git, GitHub/GitLab CI, Claude Code, Kiro, BMAD)
|
|
101
103
|
│ └── workflows/ # the method, executable by your agent
|
|
102
104
|
└── .claude/ | .cursor/ # tool profiles (--tools)
|
|
103
105
|
```
|
|
@@ -145,7 +147,7 @@ Read the doctrine: [Concevoir et exécuter des systèmes agentiques (FR)](https:
|
|
|
145
147
|
|
|
146
148
|
Two vendor-neutral files are **always written**, so the method works on any agent with no profile at all: `AGENTS.md` (the standard charter read by Codex, Cursor, Copilot, Windsurf, Cline, Zed, Amp, opencode, Junie, Warp and more), and the **phase skills** at `.agents/skills/runward-<phase>/SKILL.md` — the converged `SKILL.md` seam read by 14+ harnesses, which surface each build phase's craft rules *by relevance* at the point of action (subordinate to the gate: a skill loaded but not applied still fails `check --strict`).
|
|
147
149
|
|
|
148
|
-
Tool profiles (`--tools`) add harness-specific wiring on top: **Claude Code** (`/rw-*` commands + `.claude/skills/`), **Cursor**, **GitHub Copilot**, **Gemini CLI**, **Windsurf** (charter files), **Continue.dev** (`.continue/rules/`), **JetBrains Junie** and **Trae** (`.<harness>/skills/`). The mission structure itself is plain markdown in your repo. More profiles welcome (see [ROADMAP.md](ROADMAP.md)).
|
|
150
|
+
Tool profiles (`--tools`) add harness-specific wiring on top: **Claude Code** (`/rw-*` commands + `.claude/skills/`), **Cursor**, **GitHub Copilot**, **Gemini CLI**, **Windsurf** (charter files), **Continue.dev** (`.continue/rules/`), **JetBrains Junie** and **Trae** (`.<harness>/skills/`), and **Kiro** (`.kiro/steering/`, relevance-loaded). The mission structure itself is plain markdown in your repo. More profiles welcome (see [ROADMAP.md](ROADMAP.md)).
|
|
149
151
|
|
|
150
152
|
## License
|
|
151
153
|
|
|
@@ -2,7 +2,7 @@ import { existsSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { buildInventory, renderCharacterization, mineDrafts, renderDraft } from "../lib/characterize.js";
|
|
4
4
|
import { makeWriter } from "../lib/write.js";
|
|
5
|
-
import { c, createHeader,
|
|
5
|
+
import { c, createHeader, generationDate, section, status } from "../lib/styles.js";
|
|
6
6
|
import { VERSION } from "../lib/paths.js";
|
|
7
7
|
/**
|
|
8
8
|
* Characterize an existing codebase (ADR-0014) — read-only, deterministic, zero-LLM.
|
|
@@ -22,7 +22,7 @@ export async function characterizeCommand(opts) {
|
|
|
22
22
|
console.log(section("Reading (read-only)"));
|
|
23
23
|
const inv = buildInventory(root);
|
|
24
24
|
const dryRun = process.env.RUNWARD_DRY_RUN === "1";
|
|
25
|
-
const generatedAt =
|
|
25
|
+
const generatedAt = generationDate();
|
|
26
26
|
const md = renderCharacterization(inv, generatedAt);
|
|
27
27
|
// Generated artifact, not mission state: always refresh it (idempotent).
|
|
28
28
|
const w = makeWriter({ force: true, dryRun, root });
|
package/dist/commands/check.js
CHANGED
|
@@ -6,7 +6,7 @@ import { evidenceReport, verifyEvidenceLock, renderEvidenceLock, EVIDENCE_LOCK }
|
|
|
6
6
|
import { behavioralProof } from "../lib/behavioral-proof.js";
|
|
7
7
|
import { verifyFindings, VERIFY_FINDINGS } from "../lib/verify-findings.js";
|
|
8
8
|
import { runHooks } from "../lib/hooks.js";
|
|
9
|
-
import { c, createHeader,
|
|
9
|
+
import { c, createHeader, generationDate, section, status } from "../lib/styles.js";
|
|
10
10
|
import { VERSION } from "../lib/paths.js";
|
|
11
11
|
/**
|
|
12
12
|
* Gate audit — the gap analysis: which deliverable, expected at which
|
|
@@ -195,7 +195,7 @@ export async function checkCommand(opts) {
|
|
|
195
195
|
console.log(" " + status.error("refusing to seal a red gate — a seal certifies a crossing, not a hope. Close the gaps above, then re-run `runward check --freeze`."));
|
|
196
196
|
}
|
|
197
197
|
else {
|
|
198
|
-
const sealedAt =
|
|
198
|
+
const sealedAt = generationDate();
|
|
199
199
|
const content = renderEvidenceLock(mission, sealedAt);
|
|
200
200
|
const count = Object.keys(JSON.parse(content).files).length;
|
|
201
201
|
if (process.env.RUNWARD_DRY_RUN === "1") {
|
|
@@ -3,7 +3,7 @@ import { findMissionRoot } from "../lib/mission.js";
|
|
|
3
3
|
import { gatherComplianceInputs, renderIso42001Readiness, renderNistAiRmf, renderEuAiAct, renderOscal } from "../lib/compliance.js";
|
|
4
4
|
import { loadRegime, regimeLensId } from "../lib/regimes.js";
|
|
5
5
|
import { makeWriter } from "../lib/write.js";
|
|
6
|
-
import { c, createHeader,
|
|
6
|
+
import { c, createHeader, generationDate, section, status } from "../lib/styles.js";
|
|
7
7
|
import { VERSION } from "../lib/paths.js";
|
|
8
8
|
/**
|
|
9
9
|
* Assemble a regime-framed compliance evidence pack (ADR-0016) — deterministic, read-only, zero-LLM,
|
|
@@ -44,7 +44,7 @@ export async function complianceCommand(regime, opts) {
|
|
|
44
44
|
}
|
|
45
45
|
const mission = join(root, "runward");
|
|
46
46
|
const dryRun = process.env.RUNWARD_DRY_RUN === "1";
|
|
47
|
-
const generatedAt =
|
|
47
|
+
const generatedAt = generationDate();
|
|
48
48
|
console.log(section("Assembling (read-only, deterministic)"));
|
|
49
49
|
const inputs = gatherComplianceInputs(mission);
|
|
50
50
|
const md = spec.render(inputs, generatedAt, lens);
|
package/dist/commands/doctor.js
CHANGED
|
@@ -36,7 +36,8 @@ export async function doctorCommand() {
|
|
|
36
36
|
const ruleCount = existsSync(rulesDir) ? readdirSync(rulesDir).filter((f) => f.endsWith(".md")).length : 0;
|
|
37
37
|
ruleCount === EXPECTED_RULES ? ok(`${ruleCount} craft rules`) : fail(`craft rules mismatch: ${ruleCount}/${EXPECTED_RULES}`);
|
|
38
38
|
const adaptersDir = join(TEMPLATES, "adapters");
|
|
39
|
-
|
|
39
|
+
// Count real adapters, not the README — else substituting an adapter for a stray file passes.
|
|
40
|
+
const adapterCount = existsSync(adaptersDir) ? readdirSync(adaptersDir).filter((f) => f !== "README.md").length : 0;
|
|
40
41
|
adapterCount === EXPECTED_ADAPTERS ? ok(`${adapterCount} gate adapters`) : fail(`gate adapters mismatch: ${adapterCount}/${EXPECTED_ADAPTERS}`);
|
|
41
42
|
// Routed-count floor (ADR-0002): the phases: mapping must not be stripped below its pinned minimum.
|
|
42
43
|
const belowFloor = Object.entries(EXPECTED_MAPPED).filter(([phase, floor]) => expectedRules(TEMPLATES, phase).length < floor);
|
package/dist/commands/init.js
CHANGED
|
@@ -71,6 +71,11 @@ export async function initCommand(opts) {
|
|
|
71
71
|
// floor with them, so the gate is green because the evidence resolves — not unchecked.
|
|
72
72
|
if (existsSync(EXAMPLE_CODE))
|
|
73
73
|
copyTree(w, EXAMPLE_CODE, join(root, "code"));
|
|
74
|
+
// The reference ships its FINALIZED charter (ADR-0026): laid down first, so the scaffold
|
|
75
|
+
// write below skips it — the example demonstrates the leave-behind, not the starting text.
|
|
76
|
+
const exampleCharter = join(EXAMPLE_CODE, "..", "AGENTS.md");
|
|
77
|
+
if (existsSync(exampleCharter))
|
|
78
|
+
w.copy(exampleCharter, join(root, "AGENTS.md"));
|
|
74
79
|
// The reference omits the three non-gated scaffolding notes; add them as blank templates so the scaffold stays complete.
|
|
75
80
|
for (const extra of ["reference-stack.md", "shared-bricks.md", "gap-analysis.md"]) {
|
|
76
81
|
w.copy(join(TEMPLATES, "mission", extra), join(mission, extra));
|
package/dist/lib/compliance.js
CHANGED
|
@@ -34,7 +34,9 @@ function readRules(missionDir) {
|
|
|
34
34
|
if (!existsSync(dir))
|
|
35
35
|
return [];
|
|
36
36
|
const out = [];
|
|
37
|
-
|
|
37
|
+
// Sorted: the ASI coverage lists and the OSCAL derive from this order, so it must be deterministic
|
|
38
|
+
// across filesystems (readdirSync order is not guaranteed) for the byte-identity invariant to hold.
|
|
39
|
+
for (const f of readdirSync(dir).sort()) {
|
|
38
40
|
if (!f.endsWith(".md"))
|
|
39
41
|
continue;
|
|
40
42
|
let fm = "";
|
|
@@ -324,9 +326,15 @@ const ASI_CATALOG = "https://genai.owasp.org/resource/owasp-top-10-for-agentic-a
|
|
|
324
326
|
*/
|
|
325
327
|
export function renderOscal(inputs, missionName, generatedAt, lensId) {
|
|
326
328
|
const ns = missionName || "mission";
|
|
329
|
+
// The link points at the readiness draft co-generated with this pack — the one whose lens framed it.
|
|
330
|
+
const regime = lensId ? lensId.split("@")[0] : "iso-42001";
|
|
331
|
+
const readinessHref = `./${regime}-readiness.md`;
|
|
327
332
|
const irs = Object.keys(ASI_LABELS).map((id) => {
|
|
328
333
|
const slugs = inputs.asiCoverage.get(id) ?? [];
|
|
329
|
-
|
|
334
|
+
// Aggregate EVERY manifest row of EVERY rule mapping this ASI, across all gated deliverables —
|
|
335
|
+
// a rule can appear in more than one manifest (spec §3). `find` (first row) made the status depend
|
|
336
|
+
// on deliverable order and could report `implemented` where a later `deviated` row means `partial`.
|
|
337
|
+
const statuses = slugs.flatMap((s) => inputs.conformance.filter((c) => c.rule === s).map((c) => c.status));
|
|
330
338
|
let impl;
|
|
331
339
|
if (slugs.length === 0)
|
|
332
340
|
impl = "planned"; // no rule maps this risk — a gap
|
|
@@ -339,7 +347,7 @@ export function renderOscal(inputs, missionName, generatedAt, lensId) {
|
|
|
339
347
|
"control-id": `asi-${id.slice(3)}`,
|
|
340
348
|
description: `${id} ${ASI_LABELS[id]}. ${slugs.length ? "Addressed by rules: " + slugs.join(", ") + "." : "No rule mapped — gap to assess."}`,
|
|
341
349
|
props: [{ name: "implementation-status", value: impl }],
|
|
342
|
-
links: [{ href:
|
|
350
|
+
links: [{ href: readinessHref, rel: "reference", text: "runward assessment-readiness draft" }],
|
|
343
351
|
};
|
|
344
352
|
});
|
|
345
353
|
const doc = {
|
package/dist/lib/conformance.js
CHANGED
|
@@ -10,6 +10,7 @@ export const GATED_DELIVERABLES = [
|
|
|
10
10
|
{ phase: "topology", deliverable: "execution-topology.md", label: "Topology" },
|
|
11
11
|
{ phase: "floor", deliverable: "floor.md", label: "Floor" },
|
|
12
12
|
{ phase: "govern", deliverable: "governance/threat-model.md", label: "Govern" },
|
|
13
|
+
{ phase: "handover", deliverable: "handover.md", label: "Handover" },
|
|
13
14
|
];
|
|
14
15
|
const FRONTMATTER = /^---\n([\s\S]*?)\n---/;
|
|
15
16
|
const VALID_STATUS = new Set(["applied", "deviated", "n/a"]);
|
package/dist/lib/constants.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/** Total number of craft rules shipped under templates/rules/. */
|
|
2
|
-
export const EXPECTED_RULES =
|
|
3
|
-
/** Gate adapters shipped under templates/adapters/ (ADR-0012): one per harness seam
|
|
4
|
-
|
|
2
|
+
export const EXPECTED_RULES = 64;
|
|
3
|
+
/** Gate adapters shipped under templates/adapters/ (ADR-0012): one per harness seam. Counts the real
|
|
4
|
+
* adapters only — the port-contract README.md is excluded, so swapping an adapter for a stray file
|
|
5
|
+
* cannot pass the count. pre-commit, github-actions, gitlab-ci, claude-code-settings, kiro-hooks,
|
|
6
|
+
* bmad-review-layer. */
|
|
7
|
+
export const EXPECTED_ADAPTERS = 6;
|
|
5
8
|
/** Routed-count floor: minimum CRITICAL/HIGH rules mapped to each build phase (ADR-0002).
|
|
6
9
|
* Lowering a floor is a deliberate, tracked edit — the `phases:` mapping cannot be silently
|
|
7
10
|
* stripped to make `check --strict` pass vacuously. */
|
|
8
|
-
export const EXPECTED_MAPPED = { architect: 6, topology: 4, floor: 10, govern: 12 };
|
|
11
|
+
export const EXPECTED_MAPPED = { architect: 6, topology: 4, floor: 10, govern: 12, handover: 4 };
|
package/dist/lib/evidence.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
3
|
-
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { parseManifest, evidencePathTokens, adrIdExists, GATED_DELIVERABLES } from "./conformance.js";
|
|
5
5
|
const POINTER_PREFIX = /\b(file|test|adr):(\S.*)$/;
|
|
6
6
|
/** Parse the typed pointers out of an Evidence cell. One pointer per `;`-separated segment. */
|
|
@@ -53,16 +53,33 @@ export function parseEvidencePointers(evidence) {
|
|
|
53
53
|
function clean(token) {
|
|
54
54
|
return token.replace(/[),.:]+$/, "");
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* A rule signature is operator-authored data the gate compiles into a RegExp and runs against file
|
|
58
|
+
* content — so a catastrophic-backtracking pattern (nested quantifiers like `(a+)+`) could hang the
|
|
59
|
+
* gate on adversarial input (a self-inflicted DoS in CI). Reject the known-dangerous shape up front,
|
|
60
|
+
* deterministically: a quantifier applied to a group that itself contains a quantifier. This is a
|
|
61
|
+
* conservative screen (it may reject a rare safe pattern — the operator rewrites it), never a promise
|
|
62
|
+
* to catch every pathological regex. Signatures are simple token alternations in practice. See ADR-0020.
|
|
63
|
+
*/
|
|
64
|
+
export function unsafeSignature(source) {
|
|
65
|
+
// group whose body holds a quantifier, immediately followed by another quantifier: (…+…)+ (…*…)* etc.
|
|
66
|
+
return /\((?![?])[^()]*[+*}][^()]*\)[+*{]/.test(source);
|
|
67
|
+
}
|
|
56
68
|
/** The same three resolution bases as the drift pass (ADR-0004). */
|
|
57
69
|
export function resolutionBases(missionDir, deliverable) {
|
|
58
70
|
return [dirname(missionDir), missionDir, dirname(join(missionDir, deliverable))];
|
|
59
71
|
}
|
|
72
|
+
/** Resolve a pointer strictly WITHIN one of the three project bases (ADR-0019): an absolute
|
|
73
|
+
* path, or a `../` that climbs out of every base, is not "evidence in your project" — it is
|
|
74
|
+
* rejected, not resolved. `resolve` normalizes `..`; the containment check is a prefix test on
|
|
75
|
+
* the normalized path (with a separator, so `/a/project-evil` never counts as under `/a/project`). */
|
|
60
76
|
function resolveFile(p, bases) {
|
|
61
77
|
if (isAbsolute(p))
|
|
62
|
-
return
|
|
78
|
+
return null; // an absolute evidence path escapes the project — never valid
|
|
63
79
|
for (const b of bases) {
|
|
64
|
-
const
|
|
65
|
-
|
|
80
|
+
const baseAbs = resolve(b);
|
|
81
|
+
const abs = resolve(baseAbs, p);
|
|
82
|
+
if ((abs === baseAbs || abs.startsWith(baseAbs + sep)) && existsSync(abs))
|
|
66
83
|
return abs;
|
|
67
84
|
}
|
|
68
85
|
return null;
|
|
@@ -139,6 +156,10 @@ export function evidenceReport(missionDir, deliverable, signatures) {
|
|
|
139
156
|
// Signature (ADR-0020): a signed rule's applied evidence must contain the rule's shape.
|
|
140
157
|
const sig = signatures[row.rule];
|
|
141
158
|
if (sig) {
|
|
159
|
+
if (unsafeSignature(sig)) {
|
|
160
|
+
out.push({ rule: row.rule, problem: `unsafe signature regex (nested quantifiers risk catastrophic backtracking): /${sig}/ — simplify it in runward/rules/${row.rule}.md` });
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
142
163
|
let re;
|
|
143
164
|
try {
|
|
144
165
|
re = new RegExp(sig, "i");
|
package/dist/lib/mission.js
CHANGED
|
@@ -33,7 +33,10 @@ export const PHASES = [
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
id: "handover", label: "6 · Hand over",
|
|
36
|
-
artifacts: [
|
|
36
|
+
artifacts: [
|
|
37
|
+
{ label: "Recovery runbook", relPath: "runbook.md", templateKey: "runbook.md" },
|
|
38
|
+
{ label: "Hand-over note (the kit, proven)", relPath: "handover.md", templateKey: "handover.md" },
|
|
39
|
+
],
|
|
37
40
|
},
|
|
38
41
|
];
|
|
39
42
|
export function findMissionRoot(cwd) {
|
package/dist/lib/paths.js
CHANGED
|
@@ -27,6 +27,7 @@ export const MISSION_LAYOUT = {
|
|
|
27
27
|
"observability-schema.md": "governance/observability-schema.md",
|
|
28
28
|
"port-contract.md": "contracts/port-contract.md",
|
|
29
29
|
"runbook.md": "runbook.md",
|
|
30
|
+
"handover.md": "handover.md",
|
|
30
31
|
};
|
|
31
32
|
export const WORKFLOWS = [
|
|
32
33
|
"method", "frame", "architect", "floor", "iterate",
|
package/dist/lib/styles.js
CHANGED
|
@@ -54,6 +54,19 @@ export function section(title) {
|
|
|
54
54
|
export function isNonInteractive() {
|
|
55
55
|
return process.env.RUNWARD_YES === "1" || !process.stdout.isTTY;
|
|
56
56
|
}
|
|
57
|
+
/** Today (UTC, YYYY-MM-DD), or the RUNWARD_NOW override in non-interactive runs. The override is
|
|
58
|
+
* VALIDATED as a real YYYY-MM-DD date: an unvalidated value flowed straight into the OSCAL and the
|
|
59
|
+
* seal, so a malformed env var produced schema-invalid output. A bad value falls back to today. */
|
|
60
|
+
export function generationDate() {
|
|
61
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
62
|
+
const override = process.env.RUNWARD_NOW;
|
|
63
|
+
if (!isNonInteractive() || !override)
|
|
64
|
+
return today;
|
|
65
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(override))
|
|
66
|
+
return today;
|
|
67
|
+
const d = new Date(override + "T00:00:00Z");
|
|
68
|
+
return !isNaN(d.getTime()) && d.toISOString().slice(0, 10) === override ? override : today;
|
|
69
|
+
}
|
|
57
70
|
function stripAnsi(s) {
|
|
58
71
|
// eslint-disable-next-line no-control-regex
|
|
59
72
|
return s.replace(/\[[0-9;]*m/g, "");
|
package/dist/lib/tools.js
CHANGED
|
@@ -34,6 +34,10 @@ const PHASE_SKILLS = [
|
|
|
34
34
|
phase: "govern", label: "governance",
|
|
35
35
|
when: "governing an agentic system: the threat model, prompt-injection defense, evaluation, observability, resilience, or any sensitive-action approval",
|
|
36
36
|
},
|
|
37
|
+
{
|
|
38
|
+
phase: "handover", label: "hand-over",
|
|
39
|
+
when: "preparing or executing a succession: the recovery runbook, finalizing the agent charter as a leave-behind, the redone-task proof, or naming the owner who inherits the system",
|
|
40
|
+
},
|
|
37
41
|
];
|
|
38
42
|
/** The shared body — identical guidance across harnesses; only the packaging frontmatter differs. */
|
|
39
43
|
const skillBody = (s) => [
|
|
@@ -128,6 +132,17 @@ export const TOOL_PROFILES = [
|
|
|
128
132
|
label: "Trae (.trae/skills/)",
|
|
129
133
|
files: (root) => skillsAt(root, ".trae", "skills"),
|
|
130
134
|
},
|
|
135
|
+
{
|
|
136
|
+
id: "kiro",
|
|
137
|
+
label: "Kiro (.kiro/steering/runward-*)",
|
|
138
|
+
// Kiro reads AGENTS.md natively (the charter is already covered); this mirrors the phase
|
|
139
|
+
// skills as steering files. `inclusion: auto` + name + description is Kiro's idiom for
|
|
140
|
+
// relevance-loaded guidance — the same semantics as the SKILL.md description trigger.
|
|
141
|
+
files: (root) => PHASE_SKILLS.map((s) => ({
|
|
142
|
+
path: join(root, ".kiro", "steering", `runward-${s.phase}.md`),
|
|
143
|
+
content: ["---", "inclusion: auto", `name: runward-${s.phase}`, `description: ${yamlStr(`Runward ${s.label} craft rules. Use when ${s.when}.`)}`, "---", "", skillBody(s)].join("\n"),
|
|
144
|
+
})),
|
|
145
|
+
},
|
|
131
146
|
];
|
|
132
147
|
export const TOOL_IDS = TOOL_PROFILES.map((t) => t.id);
|
|
133
148
|
/** The gated build phases that get a relevance-loaded phase skill (ADR-0018). */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Agent charter — Inbound Request Triage (finalized leave-behind)
|
|
2
|
+
|
|
3
|
+
**Finalized at hand-over, 2026-07-03** — this is no longer the scaffolded charter: it carries what this mission learned. Any agent working on this system follows it; the receiving team owns the gate. Names and dates are illustrative, like the rest of the reference mission.
|
|
4
|
+
|
|
5
|
+
## Non-negotiable boundaries (mission-specific)
|
|
6
|
+
|
|
7
|
+
1. **No model-proposed value ever acts.** Every action-bearing field is recomputed or verified deterministically before RoutingPort (ADR-0002). The provenance marker is the law: `model-proposed` never routes, fail-closed.
|
|
8
|
+
2. **The account registry is read-only for agents.** It is the deterministic source the guard checks against; an agent that writes to it is falsifying its own verifier.
|
|
9
|
+
3. **Compliance-flagged records are always approval-gated.** No change may route them without human approval — the framing note's attached condition, not a default to optimize away.
|
|
10
|
+
4. **The TriageRecord contract changes only through governance.** Category vocabulary, fields, provenance markers: versioned, additive, one PR with its ADR — never widened ad hoc because the review queue grows (runbook §4).
|
|
11
|
+
5. **Traces stay in-app.** No third-party trace export without its own decision naming recipient, data class and retention (ADR-0003; execution-topology.md).
|
|
12
|
+
|
|
13
|
+
## How work is verified here
|
|
14
|
+
|
|
15
|
+
- `cd code && npm test` — the behavioral proof (domain, guard, contracts).
|
|
16
|
+
- `npx runward check --strict` — the conformance gate; the sole authority on the manifests.
|
|
17
|
+
- `cd code && npm run demo` — the guard's climax, reproducible: the fabricated account reference (`ACC-7777`) is refused fail-closed and escalated.
|
|
18
|
+
- Before crossing any green gate: the `verify` workflow (`runward/workflows/verify.md`), findings recorded in `runward/governance/verify-findings.md`.
|
|
19
|
+
|
|
20
|
+
## Never / PR rules
|
|
21
|
+
|
|
22
|
+
- **Never**: route on an unverified field; approve a compliance record programmatically; delete or rewrite the triage log (append-only); disable the guard "temporarily".
|
|
23
|
+
- **Always via a reviewed PR with its ADR**: contract or schema changes, category vocabulary changes, a new placement (execution-topology row), any new capability (decision matrix first — no trigger, no change).
|
|
24
|
+
|
|
25
|
+
## Mission state
|
|
26
|
+
|
|
27
|
+
| File | What it holds |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `runward/framing.md` · `runward/floor.md` | The criterion, and the measured proof against it |
|
|
30
|
+
| `runward/architecture.md` · `runward/execution-topology.md` | Ports, placements, sovereignty |
|
|
31
|
+
| `runward/adr/` | The decision journal — read the re-evaluation triggers before reopening anything |
|
|
32
|
+
| `runward/runbook.md` | The operating gestures (start, observe, recover, swap, approve) |
|
|
33
|
+
| `runward/handover.md` | The succession record: the kit, the redone-task proof, the named owner |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Hand-over Note — Request Triage
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-07-03 · **Departing builder**: the mission operator · **Receiving side**: operations team (triage product owner) · All names and dates **illustrative**, like the rest of the reference mission.
|
|
4
|
+
|
|
5
|
+
## 1. The kit
|
|
6
|
+
|
|
7
|
+
| Artifact | Where | State | Exercised by the receiving side? |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| Recovery runbook | `runbook.md` | ready | 2026-06-30 — ops engineer ran the recovery replay (§3) during the drill |
|
|
10
|
+
| Agent charter (finalized) | `AGENTS.md` (project root) | finalized at hand-over | read and countersigned at the second transfer session |
|
|
11
|
+
| Architecture note + decision journal | `architecture.md` · `adr/` (3 ADRs, triggers dated) | current | walked through, hands on keyboard |
|
|
12
|
+
| Evaluation bench | `cd code && npm test` + the replay set (floor.md §2) | ready | 2026-07-02 — re-run alone (see §2) |
|
|
13
|
+
| Evidence pack | `compliance/` — regenerate with `runward compliance <regime>` | regenerable on demand | generated once by the ops engineer during the drill |
|
|
14
|
+
|
|
15
|
+
## 2. The redone task (the proof)
|
|
16
|
+
|
|
17
|
+
- **Task**: after the illustrative provider incident of 2026-06-29 (gateway timeouts, fallback classifier engaged — runbook §4 first row), re-run the full behavioral proof and the demo, then process the suspended compliance approval that had queued during the incident — end to end, from the leave-behinds alone.
|
|
18
|
+
- **Date / doer**: 2026-07-02, the operations engineer of the receiving team; the builder unavailable by agreement (out of the loop for the day).
|
|
19
|
+
- **Evidence**: file:code/test/triage.test.ts; file:runward/runbook.md#Recovery — the proof re-ran green, the replay recovered the queued state, the suspended approval was released through the approval path (ADR-0002 provenance rules held).
|
|
20
|
+
- **Gaps found**: two, both folded back the same day — the runbook did not name where the review queue persists (added to §3), and the demo's expected output was undocumented (added to the code README).
|
|
21
|
+
|
|
22
|
+
## 3. Succession
|
|
23
|
+
|
|
24
|
+
- **Owner after departure**: the triage product owner (operations) — incidents, cost, credentials, and the three open ADR re-evaluation triggers.
|
|
25
|
+
- **Escalation path**: compliance officer for category/vocabulary questions; the sponsor (Head of Operations) for scope; provider support through the gateway contract.
|
|
26
|
+
- **Review cadence**: the usage registry and the ADR triggers are re-read at the weekly observability review (the guard-escalation rate of ADR-0002 is already on its agenda); the evidence seal is re-taken at each gate crossing.
|
|
27
|
+
- **Credentials boundary**: ticketing and gateway credentials live in the ops vault, injected at the network boundary; the builder's accesses were revoked on 2026-07-03 as part of this note.
|
|
28
|
+
|
|
29
|
+
## 4. Provider-swap drill
|
|
30
|
+
|
|
31
|
+
2026-06-27 — swapped the model adapter from the approved gateway deployment to the deterministic keyword fallback and back (the adapter switch of architecture.md §2), then re-ran the bench: accuracy on the replay set within the expected fallback envelope, provenance rules unaffected. The port held; the domain never noticed. Next drill on the shared-gateway trigger (ADR-0003).
|
|
32
|
+
|
|
33
|
+
## Rule conformance
|
|
34
|
+
|
|
35
|
+
| Rule | Status | Evidence |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| handover-redone-task-proof | applied | §2 — 2026-07-02, ops engineer, incident-recovery task end to end without the builder; file:code/test/triage.test.ts; file:runward/runbook.md#Recovery |
|
|
38
|
+
| handover-runbook-executable | applied | file:runward/runbook.md#Recovery — the seven gestures carry commands/paths (start §1, observe §1, debug §4, resume §3, swap §2+§4, bench: cd code && npm test, approvals §3); exercised during the 2026-07-02 task |
|
|
39
|
+
| handover-agents-charter-final | applied | file:AGENTS.md#Never — finalized at hand-over: mission-specific boundaries (registry read-only, compliance always approval-gated), exact verification commands, never/PR rules |
|
|
40
|
+
| handover-succession-named | applied | §3 — named owner (triage product owner), escalation path, weekly review cadence, builder's accesses revoked 2026-07-03 |
|
|
41
|
+
|
|
42
|
+
## Cross-references
|
|
43
|
+
|
|
44
|
+
- `workflows/handover.md` — the phase this note closes.
|
|
45
|
+
- `runbook.md` — the operational half of the kit.
|
|
46
|
+
- `AGENTS.md` — the charter the next agent inherits.
|
|
47
|
+
- `adr/` — the open re-evaluation triggers the owner now watches.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runward",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "After the spec: ship and run. A delivery framework for agentic systems \u2014 floor first, evolution on evidence, governance from day zero, handover with proof.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentic",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"dist",
|
|
33
33
|
"templates",
|
|
34
34
|
"regimes",
|
|
35
|
+
"examples/request-triage/AGENTS.md",
|
|
35
36
|
"examples/request-triage/runward",
|
|
36
37
|
"examples/request-triage/code/src",
|
|
37
38
|
"examples/request-triage/code/test",
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"regime": "eu-ai-act",
|
|
4
4
|
"label": "EU AI Act (Annex IV)",
|
|
5
5
|
"version": "2024-1689",
|
|
6
|
-
"notes": "Mapped
|
|
6
|
+
"notes": "Mapped against Regulation (EU) 2024/1689 (OJ L, 12 July 2024), as amended by the Digital Omnibus on AI (Council final green light 29 June 2026), which postponed the Annex III high-risk obligations to 2 December 2027 (Annex I to 2 August 2028). The 2 August 2026 milestone remains real for other obligations (Article 50 transparency for systems placed on the market after that date, governance, GPAI enforcement) but is NOT the high-risk / Annex IV deadline. Annex IV wording moves — confirm against the Official Journal before filing.",
|
|
7
7
|
"highRisk": {
|
|
8
|
-
"bindFrom": "2
|
|
9
|
-
"scope": "Annex III"
|
|
8
|
+
"bindFrom": "2 December 2027",
|
|
9
|
+
"scope": "Annex III, as postponed by the Digital Omnibus on AI (June 2026)"
|
|
10
10
|
},
|
|
11
11
|
"articles": {
|
|
12
12
|
"runtimeLogging": "art. 12"
|
|
@@ -49,6 +49,14 @@ Merge the `hooks` block into your `.claude/settings.json` (or `.claude/settings.
|
|
|
49
49
|
|
|
50
50
|
This is **one example** of a per-harness turn-end hook, not a privileged one — Claude Code just happens to expose a clean, documented seam. Any agent harness that can run a command at turn-end (Codex, and others as they add the capability) wires the *same* one line: `runward check --strict`. And where a harness offers no such seam, the `pre-commit` and CI adapters above already gate the code **whatever agent produced it** — the port is the exit code, not the agent.
|
|
51
51
|
|
|
52
|
+
## `kiro-hooks.json` — the same turn-end gate, Kiro flavor
|
|
53
|
+
|
|
54
|
+
Copy the file into `.kiro/hooks/` in your workspace. The `Stop` trigger runs `runward check --strict` when the agent finishes, and surfaces the verdict in the session — the same one line as the Claude Code hook, another non-privileged example of a per-harness turn-end seam. Kiro also reads `AGENTS.md` natively, and `runward init --tools kiro` mirrors the phase skills as steering files (`.kiro/steering/`, relevance-loaded) — the traced decisions inform the session; the gate stays the only authority.
|
|
55
|
+
|
|
56
|
+
## `bmad-review-layer.toml` — the gate as a review layer BMAD calls
|
|
57
|
+
|
|
58
|
+
Copy the `[[workflow.review_layers]]` block into the customize.toml of BMAD's `bmad-code-review` skill. It adds runward's deterministic gate as one review layer beside BMAD's adversarial LLM reviewers (their extension point explicitly allows "an external reviewer via bash"): they judge the code's craft, runward verifies the traced decisions and their evidence. A complement running on BMAD's flow, not a competitor to it — same inert posture, same exit-code port (ADR-0027).
|
|
59
|
+
|
|
52
60
|
---
|
|
53
61
|
|
|
54
62
|
Adapters are runward-owned templates: `runward update` refreshes them, `runward doctor` verifies them. They are never mission state. Add a new harness by dropping a new sample here — the port contract above does not change.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# runward gate — BMAD review-layer adapter (sample).
|
|
2
|
+
# Inert until you install it: see runward/adapters/README.md.
|
|
3
|
+
#
|
|
4
|
+
# BMAD's bmad-code-review skill exposes a review-layers extension point whose
|
|
5
|
+
# `instruction` may "run anything (e.g. an external reviewer via bash)". This
|
|
6
|
+
# adds runward's deterministic, zero-LLM gate as one review layer BESIDE BMAD's
|
|
7
|
+
# LLM reviewers — a complement, not a replacement: BMAD's subagents judge the
|
|
8
|
+
# code's craft; runward verifies, deterministically, that every CRITICAL/HIGH
|
|
9
|
+
# rule mapped to the phase is accounted for with real, resolving evidence.
|
|
10
|
+
#
|
|
11
|
+
# To install: copy this [[workflow.review_layers]] block into the customize.toml
|
|
12
|
+
# of BMAD's bmad-code-review skill (src/bmm-skills/4-implementation/bmad-code-review/).
|
|
13
|
+
# runward writes nothing into BMAD and runs nothing on its own — you wire it (ADR-0012).
|
|
14
|
+
#
|
|
15
|
+
# Exit codes (the port contract): 0 clean · 1 gaps · 2 no mission found.
|
|
16
|
+
|
|
17
|
+
[[workflow.review_layers]]
|
|
18
|
+
id = "runward-gate"
|
|
19
|
+
name = "runward deterministic gate"
|
|
20
|
+
# `when` (optional) could gate this layer on a runward/ mission being present.
|
|
21
|
+
instruction = """
|
|
22
|
+
Run the runward deterministic conformance gate as an external reviewer, via bash:
|
|
23
|
+
|
|
24
|
+
npx --yes runward check --strict
|
|
25
|
+
|
|
26
|
+
Report its exit code and output verbatim as this layer's finding:
|
|
27
|
+
- exit 0 → the conformance manifests are complete and their evidence resolves (a PASS finding, no severity).
|
|
28
|
+
- exit 1 → report each printed gap as a finding the operator must close before merge (a traced rule is unaccounted for, or an evidence pointer is hollow/stale). Do NOT paraphrase or soften: the gate is deterministic and its verdict is not a matter of judgment.
|
|
29
|
+
- exit 2 → no runward/ mission here; skip this layer (it does not apply to this repo).
|
|
30
|
+
|
|
31
|
+
This layer verifies traced decisions and their evidence; it never judges code quality — that stays the other review layers' job and the operator's at the gate.
|
|
32
|
+
"""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Hand-over Note
|
|
2
|
+
|
|
3
|
+
> **Usage.** Fill this note in phase 6 (`workflows/handover.md`), as the succession happens — not after. It is the kit made legible in one place: what is handed over, the proof that the receiving side can operate alone, and who owns the system now. `runward check --strict` verifies its conformance manifest; you transfer autonomy, not documents.
|
|
4
|
+
|
|
5
|
+
## 1. The kit
|
|
6
|
+
|
|
7
|
+
One row per leave-behind. State is honest: a document nobody has exercised is `untested`, not `ready`.
|
|
8
|
+
|
|
9
|
+
| Artifact | Where | State | Exercised by the receiving side? |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| Recovery runbook | `runbook.md` | [ready \| untested] | [when / by whom, or not yet] |
|
|
12
|
+
| Agent charter (finalized) | `AGENTS.md` | [finalized at hand-over \| still the scaffold] | [—] |
|
|
13
|
+
| Architecture note + decision journal | `architecture.md` · `adr/` | [current \| stale] | [—] |
|
|
14
|
+
| Evaluation bench | [where it runs] | [ready \| untested] | [—] |
|
|
15
|
+
| Evidence pack | `compliance/` (regenerate on demand) | [—] | [—] |
|
|
16
|
+
|
|
17
|
+
## 2. The redone task (the proof)
|
|
18
|
+
|
|
19
|
+
The hand-over is proven by a real task redone end to end **without the departing builder**. Record it here; point the manifest at this section.
|
|
20
|
+
|
|
21
|
+
- **Task**: [a real one from the mission's backlog — which change, run, fix or evaluation]
|
|
22
|
+
- **Date / doer**: [when, and who — explicitly without the builder in the loop]
|
|
23
|
+
- **Evidence**: [the artifact produced — a merged change, a report, a closed approval; a pointer the gate can verify]
|
|
24
|
+
- **Gaps found**: [every step the doer had to guess — each folded back into the runbook or charter before the gate is crossed]
|
|
25
|
+
|
|
26
|
+
## 3. Succession
|
|
27
|
+
|
|
28
|
+
- **Owner after departure**: [named person or standing role — incidents, cost, keys, open ADR triggers]
|
|
29
|
+
- **Escalation path**: [who is called when the owner is not enough — security contact, sponsor, provider support]
|
|
30
|
+
- **Review cadence**: [when the usage registry, the ADR triggers and the evidence seal are re-read, and in which forum]
|
|
31
|
+
- **Credentials boundary**: [where the keys live, who rotates them; the builder's accesses revoked on — date]
|
|
32
|
+
|
|
33
|
+
## 4. Provider-swap drill
|
|
34
|
+
|
|
35
|
+
The model is a replaceable adapter behind a port — proven, not assumed. Record the last drill: [date, from → to, what was re-run afterwards (the bench), result]. If never drilled, say so here and name the trigger that will force it.
|
|
36
|
+
|
|
37
|
+
## Rule conformance
|
|
38
|
+
|
|
39
|
+
> Account for every CRITICAL/HIGH craft rule mapped to the handover phase (`runward/rules/`, frontmatter `phases: [handover]`). `applied` needs a pointer; `deviated` needs an ADR; `n/a` needs a reason. `runward check --strict` verifies this table is complete — it checks a traced succession, not whether the hand-over went well.
|
|
40
|
+
>
|
|
41
|
+
> Evidence can be **typed**, and typed pointers are verified deterministically at the gate: `file:PATH[:LINE][#SYMBOL]` · `test:PATH[::NAME]` · `adr:NNNN` — several per cell, separated by `;`. The gate checks resolution, non-emptiness, line count, symbol/test-name presence, and the rule's `signature:` when it declares one (ADR-0019/0020). Free prose stays valid — it is your judgment; a path it cites must simply not point at an empty file.
|
|
42
|
+
|
|
43
|
+
| Rule | Status | Evidence |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| [rule-slug] | applied \| deviated \| n/a | [pointer, ADR-id, or reason] |
|
|
46
|
+
|
|
47
|
+
## Cross-references
|
|
48
|
+
|
|
49
|
+
- `workflows/handover.md` — the phase this note closes.
|
|
50
|
+
- `runbook.md` — the operational half of the kit; this note indexes, the runbook operates.
|
|
51
|
+
- `AGENTS.md` — the charter the next agent inherits.
|
|
52
|
+
- `adr/` — the open re-evaluation triggers the new owner now watches.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Agent Charter Is Finalized as the Leave-Behind
|
|
3
|
+
impact: HIGH
|
|
4
|
+
asi: [ASI10]
|
|
5
|
+
phases: [handover]
|
|
6
|
+
impactDescription: AGENTS.md leaves the mission as the standing constraint on every future agent — verification commands, judgment boundaries, never/PR rules — so the next agent inherits the discipline, not just the code
|
|
7
|
+
tags: [handover, agents-md, charter, governance, succession]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## The Agent Charter Is Finalized as the Leave-Behind
|
|
11
|
+
|
|
12
|
+
> **The next agent was not in the room.**
|
|
13
|
+
> Everything the mission learned about where agents must stop, ask, or prove is worthless if it leaves with the builder. `AGENTS.md` is where that judgment survives the succession — finalized, specific, and pointing at the gate.
|
|
14
|
+
|
|
15
|
+
During the mission, the charter accumulated defaults. At hand-over it must be **rewritten as the leave-behind**: the document a receiving team's agent reads on day one, with no builder nearby to fill the gaps. An inherited agent operating without a finalized charter is the quiet path to a rogue one (ASI10) — not through malice, but through inherited ambiguity: nothing says what it may touch, what proves its work, or when it must stop and ask a human.
|
|
16
|
+
|
|
17
|
+
**What "finalized" means, concretely:**
|
|
18
|
+
|
|
19
|
+
- **Verification commands, exact.** The charter names how work is proven here — `runward check --strict` at minimum, the behavioral proof command, the evaluation bench. An agent that cannot verify will assert.
|
|
20
|
+
- **Judgment boundaries, mission-specific.** Not the scaffold's generic five: the boundaries this mission actually discovered — which data classes are untouchable, which actions are approval-gated, which files no agent edits.
|
|
21
|
+
- **Never / PR rules.** What is never done autonomously, and what always goes through a reviewed change — stated as rules, not anecdotes.
|
|
22
|
+
- **The pointers resolve.** The workflows, rules and deliverables the charter cites still exist where it says — typed evidence in the manifest lets the gate verify that.
|
|
23
|
+
|
|
24
|
+
**Incorrect:**
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
| handover-agents-charter-final | applied | AGENTS.md exists since init |
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Existing since `init` is the opposite of the point: the scaffold is the starting text, the leave-behind is the mission's learned judgment.
|
|
31
|
+
|
|
32
|
+
**Correct:**
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
| handover-agents-charter-final | applied | file:AGENTS.md#Non-negotiable — finalized at hand-over: verification commands (check --strict + npm test + bench), the two mission-specific boundaries (registry data read-only; compliance-flagged routing always approval-gated), never/PR rules |
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Checklist:**
|
|
39
|
+
|
|
40
|
+
- [ ] The charter was re-edited at hand-over, not merely left as scaffolded.
|
|
41
|
+
- [ ] Verification commands are exact and were run by the receiving side (see the redone task).
|
|
42
|
+
- [ ] Mission-specific boundaries and never/PR rules are present and dated.
|
|
43
|
+
- [ ] Every path the charter cites resolves — the manifest points at the charter with typed evidence.
|
|
44
|
+
|
|
45
|
+
**Where this sits in the Runward method:** ADR-0010 made `AGENTS.md` a first-class hand-over deliverable; ADR-0026 makes its finalization a gated fact. The charter is how the mission's judgment outlives the mission.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Redone-Task Proof
|
|
3
|
+
impact: CRITICAL
|
|
4
|
+
asi: [ASI09]
|
|
5
|
+
phases: [handover]
|
|
6
|
+
impactDescription: A hand-over is proven by a real task redone end to end without the departing builder — recorded, dated, pointed at — so autonomy is a fact, not a farewell promise
|
|
7
|
+
tags: [handover, succession, autonomy, proof, transmission]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## The Redone-Task Proof
|
|
11
|
+
|
|
12
|
+
> **You transfer autonomy, not documents.**
|
|
13
|
+
> The hand-over is proven the day the receiving side redoes a real task, end to end, without the person who built the system. Until that has happened and been recorded, the succession is a hope with a runbook attached.
|
|
14
|
+
|
|
15
|
+
A kit can be complete and dead: every document present, nobody able to act. The only observable that distinguishes a live succession from a paper one is the **redone task** — a real change, run, fix or evaluation executed by the receiving team alone, from the leave-behinds, with the builder unavailable by agreement.
|
|
16
|
+
|
|
17
|
+
**What the record must carry** (in `handover.md`, the proof record):
|
|
18
|
+
|
|
19
|
+
- **The task** — a real one from the mission's actual work, not a rehearsed demo. Fixing a routing rule, re-running the evaluation bench after a model swap, processing a suspended approval, resuming from a checkpoint: something that exercises the runbook, the code and a decision.
|
|
20
|
+
- **The date and the doer** — who redid it, when, explicitly without the builder in the loop.
|
|
21
|
+
- **The evidence** — a pointer the gate can verify (`file:`, `test:`, or the artifact the task produced). "It went fine" is not evidence; the merged change, the produced report, the closed approval is.
|
|
22
|
+
- **What broke, honestly** — every step where the doer had to guess is a defect in the kit. Route each one back into the runbook or the charter before the gate is crossed.
|
|
23
|
+
|
|
24
|
+
**Incorrect:**
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
| handover-redone-task-proof | applied | the team is confident and has been walked through everything twice |
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A walkthrough is the builder performing; the proof is the builder absent.
|
|
31
|
+
|
|
32
|
+
**Correct:**
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
| handover-redone-task-proof | applied | file:runward/handover.md#Redone-task — 2026-06-28, ops engineer re-ran the eval bench after the provider swap, alone, from runbook §4; report at code/eval/reports/2026-06-28.md; two runbook gaps found and fixed |
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Checklist:**
|
|
39
|
+
|
|
40
|
+
- [ ] The task was real mission work, chosen from the backlog, not staged.
|
|
41
|
+
- [ ] The builder was genuinely out of the loop (agreed beforehand, verifiable).
|
|
42
|
+
- [ ] The record names task, date, doer, and points at the produced artifact.
|
|
43
|
+
- [ ] Every gap the doer hit was folded back into the runbook or charter.
|
|
44
|
+
- [ ] The record lives in `handover.md` where `runward check --strict` reads it.
|
|
45
|
+
|
|
46
|
+
**Where this sits in the Runward method:** phase 6 is where every other scaffold has already stopped. This rule is the difference between handing over a repository and handing over a running responsibility — and it is why the hand-over claim is gated, not narrated (ADR-0026).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Runbook Is Executable, Not Descriptive
|
|
3
|
+
impact: HIGH
|
|
4
|
+
asi: [ASI08]
|
|
5
|
+
phases: [handover]
|
|
6
|
+
impactDescription: The recovery runbook carries real commands for the gestures that matter at 3 a.m. — start, observe, debug, resume, swap the provider, rerun the bench, process a suspended approval — so an incident meets procedures, not prose
|
|
7
|
+
tags: [handover, runbook, operations, recovery, resilience]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## The Runbook Is Executable, Not Descriptive
|
|
11
|
+
|
|
12
|
+
> **A runbook is judged at 3 a.m., by someone who was not there when the system was built.**
|
|
13
|
+
> Every gesture it promises must be a command that runs, a path that exists, a signal that can actually be read — not a paragraph explaining that such things are possible.
|
|
14
|
+
|
|
15
|
+
Agentic systems fail in cascades (ASI08): a provider degrades, a queue backs up, an approval hangs, and the operator's margin is measured in minutes. The runbook is the artifact that converts a cascade into a sequence of known gestures. A descriptive runbook — "the system can be restarted; logs are available" — is a cascade accelerant: it costs the reader the minutes it pretends to save.
|
|
16
|
+
|
|
17
|
+
**The seven gestures the runbook must carry, each with its real command or path:**
|
|
18
|
+
|
|
19
|
+
1. **Start / stop** — the exact commands, including the safe-stop that drains instead of kills.
|
|
20
|
+
2. **Observe** — where the structured logs and metrics are, and the one query that answers "is it healthy?".
|
|
21
|
+
3. **Debug a trajectory** — how to reconstruct a full run from one request ID.
|
|
22
|
+
4. **Resume from a checkpoint** — where suspended state lives and the command that rehydrates it.
|
|
23
|
+
5. **Swap the model provider** — the adapter switch, the config to change, and what to re-run afterwards.
|
|
24
|
+
6. **Rerun the evaluation bench** — the command, and where the report lands.
|
|
25
|
+
7. **Process a suspended approval** — where it queues, who may approve, the command or UI path that releases it.
|
|
26
|
+
|
|
27
|
+
**Incorrect:**
|
|
28
|
+
|
|
29
|
+
```markdown
|
|
30
|
+
## Recovery
|
|
31
|
+
The system is resilient and can be restarted. Observability is in place.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Correct:**
|
|
35
|
+
|
|
36
|
+
```markdown
|
|
37
|
+
## 4. Resume from a checkpoint
|
|
38
|
+
Suspended runs live in var/journal/ (one file per request ID).
|
|
39
|
+
npm run resume -- --request <id>
|
|
40
|
+
Verify: the trajectory for <id> continues in the structured log within 30 s.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Checklist:**
|
|
44
|
+
|
|
45
|
+
- [ ] All seven gestures present, each with a command or concrete path — no gesture "described" without one.
|
|
46
|
+
- [ ] Commands were executed at least once by someone other than their author (the redone task is the natural occasion).
|
|
47
|
+
- [ ] The runbook names its own verification: how the reader confirms each gesture worked.
|
|
48
|
+
- [ ] Paths and commands are pointed at from the manifest with typed evidence, so the gate catches drift when they move.
|
|
49
|
+
|
|
50
|
+
**Where this sits in the Runward method:** the runbook is scaffolded at `init` and demanded by the hand-over workflow since day one; this rule makes its *executability* a gated fact (ADR-0026) instead of a review opinion.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The Succession Is Named
|
|
3
|
+
impact: HIGH
|
|
4
|
+
asi: []
|
|
5
|
+
phases: [handover]
|
|
6
|
+
impactDescription: The system leaves the mission with a named owner, an escalation path and a review cadence — an unowned agentic system in production is an incident with a start date
|
|
7
|
+
tags: [handover, ownership, succession, governance, operations]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## The Succession Is Named
|
|
11
|
+
|
|
12
|
+
> **Systems do not stay owned by default; they become orphans by default.**
|
|
13
|
+
> The day the builder leaves, either a named person owns the system — its budget, its keys, its incidents, its re-evaluation triggers — or nobody does, and every ADR trigger, every drift signal, every suspended approval now fires into a void.
|
|
14
|
+
|
|
15
|
+
runward's whole discipline rests on someone reading the signals: re-evaluation triggers are *watched*, drift is *re-verified*, approvals are *processed*, the usage registry is *reviewed*. Those are verbs with a subject. Hand-over is the moment the subject changes — and the moment it silently becomes no one, unless the succession is written down where the gate can see it.
|
|
16
|
+
|
|
17
|
+
**What the record must carry** (in `handover.md`):
|
|
18
|
+
|
|
19
|
+
- **The owner** — a named person or standing role (not "the team") who owns the system after the departure: incidents, cost, keys, and the decision journal's open triggers.
|
|
20
|
+
- **The escalation path** — who is called when the owner is not enough: security contact, sponsor, provider support entry points.
|
|
21
|
+
- **The review cadence** — when the usage registry, the ADR triggers and the evidence seal are re-read (the iterate-gate rhythm surviving the builder), and in which forum.
|
|
22
|
+
- **The credentials boundary** — where the keys live and who may rotate them; the departing builder's accesses are revoked as part of the hand-over, and the record says so.
|
|
23
|
+
|
|
24
|
+
**Incorrect:**
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
| handover-succession-named | applied | the team owns it going forward |
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A team is not a subject; "going forward" is not a cadence.
|
|
31
|
+
|
|
32
|
+
**Correct:**
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
| handover-succession-named | applied | file:runward/handover.md#Succession — owner: ops lead (named); escalation: security officer, then sponsor; registry + ADR triggers reviewed monthly in the ops review; builder's accesses revoked 2026-06-30 |
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Checklist:**
|
|
39
|
+
|
|
40
|
+
- [ ] One named owner (person or standing role) — for incidents, cost, keys and open ADR triggers.
|
|
41
|
+
- [ ] An escalation path with real names or roles, reachable.
|
|
42
|
+
- [ ] A written cadence for re-reading the registry, the triggers and the seal.
|
|
43
|
+
- [ ] The builder's credentials revoked, and the revocation recorded.
|
|
44
|
+
- [ ] The record lives in `handover.md`, verified by `runward check --strict`.
|
|
45
|
+
|
|
46
|
+
**Where this sits in the Runward method:** governance from day zero only matters if it survives day last. This rule is the difference between transmitting a system and abandoning it politely (ADR-0026).
|
|
@@ -31,3 +31,4 @@ This project is delivered with the Runward method: floor first, evolution on evi
|
|
|
31
31
|
| `runward/adr/` | Decision journal |
|
|
32
32
|
| `runward/governance/` | Threat model, evaluation rubric, observability schema |
|
|
33
33
|
| `runward/runbook.md` | Recovery runbook for the receiving team |
|
|
34
|
+
| `runward/handover.md` | The succession record: kit index, redone-task proof, named owner — gated at phase 6 |
|
|
@@ -8,12 +8,13 @@ Use this workflow when a delivery approaches its end or a team must become self-
|
|
|
8
8
|
|
|
9
9
|
- The artifacts of prior phases: architecture note, floor, ADR journal, threat model, evaluation rubric, observability schema.
|
|
10
10
|
- A receiving team with time allocated for transfer sessions.
|
|
11
|
-
- The `mission/runbook.md`
|
|
11
|
+
- The `mission/runbook.md` and `mission/handover.md` templates.
|
|
12
12
|
|
|
13
13
|
## Outputs
|
|
14
14
|
|
|
15
15
|
- A handover kit (below).
|
|
16
16
|
- A demonstrated proof of autonomy.
|
|
17
|
+
- The hand-over note (`mission/handover.md`): the kit indexed, the redone-task proof recorded, the succession named, the provider-swap drill logged — the gated deliverable of this phase.
|
|
17
18
|
|
|
18
19
|
## Procedure
|
|
19
20
|
|
|
@@ -36,11 +37,15 @@ Use this workflow when a delivery approaches its end or a team must become self-
|
|
|
36
37
|
|
|
37
38
|
**Capitalize upward.** What repeats deserves standardizing. Escalate recurring patterns to whoever can industrialize them: an evolution trigger observed several times, an adapter rewritten identically across missions, a recurring guardrail. Feed proven arbitrations back into the decision matrix and the workflow files — the corpus hardens mission after mission. What gets capitalized is never the prototype (replaceable); it is the architecture and the method (owned).
|
|
38
39
|
|
|
39
|
-
**
|
|
40
|
+
**Record the succession in the hand-over note.** Fill `mission/handover.md` as the succession happens: the kit index with honest states, the redone-task proof (task, date, doer, evidence, gaps folded back), the named owner with escalation path and review cadence, the provider-swap drill. Confront the CRITICAL/HIGH rules mapped to the handover phase (`runward/rules/`, `phases: [handover]`) and account for each in the note's `Rule conformance` manifest — `runward check --strict` verifies it, down to the typed evidence pointers.
|
|
41
|
+
|
|
42
|
+
**Verify the Definition of Done explicitly.** The phase is done when the receiving team redoes a task alone, end to end, demonstrated — a sign-off or a training session does not count. The team, without you: starts the system, observes it, debugs an incident, adds an evaluation scenario, and decides a switch by leaning on the ADRs. All of it demonstrated, not promised — and recorded in the hand-over note, where the gate reads it.
|
|
40
43
|
|
|
41
44
|
## Definition of Done
|
|
42
45
|
|
|
43
46
|
- Handover kit complete: assets, architecture note, ADR journal, runbook, proof in code, and the finalized `AGENTS.md` charter (the harness-neutral leave-behind).
|
|
47
|
+
- Hand-over note produced (`mission/handover.md`): kit indexed, redone-task proof recorded, succession named, provider-swap drill logged.
|
|
48
|
+
- Every handover CRITICAL/HIGH rule accounted for in the note's conformance manifest (`runward check --strict`).
|
|
44
49
|
- Transfer sessions held with the team operating, not observing.
|
|
45
50
|
- The team has repeated at least one real task end to end, alone — demonstrated, witnessed, recorded.
|
|
46
51
|
- Recurring patterns escalated and fed back into the framework's templates.
|