runward 0.13.3 → 0.14.1
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 +1 -1
- package/dist/cli.js +27 -3
- package/dist/commands/check.js +4 -1
- package/dist/commands/status.js +2 -2
- package/dist/lib/conformance.js +9 -2
- package/dist/lib/mission.js +23 -4
- package/dist/lib/tools.js +7 -2
- package/examples/request-triage/runward/compliance/eu-ai-act-readiness.md +89 -0
- package/examples/request-triage/runward/compliance/oscal-component-definition.json +209 -0
- package/package.json +2 -1
- package/templates/workflows/architect.md +3 -0
- package/templates/workflows/frame.md +2 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**After the spec, the hard part starts. Runward ships it and runs it.**
|
|
8
8
|
|
|
9
|
-
*
|
|
9
|
+
*The floor that keeps an agentic system alive. Run-grade engineering, from business need to production.*
|
|
10
10
|
|
|
11
11
|
Your agent builds fast. Runward frames what it builds — six gated phases your agent executes and one human signs off — so what ships actually holds.
|
|
12
12
|
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,8 @@ import { doctorCommand } from "./commands/doctor.js";
|
|
|
13
13
|
import { updateCommand } from "./commands/update.js";
|
|
14
14
|
import { characterizeCommand } from "./commands/characterize.js";
|
|
15
15
|
import { complianceCommand } from "./commands/compliance.js";
|
|
16
|
-
|
|
16
|
+
import { TOOL_IDS } from "./lib/tools.js";
|
|
17
|
+
// Exit codes: 0 = success · 1 = gaps/warnings · 2 = missing prerequisite or CLI misuse (typo, unknown flag)
|
|
17
18
|
process.on("uncaughtException", (err) => {
|
|
18
19
|
if (err.name === "ExitPromptError")
|
|
19
20
|
process.exit(130); // Ctrl+C in a prompt
|
|
@@ -52,7 +53,7 @@ program
|
|
|
52
53
|
.command("init")
|
|
53
54
|
.description("scaffold the mission structure (interactive wizard, or --yes)")
|
|
54
55
|
.option("-p, --path <path>", "project directory (default: prompt, or . with --yes)")
|
|
55
|
-
.option("-t, --tools <list>",
|
|
56
|
+
.option("-t, --tools <list>", `comma-separated tool profiles: ${TOOL_IDS.join(",")}`)
|
|
56
57
|
.option("--force", "overwrite existing files")
|
|
57
58
|
.option("--example", "scaffold a filled reference mission (request-triage) — the whole chain is green out of the box")
|
|
58
59
|
.action(initCommand);
|
|
@@ -91,4 +92,27 @@ program
|
|
|
91
92
|
.argument("[regime]", "iso-42001 | nist-ai-rmf | eu-ai-act")
|
|
92
93
|
.option("-p, --path <path>", "project directory")
|
|
93
94
|
.action(complianceCommand);
|
|
94
|
-
|
|
95
|
+
// exitOverride lets us map Commander's own errors onto runward's exit-code contract:
|
|
96
|
+
// a parse error (unknown command/option, missing/excess argument) is operator misuse → 2,
|
|
97
|
+
// so CI can tell a typo from a legitimate gate failure (exit 1). Help/version exit 0.
|
|
98
|
+
// Applied to the root AND every subcommand — it does not propagate on its own.
|
|
99
|
+
const MISUSE = new Set([
|
|
100
|
+
"commander.unknownCommand", "commander.unknownOption", "commander.invalidArgument",
|
|
101
|
+
"commander.missingArgument", "commander.excessArguments",
|
|
102
|
+
"commander.missingMandatoryOptionValue", "commander.optionMissingArgument",
|
|
103
|
+
]);
|
|
104
|
+
const onCommanderExit = (err) => {
|
|
105
|
+
const code = err?.code ?? "";
|
|
106
|
+
if (code === "commander.helpDisplayed" || code === "commander.version" || code === "commander.help")
|
|
107
|
+
process.exit(0);
|
|
108
|
+
process.exit(MISUSE.has(code) ? 2 : (err?.exitCode ?? 1)); // Commander already wrote the message
|
|
109
|
+
};
|
|
110
|
+
program.exitOverride(onCommanderExit);
|
|
111
|
+
program.commands.forEach((cmd) => cmd.exitOverride(onCommanderExit));
|
|
112
|
+
program.parseAsync().catch((err) => {
|
|
113
|
+
// An error escaping an async action — Commander's own exits are handled above.
|
|
114
|
+
console.error("\n " + c.error("✗") + " " + (err?.message ?? String(err)));
|
|
115
|
+
if (process.env.VERBOSE && err?.stack)
|
|
116
|
+
console.error(err.stack);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
package/dist/commands/check.js
CHANGED
|
@@ -68,7 +68,10 @@ export async function checkCommand(opts) {
|
|
|
68
68
|
for (const d of driftReport(mission, deliverable))
|
|
69
69
|
drift.push(`${label} · ${d.rule} — ${d.problem}`);
|
|
70
70
|
const { expected, violations } = conformance(mission, phase, deliverable);
|
|
71
|
-
|
|
71
|
+
// Non-vacuity (ADR-0002): when no rules are currently mapped to a phase, conformance()
|
|
72
|
+
// still raises a `(mapping)` violation if the mapping was stripped below its pinned
|
|
73
|
+
// floor. Only skip when there is genuinely nothing to report — never discard that signal.
|
|
74
|
+
if (expected.length === 0 && violations.length === 0)
|
|
72
75
|
continue;
|
|
73
76
|
checked++;
|
|
74
77
|
if (violations.length === 0) {
|
package/dist/commands/status.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import { analyze, findMissionRoot } from "../lib/mission.js";
|
|
3
|
+
import { analyze, findMissionRoot, isRealAdr } from "../lib/mission.js";
|
|
4
4
|
import { c, createHeader, section } from "../lib/styles.js";
|
|
5
5
|
import { VERSION, WORKFLOWS } from "../lib/paths.js";
|
|
6
6
|
/** Mission snapshot: phase progress, decision journal, activity, workflows —
|
|
@@ -44,7 +44,7 @@ export async function statusCommand(opts) {
|
|
|
44
44
|
console.log(section("Decision journal"));
|
|
45
45
|
const adrDir = join(mission, "adr");
|
|
46
46
|
const adrs = existsSync(adrDir)
|
|
47
|
-
? readdirSync(adrDir).filter(
|
|
47
|
+
? readdirSync(adrDir).filter(isRealAdr).sort()
|
|
48
48
|
: [];
|
|
49
49
|
if (adrs.length === 0) {
|
|
50
50
|
console.log(c.darkGray(" no ADR yet — every structural decision must be locked"));
|
package/dist/lib/conformance.js
CHANGED
|
@@ -59,7 +59,9 @@ export function parseManifest(content) {
|
|
|
59
59
|
const cols = line.split("|").slice(1, -1).map((c) => c.replace(/`/g, "").trim());
|
|
60
60
|
if (cols.length < 3)
|
|
61
61
|
continue;
|
|
62
|
-
|
|
62
|
+
// Evidence may itself contain a pipe (a TS union `a | b`, a table hint) — rejoin the
|
|
63
|
+
// tail so it is not truncated, which would wrongly fail an n/a row on a trivial reason.
|
|
64
|
+
const rule = cols[0], status = cols[1], evidence = cols.slice(2).join(" | ");
|
|
63
65
|
if (/^rule$/i.test(rule) || /^:?-+:?$/.test(rule))
|
|
64
66
|
continue; // header / separator
|
|
65
67
|
rows.push({ rule, status: status.toLowerCase(), evidence });
|
|
@@ -71,7 +73,12 @@ function adrExists(missionDir, evidence) {
|
|
|
71
73
|
if (!id)
|
|
72
74
|
return false;
|
|
73
75
|
const dir = join(missionDir, "adr");
|
|
74
|
-
|
|
76
|
+
// Anchor on a digit boundary so a `deviated` row citing ADR-1 is not satisfied by
|
|
77
|
+
// ADR-10 / ADR-12 when filenames are unpadded.
|
|
78
|
+
return existsSync(dir) && readdirSync(dir).some((f) => {
|
|
79
|
+
const u = f.toUpperCase();
|
|
80
|
+
return u.startsWith(id) && !/[0-9]/.test(u.charAt(id.length));
|
|
81
|
+
});
|
|
75
82
|
}
|
|
76
83
|
/**
|
|
77
84
|
* The reconstruction lifecycle (ADR-0013/0014). A retroactively reconstructed decision is a
|
package/dist/lib/mission.js
CHANGED
|
@@ -54,13 +54,19 @@ export function findMissionRoot(cwd) {
|
|
|
54
54
|
// which distinguishes it from cross-references like [ADR-0001] or [framing.md].
|
|
55
55
|
// A closing bracket followed by "(" is a markdown link ([floor note](floor.md)) — never a placeholder.
|
|
56
56
|
const PLACEHOLDER = /\[[^\]\n]*\s[^\]\n]{1,80}\](?!\()/g;
|
|
57
|
+
/** A real ADR file: ADR-<n>-*.md, excluding the scaffolded template. Single source of
|
|
58
|
+
* truth so the mission, status, and conformance paths agree (they used to diverge:
|
|
59
|
+
* a `!f.includes("0000")` filter wrongly dropped e.g. ADR-0021-…-10000-ms.md). */
|
|
60
|
+
export function isRealAdr(f) {
|
|
61
|
+
return /^ADR-\d+/.test(f) && f.endsWith(".md") && f !== "ADR-0000-template.md";
|
|
62
|
+
}
|
|
57
63
|
export function artifactState(missionDir, a) {
|
|
58
64
|
const path = join(missionDir, a.relPath);
|
|
59
65
|
if (!existsSync(path))
|
|
60
66
|
return "missing";
|
|
61
67
|
// Special case: ADR directory — count real ADRs beyond the template.
|
|
62
68
|
if (a.relPath === "adr") {
|
|
63
|
-
const adrs = readdirSync(path).filter(
|
|
69
|
+
const adrs = readdirSync(path).filter(isRealAdr);
|
|
64
70
|
return adrs.length > 0 ? "filled" : "untouched";
|
|
65
71
|
}
|
|
66
72
|
// Special case: contracts directory — filled as soon as one .md is not the raw port-contract template.
|
|
@@ -77,9 +83,22 @@ export function artifactState(missionDir, a) {
|
|
|
77
83
|
const template = readFileSync(join(TEMPLATES, "mission", a.templateKey), "utf8");
|
|
78
84
|
if (content.trim() === template.trim())
|
|
79
85
|
return "untouched";
|
|
86
|
+
if ((content.match(PLACEHOLDER) || []).length >= 3)
|
|
87
|
+
return "in-progress";
|
|
88
|
+
// Divergence guard: a deliverable is "filled" only when it departs meaningfully from
|
|
89
|
+
// the scaffold. Templates with few placeholders (decision-matrix, execution-topology)
|
|
90
|
+
// cannot lean on the placeholder floor, so a one-byte interior edit would otherwise
|
|
91
|
+
// pass. Require several lines of genuinely new content beyond the template. Calibrated
|
|
92
|
+
// against the reference mission (its lightest fill adds 5 lines / 215 words).
|
|
93
|
+
const lines = (s) => s.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
|
|
94
|
+
const templateLines = new Set(lines(template));
|
|
95
|
+
const added = lines(content).filter((l) => !templateLines.has(l));
|
|
96
|
+
const addedWords = added.reduce((n, l) => n + l.split(/\s+/).filter(Boolean).length, 0);
|
|
97
|
+
if (added.length < 3 || addedWords < 20)
|
|
98
|
+
return "in-progress";
|
|
99
|
+
return "filled";
|
|
80
100
|
}
|
|
81
|
-
|
|
82
|
-
if (placeholders >= 3)
|
|
101
|
+
if ((content.match(PLACEHOLDER) || []).length >= 3)
|
|
83
102
|
return "in-progress";
|
|
84
103
|
return "filled";
|
|
85
104
|
}
|
|
@@ -90,7 +109,7 @@ export function analyze(missionDir) {
|
|
|
90
109
|
});
|
|
91
110
|
const adrDir = join(missionDir, "adr");
|
|
92
111
|
const adrCount = existsSync(adrDir)
|
|
93
|
-
? readdirSync(adrDir).filter(
|
|
112
|
+
? readdirSync(adrDir).filter(isRealAdr).length
|
|
94
113
|
: 0;
|
|
95
114
|
const firstIncomplete = phases.find((p) => !p.complete);
|
|
96
115
|
return { phases, adrCount, currentPhase: firstIncomplete ? firstIncomplete.spec.label : "all gates passed" };
|
package/dist/lib/tools.js
CHANGED
|
@@ -46,11 +46,16 @@ const skillBody = (s) => [
|
|
|
46
46
|
"This skill helps you *apply* the rules; it does not enforce them. `runward check --strict` is the sole authority and verifies the manifest deterministically. A rule surfaced here but not accounted for still fails the gate.",
|
|
47
47
|
"",
|
|
48
48
|
].join("\n");
|
|
49
|
+
/** A YAML-safe double-quoted scalar. The description embeds the `when` trigger, which carries
|
|
50
|
+
* colons (and apostrophes) — left bare, a spec-conformant YAML parser (PyYAML safe_load,
|
|
51
|
+
* js-yaml) rejects the frontmatter, even though today's lenient line-based harness readers
|
|
52
|
+
* tolerate it. Double quotes take colons and apostrophes without escaping. */
|
|
53
|
+
const yamlStr = (v) => `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
49
54
|
/** SKILL.md open-format: name + description (the relevance trigger) + body. */
|
|
50
55
|
const skillMd = (s) => [
|
|
51
56
|
"---",
|
|
52
57
|
`name: runward-${s.phase}`,
|
|
53
|
-
`description: Runward ${s.label} craft rules. Use when ${s.when}
|
|
58
|
+
`description: ${yamlStr(`Runward ${s.label} craft rules. Use when ${s.when}.`)}`,
|
|
54
59
|
"---",
|
|
55
60
|
"",
|
|
56
61
|
skillBody(s),
|
|
@@ -110,7 +115,7 @@ export const TOOL_PROFILES = [
|
|
|
110
115
|
label: "Continue.dev (.continue/rules/runward-*)",
|
|
111
116
|
files: (root) => PHASE_SKILLS.map((s) => ({
|
|
112
117
|
path: join(root, ".continue", "rules", `runward-${s.phase}.md`),
|
|
113
|
-
content: ["---", `name: Runward ${s.label} craft`, `description: Use when ${s.when}
|
|
118
|
+
content: ["---", `name: ${yamlStr(`Runward ${s.label} craft`)}`, `description: ${yamlStr(`Use when ${s.when}.`)}`, "alwaysApply: false", "---", "", skillBody(s)].join("\n"),
|
|
114
119
|
})),
|
|
115
120
|
},
|
|
116
121
|
{
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# EU AI Act — Annex IV technical documentation — assessment-readiness draft
|
|
2
|
+
|
|
3
|
+
> **Draft, incomplete — not a conformity assessment.** Assembled by `runward compliance eu-ai-act` on 2026-07-13,
|
|
4
|
+
> deterministically from ratified engineering artifacts (no model call). High-risk obligations bind from
|
|
5
|
+
> **2 August 2026** (Annex III). This populates Annex IV Point 2 (design & validation) and the design-rationale
|
|
6
|
+
> history; it does **not** satisfy art. 12 runtime logging, and it is not a signed declaration of conformity.
|
|
7
|
+
> Verify against the Official Journal text before filing.
|
|
8
|
+
|
|
9
|
+
## Annex IV coverage map
|
|
10
|
+
|
|
11
|
+
| Annex IV point | runward supplies | Required from the provider |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| 1. General description | UI, HW/SW/firmware notes | intended purpose, provider, versioning, distribution |
|
|
14
|
+
| 2. Elements & development | **architecture, validation procedures + metrics, cybersecurity (manifest + rubric + threat model); design choices, alternatives, assumptions, pre-determined changes = the ADR journal** | third-party sourcing/licensing, sign-off on test logs |
|
|
15
|
+
| 3. Monitoring & control | accuracy characterization, input-data specs, oversight tooling | fundamental-rights / discrimination risk sourcing |
|
|
16
|
+
| 4. Performance metrics | metric-choice justification (rubric) | — |
|
|
17
|
+
| 5. Risk management (art. 9) | technical inputs (threat model, testing) | **risk acceptance / RMS governance** |
|
|
18
|
+
| 6. Lifecycle changes | engineering change record (ADR journal) | release/change-management governance |
|
|
19
|
+
| 7. Harmonised standards | technical notes | **standards selection** (compliance strategy) |
|
|
20
|
+
| 8. Declaration of conformity | — | **signed legal act (art. 47)** |
|
|
21
|
+
| 9. Post-market monitoring | telemetry/logging backbone | **post-market monitoring plan (art. 72)** |
|
|
22
|
+
|
|
23
|
+
## Point 2 — design decisions (ADR journal, near-verbatim to the Annex IV requirement)
|
|
24
|
+
|
|
25
|
+
| ADR | Status |
|
|
26
|
+
|---|---|
|
|
27
|
+
| ADR-0001: single orchestrator, sequential triage (`ADR-0001-single-orchestrator.md`) | accepted |
|
|
28
|
+
| ADR-0002: deterministic guard on model-extracted fields (`ADR-0002-deterministic-guard-on-extracted-fields.md`) | accepted |
|
|
29
|
+
|
|
30
|
+
## Agentic-risk coverage (OWASP ASI → Point 2 cybersecurity / Point 5 risk)
|
|
31
|
+
|
|
32
|
+
| ASI | Risk | Rules addressing it |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| ASI01 | Agent Goal Hijack | `frontier-deterministic-boundary`, `hexa-move-deterministic-out`, `security-prompt-injection` |
|
|
35
|
+
| ASI02 | Tool Misuse & Exploitation | `checklist-pre-production-security`, `hexa-move-deterministic-out`, `resilience-fail-open`, `security-code-execution-sandbox`, `security-tool-change-reapproval`, `tools-registry-pattern`, `tools-scope-atomicity` |
|
|
36
|
+
| ASI03 | Identity & Privilege Abuse | `checklist-pre-production-security`, `config-secrets-boundary`, `tools-registry-pattern` |
|
|
37
|
+
| ASI04 | Agentic Supply Chain Vulnerabilities | `contracts-governance`, `resilience-multi-provider-fallback`, `security-mcp-server-pinning`, `security-tool-change-reapproval` |
|
|
38
|
+
| ASI05 | Unexpected Code Execution | `security-code-execution-sandbox` |
|
|
39
|
+
| ASI06 | Memory & Context Poisoning | `checklist-pre-production-security`, `data-memory-provenance`, `patterns-memory-router-tiered`, `security-prompt-injection`, `state-event-sourcing` |
|
|
40
|
+
| ASI07 | Insecure Inter-Agent Communication | `contracts-governance` |
|
|
41
|
+
| ASI08 | Cascading Failures | `async-job-guardrails`, `checklist-pre-production-resilience`, `eval-loop`, `resilience-fail-open`, `resilience-multi-provider-fallback`, `resilience-retry-backoff`, `scaling-distributed-rate-limiting` |
|
|
42
|
+
| ASI09 | Human-Agent Trust Exploitation | `security-human-agent-trust` |
|
|
43
|
+
| ASI10 | Rogue Agents | `config-secrets-boundary`, `scaling-distributed-rate-limiting`, `security-mcp-server-pinning` |
|
|
44
|
+
|
|
45
|
+
## Control-implementation status (feeds Point 2 validation)
|
|
46
|
+
|
|
47
|
+
| Rule | Status | Evidence | Phase |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `contracts-governance` | applied | §3 TriageRecord v1.0 — versioned, additive, tolerant reader, fail-closed; contracts/ | Architect |
|
|
50
|
+
| `hexa-architecture` | applied | §2 pure triage domain, four ports; code/src/core/ | Architect |
|
|
51
|
+
| `hexa-adapter-pattern` | applied | §3 every dependency behind a port; code/src/adapters/ | Architect |
|
|
52
|
+
| `hexa-typescript-native` | n/a | language deliberately left open at this note (§5); locked at floor kickoff (ADR-0003 pending) | Architect |
|
|
53
|
+
| `process-adr-and-journal` | applied | adr/ADR-0001, adr/ADR-0002 — dated decisions with reevaluation triggers | Architect |
|
|
54
|
+
| `security-mcp-server-pinning` | n/a | the floor consumes no MCP or external tool server; tools are in-process and deterministic | Architect |
|
|
55
|
+
| `topology-port-placement-mapped` | applied | §2 map — all four ports placed; the two non-in-app placements (ModelPort → managed vendor runtime under an approved deployment, RoutingPort → existing ticketing infra) are recorded in architecture.md §2 and the legacy-integration note | Topology |
|
|
56
|
+
| `topology-sovereignty-by-data-class` | applied | §2 map — a data class and a sovereignty level per port; request text is bound to the approved model deployment (residency), the TriageRecord is kept internal | Topology |
|
|
57
|
+
| `topology-trace-export-decision` | n/a | the floor exports no execution traces to a third party; observability is in-app structured logs per governance/observability-schema.md | Topology |
|
|
58
|
+
| `topology-usage-registry-present` | applied | §3 usage registry — the single prod deployment with its risk class, data classes, action scopes and owner | Topology |
|
|
59
|
+
| `frontier-deterministic-boundary` | applied | code/src/core/domain/guard.ts — every action-bearing field recomputed/verified (ADR-0002), fail-closed | Floor |
|
|
60
|
+
| `hexa-move-deterministic-out` | applied | code/src/core/domain/guard.ts + keyword classifier — classification and validation are deterministic | Floor |
|
|
61
|
+
| `config-secrets-boundary` | n/a | the illustrative floor runs the deterministic keyword classifier; no provider secret is read in this example code | Floor |
|
|
62
|
+
| `provider-llm-auto-detection` | n/a | only the deterministic keyword adapter ships here; no real provider to auto-detect | Floor |
|
|
63
|
+
| `security-prompt-injection` | applied | threat-model §3 + ADR-0002 — model-proposed values never act; request text is data, not instruction | Floor |
|
|
64
|
+
| `hexa-architecture` | applied | code/src/core/ pure domain behind four ports | Floor |
|
|
65
|
+
| `hexa-adapter-pattern` | applied | code/src/adapters/ — mailbox/web, keyword-model, routing, log behind ports | Floor |
|
|
66
|
+
| `provider-no-crash-missing-env` | applied | code/src/adapters/keyword-model.adapter.ts — deterministic fallback runs with no key | Floor |
|
|
67
|
+
| `state-event-sourcing` | applied | code/src/adapters/in-memory-triage-log.adapter.ts — append-only, keyed by request ID | Floor |
|
|
68
|
+
| `tools-scope-atomicity` | applied | architecture §2 middleware chain + approval on RoutingPort for compliance records | Floor |
|
|
69
|
+
| `eval-loop` | applied | evaluation-rubric.md — abstention scenarios, guard-escalation rate watched off the hot path | Govern |
|
|
70
|
+
| `security-prompt-injection` | applied | §3 guardrails — untrusted request text is data; deterministic guard before RoutingPort (ADR-0002) | Govern |
|
|
71
|
+
| `config-secrets-boundary` | n/a | the illustrative floor reads no provider secret (deterministic keyword classifier) | Govern |
|
|
72
|
+
| `resilience-fail-open` | applied | §3 — sensitive routing fails closed; the guard rejects on doubt (ADR-0002) | Govern |
|
|
73
|
+
| `resilience-multi-provider-fallback` | n/a | single deterministic classifier; no second provider in this floor | Govern |
|
|
74
|
+
| `resilience-retry-backoff` | n/a | in-memory adapters; no external call to retry in the shipped floor | Govern |
|
|
75
|
+
| `async-job-guardrails` | n/a | synchronous request triage; no background jobs at the floor | Govern |
|
|
76
|
+
| `security-mcp-server-pinning` | n/a | no MCP or external tool server consumed at the floor | Govern |
|
|
77
|
+
| `security-tool-change-reapproval` | n/a | tools are in-process and deterministic; no signed external tool to re-approve | Govern |
|
|
78
|
+
| `data-memory-provenance` | n/a | no persistent memory; each request is triaged independently (named deferral) | Govern |
|
|
79
|
+
| `security-code-execution-sandbox` | n/a | the floor runs no model-generated or tool-invoked code; the classifier is deterministic in-process code and RoutingPort calls a typed ticketing API, not code | Govern |
|
|
80
|
+
| `security-human-agent-trust` | applied | §3 — each TriageRecord field carries a provenance marker (computed / verified / model-proposed); RoutingPort approval on a compliance-flagged record shows provenance before the human decides (ADR-0002) | Govern |
|
|
81
|
+
|
|
82
|
+
## Required from the provider (runward cannot produce this)
|
|
83
|
+
|
|
84
|
+
- **Point 1** general description (intended purpose, provider, versioning) · **Point 5** RMS governance & risk acceptance (art. 9).
|
|
85
|
+
- **Point 7** harmonised-standards selection · **Point 8** the signed **EU declaration of conformity** (art. 47) · **Point 9** the **post-market monitoring plan** (art. 72).
|
|
86
|
+
- **Art. 12 runtime event logs** — produced by the running system, not by runward.
|
|
87
|
+
|
|
88
|
+
_Engineering framing, not legal advice; Annex IV wording moves — confirm against the Official Journal (Reg. (EU) 2024/1689) before filing._
|
|
89
|
+
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
{
|
|
2
|
+
"component-definition": {
|
|
3
|
+
"uuid": "748fac1c-a21c-5282-8140-b77323a0e7da",
|
|
4
|
+
"metadata": {
|
|
5
|
+
"title": "runward — agentic-security control evidence (request-triage)",
|
|
6
|
+
"last-modified": "2026-07-13T00:00:00Z",
|
|
7
|
+
"version": "2026-07-13",
|
|
8
|
+
"oscal-version": "1.1.2",
|
|
9
|
+
"remarks": "Assessment-readiness DRAFT, assembled deterministically by runward from ratified engineering artifacts (rule to OWASP ASI mapping, conformance manifest, ADR journal). Supporting evidence only — NOT a compliance claim, NOT a certification, NOT a conformity assessment. Applicability, risk acceptance and management sign-off are the operator's."
|
|
10
|
+
},
|
|
11
|
+
"components": [
|
|
12
|
+
{
|
|
13
|
+
"uuid": "78f7623e-fce0-5c98-a040-96a5f5a97e1f",
|
|
14
|
+
"type": "software",
|
|
15
|
+
"title": "request-triage",
|
|
16
|
+
"description": "The agentic system governed by this runward mission.",
|
|
17
|
+
"control-implementations": [
|
|
18
|
+
{
|
|
19
|
+
"uuid": "f8f94d50-a2a4-575a-a0cb-d993e50693ca",
|
|
20
|
+
"source": "https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/",
|
|
21
|
+
"description": "OWASP Top 10 for Agentic Applications (ASI01–ASI10) coverage, derived from the mission's craft-rule mapping and conformance manifest.",
|
|
22
|
+
"implemented-requirements": [
|
|
23
|
+
{
|
|
24
|
+
"uuid": "10e10f8e-4f67-5a34-94df-7efaa113769f",
|
|
25
|
+
"control-id": "asi-01",
|
|
26
|
+
"description": "ASI01 Agent Goal Hijack. Addressed by rules: frontier-deterministic-boundary, hexa-move-deterministic-out, security-prompt-injection.",
|
|
27
|
+
"props": [
|
|
28
|
+
{
|
|
29
|
+
"name": "implementation-status",
|
|
30
|
+
"value": "implemented"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"links": [
|
|
34
|
+
{
|
|
35
|
+
"href": "./iso-42001-readiness.md",
|
|
36
|
+
"rel": "reference",
|
|
37
|
+
"text": "runward assessment-readiness draft"
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"uuid": "bb043f31-d856-50d7-8e07-6f7ab9b517e4",
|
|
43
|
+
"control-id": "asi-02",
|
|
44
|
+
"description": "ASI02 Tool Misuse & Exploitation. Addressed by rules: checklist-pre-production-security, hexa-move-deterministic-out, resilience-fail-open, security-code-execution-sandbox, security-tool-change-reapproval, tools-registry-pattern, tools-scope-atomicity.",
|
|
45
|
+
"props": [
|
|
46
|
+
{
|
|
47
|
+
"name": "implementation-status",
|
|
48
|
+
"value": "partial"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"links": [
|
|
52
|
+
{
|
|
53
|
+
"href": "./iso-42001-readiness.md",
|
|
54
|
+
"rel": "reference",
|
|
55
|
+
"text": "runward assessment-readiness draft"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"uuid": "bfb244c6-c14d-54d8-b2a7-9efc5fa9b8aa",
|
|
61
|
+
"control-id": "asi-03",
|
|
62
|
+
"description": "ASI03 Identity & Privilege Abuse. Addressed by rules: checklist-pre-production-security, config-secrets-boundary, tools-registry-pattern.",
|
|
63
|
+
"props": [
|
|
64
|
+
{
|
|
65
|
+
"name": "implementation-status",
|
|
66
|
+
"value": "partial"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"links": [
|
|
70
|
+
{
|
|
71
|
+
"href": "./iso-42001-readiness.md",
|
|
72
|
+
"rel": "reference",
|
|
73
|
+
"text": "runward assessment-readiness draft"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"uuid": "ba1a82be-4b49-5b9f-a017-2d9a1c7ba34a",
|
|
79
|
+
"control-id": "asi-04",
|
|
80
|
+
"description": "ASI04 Agentic Supply Chain Vulnerabilities. Addressed by rules: contracts-governance, resilience-multi-provider-fallback, security-mcp-server-pinning, security-tool-change-reapproval.",
|
|
81
|
+
"props": [
|
|
82
|
+
{
|
|
83
|
+
"name": "implementation-status",
|
|
84
|
+
"value": "partial"
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"links": [
|
|
88
|
+
{
|
|
89
|
+
"href": "./iso-42001-readiness.md",
|
|
90
|
+
"rel": "reference",
|
|
91
|
+
"text": "runward assessment-readiness draft"
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"uuid": "c3876a6c-d8fe-51ca-b1f6-cff5da50c134",
|
|
97
|
+
"control-id": "asi-05",
|
|
98
|
+
"description": "ASI05 Unexpected Code Execution. Addressed by rules: security-code-execution-sandbox.",
|
|
99
|
+
"props": [
|
|
100
|
+
{
|
|
101
|
+
"name": "implementation-status",
|
|
102
|
+
"value": "partial"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"links": [
|
|
106
|
+
{
|
|
107
|
+
"href": "./iso-42001-readiness.md",
|
|
108
|
+
"rel": "reference",
|
|
109
|
+
"text": "runward assessment-readiness draft"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"uuid": "02c247e9-35be-5009-bc90-406e39c97f01",
|
|
115
|
+
"control-id": "asi-06",
|
|
116
|
+
"description": "ASI06 Memory & Context Poisoning. Addressed by rules: checklist-pre-production-security, data-memory-provenance, patterns-memory-router-tiered, security-prompt-injection, state-event-sourcing.",
|
|
117
|
+
"props": [
|
|
118
|
+
{
|
|
119
|
+
"name": "implementation-status",
|
|
120
|
+
"value": "partial"
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"links": [
|
|
124
|
+
{
|
|
125
|
+
"href": "./iso-42001-readiness.md",
|
|
126
|
+
"rel": "reference",
|
|
127
|
+
"text": "runward assessment-readiness draft"
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"uuid": "d1714857-8222-50ed-8fe4-4c106d904621",
|
|
133
|
+
"control-id": "asi-07",
|
|
134
|
+
"description": "ASI07 Insecure Inter-Agent Communication. Addressed by rules: contracts-governance.",
|
|
135
|
+
"props": [
|
|
136
|
+
{
|
|
137
|
+
"name": "implementation-status",
|
|
138
|
+
"value": "implemented"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"links": [
|
|
142
|
+
{
|
|
143
|
+
"href": "./iso-42001-readiness.md",
|
|
144
|
+
"rel": "reference",
|
|
145
|
+
"text": "runward assessment-readiness draft"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"uuid": "eb4325d7-8950-5078-b536-30a62da4006b",
|
|
151
|
+
"control-id": "asi-08",
|
|
152
|
+
"description": "ASI08 Cascading Failures. Addressed by rules: async-job-guardrails, checklist-pre-production-resilience, eval-loop, resilience-fail-open, resilience-multi-provider-fallback, resilience-retry-backoff, scaling-distributed-rate-limiting.",
|
|
153
|
+
"props": [
|
|
154
|
+
{
|
|
155
|
+
"name": "implementation-status",
|
|
156
|
+
"value": "partial"
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"links": [
|
|
160
|
+
{
|
|
161
|
+
"href": "./iso-42001-readiness.md",
|
|
162
|
+
"rel": "reference",
|
|
163
|
+
"text": "runward assessment-readiness draft"
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"uuid": "56e6885a-fd37-599f-b5dc-5cfd4c6cf582",
|
|
169
|
+
"control-id": "asi-09",
|
|
170
|
+
"description": "ASI09 Human-Agent Trust Exploitation. Addressed by rules: security-human-agent-trust.",
|
|
171
|
+
"props": [
|
|
172
|
+
{
|
|
173
|
+
"name": "implementation-status",
|
|
174
|
+
"value": "implemented"
|
|
175
|
+
}
|
|
176
|
+
],
|
|
177
|
+
"links": [
|
|
178
|
+
{
|
|
179
|
+
"href": "./iso-42001-readiness.md",
|
|
180
|
+
"rel": "reference",
|
|
181
|
+
"text": "runward assessment-readiness draft"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"uuid": "7a1bdaf4-ea39-5b53-b2a6-2b90660c38be",
|
|
187
|
+
"control-id": "asi-10",
|
|
188
|
+
"description": "ASI10 Rogue Agents. Addressed by rules: config-secrets-boundary, scaling-distributed-rate-limiting, security-mcp-server-pinning.",
|
|
189
|
+
"props": [
|
|
190
|
+
{
|
|
191
|
+
"name": "implementation-status",
|
|
192
|
+
"value": "partial"
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"links": [
|
|
196
|
+
{
|
|
197
|
+
"href": "./iso-42001-readiness.md",
|
|
198
|
+
"rel": "reference",
|
|
199
|
+
"text": "runward assessment-readiness draft"
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "runward",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "After the spec: ship and run. A delivery framework for agentic systems — floor first, evolution on evidence, governance from day zero, handover with proof.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentic",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"@types/node": "^22.0.0",
|
|
50
50
|
"ajv": "^8.20.0",
|
|
51
51
|
"ajv-formats": "^3.0.1",
|
|
52
|
+
"js-yaml": "^5.2.1",
|
|
52
53
|
"typescript": "^5.5.0"
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -8,12 +8,14 @@ Use this workflow once framing is decided and structure must follow: "how do we
|
|
|
8
8
|
|
|
9
9
|
- The framing note: floor/target split, success criterion, hard constraints, presumed boundaries.
|
|
10
10
|
- The `mission/architecture.md`, `mission/port-contract.md`, and `mission/adr/ADR-0000-template.md` templates.
|
|
11
|
+
- The `mission/decision-matrix.md` template (the structuring defaults and the trigger that switches each).
|
|
11
12
|
- The `mission/shared-bricks.md` and `mission/execution-topology.md` templates (the infrastructure vision).
|
|
12
13
|
|
|
13
14
|
## Outputs
|
|
14
15
|
|
|
15
16
|
- A light architecture note.
|
|
16
17
|
- The port list with contracts and the integration protocol.
|
|
18
|
+
- The decision matrix adopted for the mission: each structuring choice with its sober default and the objective trigger that switches it (`mission/decision-matrix.md`).
|
|
17
19
|
- The execution-topology note: each port placed behind its location family, with data class and sovereignty.
|
|
18
20
|
- One ADR per structuring decision, including each non-in-app placement.
|
|
19
21
|
|
|
@@ -41,6 +43,7 @@ Use this workflow once framing is decided and structure must follow: "how do we
|
|
|
41
43
|
## Definition of Done
|
|
42
44
|
|
|
43
45
|
- Architecture note produced, boundaries first, stack open.
|
|
46
|
+
- Decision matrix adopted for the mission: every structuring choice carries a sober default and an objective trigger; the positions taken so far are recorded (`mission/decision-matrix.md`).
|
|
44
47
|
- Every port named with contract and initial version; integration protocol stated.
|
|
45
48
|
- Execution-topology note produced: every port placed behind a location family, with its data class(es) and sovereignty; non-in-app placements carry an ADR; the usage registry is seeded.
|
|
46
49
|
- One ADR per structuring choice, locked before the note mentions it.
|
|
@@ -13,6 +13,7 @@ Use this workflow at the start of any agentic-system mission, or whenever the pe
|
|
|
13
13
|
## Outputs
|
|
14
14
|
|
|
15
15
|
- A framing note (use the `mission/framing.md` template).
|
|
16
|
+
- A steering contract (use the `mission/mission-contract.md` template): what is delivered, on what condition it is accepted, and along which roadmap — filled with the sponsor, tying the success criterion to the deliverables and the milestones.
|
|
16
17
|
- The floor/target split with named deferrals.
|
|
17
18
|
- Presumed architecture boundaries — ports and integration protocol, stack left open.
|
|
18
19
|
|
|
@@ -55,6 +56,7 @@ Lock any truly structuring decision through `decision-loop` before committing it
|
|
|
55
56
|
## Definition of Done
|
|
56
57
|
|
|
57
58
|
- Framing note produced: problem, value, observable success criterion, hard constraints — one page.
|
|
59
|
+
- Steering contract filled with the sponsor: the success criterion tied to the deliverables and to what closes each engagement (`mission/mission-contract.md`).
|
|
58
60
|
- Floor perimeter listed, every deferral named with its trigger.
|
|
59
61
|
- Presumed boundaries stated; language and topology explicitly left open.
|
|
60
62
|
- Note reviewed via `review` before circulation.
|