@kontourai/flow-agents 0.4.0 → 1.0.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/.github/workflows/kit-gates-demo.yml +171 -0
- package/CHANGELOG.md +43 -0
- package/CONTEXT.md +1 -1
- package/README.md +13 -2
- package/build/src/cli/flow-kit.js +175 -6
- package/build/src/cli/validate-source-tree.js +19 -2
- package/build/src/flow-kit/validate.js +98 -0
- package/build/src/runtime-adapters.js +1 -1
- package/build/src/tools/validate-source-tree.js +3 -2
- package/context/scripts/hooks/config-protection.js +217 -15
- package/docs/fixture-ownership.md +2 -1
- package/docs/index.md +9 -1
- package/docs/kit-authoring-guide.md +126 -0
- package/docs/knowledge-kit.md +69 -0
- package/docs/vision.md +22 -0
- package/evals/fixtures/kit-conformance-levels/k0-flows-only/flows/review.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k0-flows-only/kit.json +13 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/docs/README.md +3 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/flows/build.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k1-agent-extension/kit.json +20 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/docs/README.md +3 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/eval-suites/contract-suite/suite.test.js +1 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/flows/synthesize.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/k2-with-evals/kit.json +27 -0
- package/evals/fixtures/kit-conformance-levels/third-party-extension/flows/review.flow.json +26 -0
- package/evals/fixtures/kit-conformance-levels/third-party-extension/kit.json +19 -0
- package/evals/integration/test_activate_npx_context.sh +134 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +163 -0
- package/evals/integration/test_hook_category_behaviors.sh +51 -0
- package/evals/integration/test_kit_conformance_levels.sh +209 -0
- package/evals/run.sh +2 -0
- package/kits/catalog.json +6 -0
- package/kits/knowledge/adapters/default-store/index.js +2 -2
- package/kits/knowledge/adapters/flow-runner/entity-extractor.js +194 -0
- package/kits/knowledge/adapters/flow-runner/index.js +349 -0
- package/kits/knowledge/adapters/obsidian-store/README.md +141 -0
- package/kits/knowledge/adapters/obsidian-store/demo.js +181 -0
- package/kits/knowledge/adapters/obsidian-store/index.js +868 -0
- package/kits/knowledge/adapters/shared/codec.js +325 -0
- package/kits/knowledge/docs/store-contract.md +72 -0
- package/kits/knowledge/evals/entities/demo-acme.js +125 -0
- package/kits/knowledge/evals/entities/suite.test.js +722 -0
- package/kits/knowledge/kit.json +10 -0
- package/kits/release-evidence/fixtures/claims/README.md +14 -0
- package/kits/release-evidence/fixtures/claims/fail-rejected-release.trust.json +22 -0
- package/kits/release-evidence/fixtures/claims/pass-trusted-release.trust.json +22 -0
- package/kits/release-evidence/flows/release-evidence.flow.json +38 -0
- package/kits/release-evidence/kit.json +13 -0
- package/package.json +1 -1
- package/packaging/conformance/fixtures/config-protection--allow-no-verify-in-string.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-git-no-verify.json +23 -0
- package/scripts/hooks/config-protection.js +217 -15
- package/src/cli/flow-kit.ts +162 -5
- package/src/cli/validate-source-tree.ts +7 -1
- package/src/flow-kit/validate.ts +127 -0
- package/src/runtime-adapters.ts +1 -1
- package/src/tools/validate-source-tree.ts +3 -2
|
@@ -84,7 +84,8 @@ const fixtureOwnerPolicies = new Map([
|
|
|
84
84
|
["evals/fixtures/backlog-provider-settings", { owners: ["evals/integration/test_effective_backlog_settings.sh"], classification: "settings precedence fixtures" }],
|
|
85
85
|
["evals/fixtures/builder-kit-workflow-state", { owners: ["evals/static/test_workflow_skills.sh"], classification: "Builder Kit workflow-state fixtures" }],
|
|
86
86
|
["evals/fixtures/console-learning-projection", { owners: ["evals/integration/test_console_learning_projection.sh"], classification: "console learning projection fixtures" }],
|
|
87
|
-
["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
|
|
87
|
+
["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/integration/test_activate_npx_context.sh", "evals/integration/test_flow_kit_install_git.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
|
|
88
|
+
["evals/fixtures/kit-conformance-levels", { owners: ["evals/integration/test_kit_conformance_levels.sh"], classification: "K-level conformance and consumer-target derivation fixtures" }],
|
|
88
89
|
["evals/fixtures/hook-influence", { owners: ["evals/integration/test_hook_influence_cases.sh", "evals/static/test_workflow_skills.sh", "scripts/validate-hook-influence-cases.js"], classification: "hook influence behavioral cases" }],
|
|
89
90
|
["evals/fixtures/pull-work-provider", { owners: ["evals/integration/test_pull_work_provider.sh"], classification: "work item provider normalization fixtures" }],
|
|
90
91
|
["evals/fixtures/pull-work-wip-shepherding", { owners: ["evals/static/test_workflow_skills.sh"], classification: "WIP shepherding state fixtures" }],
|
|
@@ -359,7 +360,7 @@ function validateKits(reporter) {
|
|
|
359
360
|
if (flowSchemaPath && fs.existsSync(flowSchemaPath))
|
|
360
361
|
console.log(fs.existsSync(localCli) ? `info: validating kit Flow Definitions with Flow CLI at ${localCli}` : `warning: Flow validator unavailable; source-tree check only verifies Flow Definition top-level shape`);
|
|
361
362
|
else
|
|
362
|
-
console.log("warning: Flow schema not configured; source-tree check only verifies Flow Definition top-level shape. Set FLOW_CLI_ROOT to enable Flow CLI validation.");
|
|
363
|
+
console.log("warning: Flow schema not configured; source-tree check only verifies Flow Definition top-level shape. Set FLOW_CLI_ROOT to enable Flow CLI validation. Container validation (kit.json core fields) will delegate to 'flow validate-kit' from @kontourai/flow when FLOW_CLI_ROOT is available.");
|
|
363
364
|
kits.forEach((entry, index) => {
|
|
364
365
|
const kitText = typeof entry === "string" ? entry : ["path", "directory", "dir", "id", "name"].map((key) => entry?.[key]).find((value) => typeof value === "string" && value);
|
|
365
366
|
if (!kitText) {
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* Blocks modifications to linter/formatter config files.
|
|
6
6
|
* Steers the agent to fix source code instead of weakening configs.
|
|
7
7
|
*
|
|
8
|
+
* Also blocks git verification-bypass flags in actual flag positions only.
|
|
9
|
+
* Text that merely mentions the flag inside quoted strings or prose is allowed.
|
|
10
|
+
*
|
|
8
11
|
* Exit codes: 0 = allow, 2 = block
|
|
9
12
|
*/
|
|
10
13
|
|
|
@@ -25,6 +28,195 @@ const PROTECTED_FILES = new Set([
|
|
|
25
28
|
'.markdownlint.json', '.markdownlint.yaml', '.markdownlintrc',
|
|
26
29
|
]);
|
|
27
30
|
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Shell-aware tokenizer
|
|
33
|
+
//
|
|
34
|
+
// Splits a shell command string into tokens, respecting single/double quotes
|
|
35
|
+
// and backslash escapes. Quoted content stays inside its parent token so
|
|
36
|
+
// flag text inside a -m argument string is never matched as a flag.
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* tokenize(cmd) -- shell-aware tokenizer for a single command segment.
|
|
41
|
+
* Returns an array of unquoted token strings.
|
|
42
|
+
*/
|
|
43
|
+
function tokenize(cmd) {
|
|
44
|
+
const tokens = [];
|
|
45
|
+
let i = 0;
|
|
46
|
+
const len = cmd.length;
|
|
47
|
+
|
|
48
|
+
while (i < len) {
|
|
49
|
+
// Skip whitespace between tokens.
|
|
50
|
+
while (i < len && /\s/.test(cmd[i])) i++;
|
|
51
|
+
if (i >= len) break;
|
|
52
|
+
|
|
53
|
+
let token = '';
|
|
54
|
+
|
|
55
|
+
// Consume one token -- stop at unquoted whitespace.
|
|
56
|
+
while (i < len) {
|
|
57
|
+
const ch = cmd[i];
|
|
58
|
+
|
|
59
|
+
if (ch === '\\') {
|
|
60
|
+
// Backslash escape outside of quotes.
|
|
61
|
+
i++;
|
|
62
|
+
if (i < len) token += cmd[i++];
|
|
63
|
+
} else if (ch === "'") {
|
|
64
|
+
// Single-quoted string: no escape processing, read until closing quote.
|
|
65
|
+
i++;
|
|
66
|
+
while (i < len && cmd[i] !== "'") token += cmd[i++];
|
|
67
|
+
i++; // consume closing quote
|
|
68
|
+
} else if (ch === '"') {
|
|
69
|
+
// Double-quoted string: honour \" and \\ escape sequences.
|
|
70
|
+
i++;
|
|
71
|
+
while (i < len && cmd[i] !== '"') {
|
|
72
|
+
if (cmd[i] === '\\' && i + 1 < len && (cmd[i + 1] === '"' || cmd[i + 1] === '\\')) {
|
|
73
|
+
i++; // skip the backslash
|
|
74
|
+
token += cmd[i++];
|
|
75
|
+
} else {
|
|
76
|
+
token += cmd[i++];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
i++; // consume closing quote
|
|
80
|
+
} else if (/\s/.test(ch)) {
|
|
81
|
+
break; // end of token
|
|
82
|
+
} else {
|
|
83
|
+
token += ch;
|
|
84
|
+
i++;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (token.length > 0) tokens.push(token);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return tokens;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* splitSegments(cmd) -- split on shell connectors && || ; | outside of quotes.
|
|
96
|
+
*/
|
|
97
|
+
function splitSegments(cmd) {
|
|
98
|
+
const segments = [];
|
|
99
|
+
let i = 0;
|
|
100
|
+
const len = cmd.length;
|
|
101
|
+
let segStart = 0;
|
|
102
|
+
|
|
103
|
+
while (i < len) {
|
|
104
|
+
const ch = cmd[i];
|
|
105
|
+
|
|
106
|
+
if (ch === '\\') {
|
|
107
|
+
i += 2; // skip escaped character
|
|
108
|
+
} else if (ch === "'") {
|
|
109
|
+
i++;
|
|
110
|
+
while (i < len && cmd[i] !== "'") i++;
|
|
111
|
+
i++; // skip closing quote
|
|
112
|
+
} else if (ch === '"') {
|
|
113
|
+
i++;
|
|
114
|
+
while (i < len) {
|
|
115
|
+
if (cmd[i] === '\\' && i + 1 < len) { i += 2; continue; }
|
|
116
|
+
if (cmd[i] === '"') { i++; break; }
|
|
117
|
+
i++;
|
|
118
|
+
}
|
|
119
|
+
} else if (ch === '&' && i + 1 < len && cmd[i + 1] === '&') {
|
|
120
|
+
segments.push(cmd.slice(segStart, i).trim());
|
|
121
|
+
i += 2; segStart = i;
|
|
122
|
+
} else if (ch === '|' && i + 1 < len && cmd[i + 1] === '|') {
|
|
123
|
+
segments.push(cmd.slice(segStart, i).trim());
|
|
124
|
+
i += 2; segStart = i;
|
|
125
|
+
} else if (ch === ';') {
|
|
126
|
+
segments.push(cmd.slice(segStart, i).trim());
|
|
127
|
+
i++; segStart = i;
|
|
128
|
+
} else if (ch === '|') {
|
|
129
|
+
// single pipe
|
|
130
|
+
segments.push(cmd.slice(segStart, i).trim());
|
|
131
|
+
i++; segStart = i;
|
|
132
|
+
} else {
|
|
133
|
+
i++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const tail = cmd.slice(segStart).trim();
|
|
138
|
+
if (tail.length > 0) segments.push(tail);
|
|
139
|
+
return segments.filter(s => s.length > 0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Git global flags that consume a following argument value.
|
|
143
|
+
const GIT_GLOBAL_FLAGS_WITH_ARG = new Set(['-C', '-c', '--exec-path', '--git-dir', '--work-tree', '--namespace']);
|
|
144
|
+
// Git global flags that are standalone (no following argument).
|
|
145
|
+
const GIT_GLOBAL_FLAGS_STANDALONE = new Set([
|
|
146
|
+
'--version', '--help', '--html-path', '--man-path', '--info-path',
|
|
147
|
+
'-p', '--paginate', '-P', '--no-pager', '--no-replace-objects',
|
|
148
|
+
'--bare', '--literal-pathspecs', '--glob-pathspecs', '--noglob-pathspecs',
|
|
149
|
+
'--icase-pathspecs', '--no-optional-locks', '--list-cmds',
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* resolveGitSubcommand(tokens) -- walk past global git flags and return the
|
|
154
|
+
* subcommand token, or null if not determinable.
|
|
155
|
+
*/
|
|
156
|
+
function resolveGitSubcommand(tokens) {
|
|
157
|
+
let i = 1;
|
|
158
|
+
while (i < tokens.length) {
|
|
159
|
+
const t = tokens[i];
|
|
160
|
+
if (GIT_GLOBAL_FLAGS_WITH_ARG.has(t)) {
|
|
161
|
+
i += 2;
|
|
162
|
+
} else if (GIT_GLOBAL_FLAGS_STANDALONE.has(t)) {
|
|
163
|
+
i += 1;
|
|
164
|
+
} else if (t.startsWith('-')) {
|
|
165
|
+
i += 1; // unknown global flag -- skip conservatively
|
|
166
|
+
} else {
|
|
167
|
+
return { subcommand: t, flagsStart: i + 1 };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Flags for git commit that consume the immediately following token as a value.
|
|
174
|
+
const COMMIT_FLAGS_WITH_VALUE = new Set([
|
|
175
|
+
'-m', '--message', '-F', '--file', '-C', '-c',
|
|
176
|
+
'--author', '--date', '--fixup', '--squash', '--pathspec-from-file',
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
// Flags for git push that consume the following token as a value.
|
|
180
|
+
const PUSH_FLAGS_WITH_VALUE = new Set([
|
|
181
|
+
'--receive-pack', '--repo', '--push-option', '-o', '--recurse-submodules',
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
const BYPASS_NV = '--no-verify';
|
|
185
|
+
const BYPASS_N = '-n'; // short alias (commit only; on push -n means --dry-run)
|
|
186
|
+
|
|
187
|
+
function checkSegmentForBypass(tokens) {
|
|
188
|
+
if (tokens.length === 0 || tokens[0] !== 'git') return null;
|
|
189
|
+
const resolved = resolveGitSubcommand(tokens);
|
|
190
|
+
if (!resolved) return null;
|
|
191
|
+
const { subcommand, flagsStart } = resolved;
|
|
192
|
+
if (subcommand === 'commit') {
|
|
193
|
+
for (let i = flagsStart; i < tokens.length; i++) {
|
|
194
|
+
const t = tokens[i];
|
|
195
|
+
if (t === BYPASS_NV || t === BYPASS_N) return `git ${subcommand} ${t}`;
|
|
196
|
+
if (COMMIT_FLAGS_WITH_VALUE.has(t)) i++;
|
|
197
|
+
}
|
|
198
|
+
} else if (subcommand === 'push') {
|
|
199
|
+
for (let i = flagsStart; i < tokens.length; i++) {
|
|
200
|
+
const t = tokens[i];
|
|
201
|
+
if (t === BYPASS_NV) return `git ${subcommand} ${t}`;
|
|
202
|
+
if (PUSH_FLAGS_WITH_VALUE.has(t)) i++;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function checkCommandForBypass(command) {
|
|
209
|
+
if (typeof command !== 'string' || !command) return null;
|
|
210
|
+
if (!command.includes('git')) return null;
|
|
211
|
+
const segments = splitSegments(command);
|
|
212
|
+
for (const seg of segments) {
|
|
213
|
+
const tokens = tokenize(seg);
|
|
214
|
+
const result = checkSegmentForBypass(tokens);
|
|
215
|
+
if (result) return result;
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
28
220
|
function run(inputOrRaw, options = {}) {
|
|
29
221
|
if (options.truncated) {
|
|
30
222
|
return {
|
|
@@ -33,30 +225,40 @@ function run(inputOrRaw, options = {}) {
|
|
|
33
225
|
'Refusing to bypass config-protection on a truncated payload.',
|
|
34
226
|
};
|
|
35
227
|
}
|
|
36
|
-
|
|
37
228
|
let input;
|
|
38
229
|
try {
|
|
39
230
|
input = typeof inputOrRaw === 'string' ? JSON.parse(inputOrRaw) : inputOrRaw;
|
|
40
231
|
} catch { return { exitCode: 0 }; }
|
|
41
|
-
|
|
42
232
|
const filePath = input?.tool_input?.path || input?.tool_input?.file_path || '';
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
233
|
+
if (filePath) {
|
|
234
|
+
const basename = path.basename(filePath);
|
|
235
|
+
if (PROTECTED_FILES.has(basename)) {
|
|
236
|
+
return {
|
|
237
|
+
exitCode: 2,
|
|
238
|
+
stderr: `BLOCKED: Modifying ${basename} is not allowed. ` +
|
|
239
|
+
'Fix the source code to satisfy linter/formatter rules instead of ' +
|
|
240
|
+
'weakening the config. If this is a legitimate config change, ' +
|
|
241
|
+
'disable the config-protection hook temporarily.',
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const command = input?.tool_input?.command || '';
|
|
246
|
+
if (command) {
|
|
247
|
+
const bypass = checkCommandForBypass(command);
|
|
248
|
+
if (bypass) {
|
|
249
|
+
return {
|
|
250
|
+
exitCode: 2,
|
|
251
|
+
stderr: `BLOCKED: "${bypass}" bypasses git verification hooks. ` +
|
|
252
|
+
'Verification hooks enforce project quality gates and must not be opted out. ' +
|
|
253
|
+
'Fix the failing check instead of skipping it. ' +
|
|
254
|
+
'If the hook is genuinely misconfigured, correct the hook configuration directly.',
|
|
255
|
+
};
|
|
256
|
+
}
|
|
54
257
|
}
|
|
55
|
-
|
|
56
258
|
return { exitCode: 0 };
|
|
57
259
|
}
|
|
58
260
|
|
|
59
|
-
module.exports = { run };
|
|
261
|
+
module.exports = { run, tokenize, splitSegments, checkCommandForBypass };
|
|
60
262
|
|
|
61
263
|
// Stdin fallback for spawnSync execution
|
|
62
264
|
if (require.main === module) {
|
|
@@ -16,7 +16,8 @@ run `npm run validate:source --` and `npm run fixture:retirement-audit --`.
|
|
|
16
16
|
| `evals/fixtures/backlog-provider-settings` | settings precedence fixtures | `evals/integration/test_effective_backlog_settings.sh` | Keep while backlog provider settings resolution supports global defaults and project overrides. |
|
|
17
17
|
| `evals/fixtures/builder-kit-workflow-state` | Builder Kit workflow-state fixtures | `evals/static/test_workflow_skills.sh` | Keep while Builder Kit state contract and resume behavior are documented in workflow skill contracts. |
|
|
18
18
|
| `evals/fixtures/console-learning-projection` | console learning projection fixtures | `evals/integration/test_console_learning_projection.sh` | Keep while learning projection supports correction and open-route examples. |
|
|
19
|
-
| `evals/fixtures/flow-kit-repository` | Flow Kit repository contract fixtures | `evals/integration/test_flow_kit_repository.sh`, `evals/integration/test_local_flow_kit_install.sh`, `evals/integration/test_runtime_adapter_activation.sh`, `evals/static/test_workflow_skills.sh` | Keep valid and invalid cases paired with the Flow Kit repository contract. |
|
|
19
|
+
| `evals/fixtures/flow-kit-repository` | Flow Kit repository contract fixtures | `evals/integration/test_flow_kit_repository.sh`, `evals/integration/test_local_flow_kit_install.sh`, `evals/integration/test_runtime_adapter_activation.sh`, `evals/integration/test_activate_npx_context.sh`, `evals/integration/test_flow_kit_install_git.sh`, `evals/static/test_workflow_skills.sh` | Keep valid and invalid cases paired with the Flow Kit repository contract. |
|
|
20
|
+
| `evals/fixtures/kit-conformance-levels` | K-level conformance and consumer-target derivation fixtures | `evals/integration/test_kit_conformance_levels.sh` | Keep while K-level derivation, degradation invariant, and consumer-target badge rules are tested. |
|
|
20
21
|
| `evals/fixtures/hook-influence` | hook influence behavioral cases | `evals/integration/test_hook_influence_cases.sh`, `evals/static/test_workflow_skills.sh`, `scripts/validate-hook-influence-cases.js` | Keep while hook influence cases define agent guidance behavior. |
|
|
21
22
|
| `evals/fixtures/pull-work-provider` | work item provider normalization fixtures | `evals/integration/test_pull_work_provider.sh` | Keep while provider normalization preserves blockers, artifact refs, board membership, and freshness metadata. |
|
|
22
23
|
| `evals/fixtures/pull-work-wip-shepherding` | WIP shepherding state fixtures | `evals/static/test_workflow_skills.sh` | Keep while pull-work documents personal versus global WIP behavior. |
|
package/docs/index.md
CHANGED
|
@@ -19,6 +19,10 @@ title: Kontour Flow Agents
|
|
|
19
19
|
<strong>Evidence over confidence</strong>
|
|
20
20
|
<span>Hooks catch stop-short behavior, and important work ends with tests, browser checks, CI results, review findings, or an explicit <code>NOT_VERIFIED</code> gap — never just a confident summary.</span>
|
|
21
21
|
</section>
|
|
22
|
+
<section>
|
|
23
|
+
<strong>Flow Kits — workflow + output shape</strong>
|
|
24
|
+
<span>A kit bundles a workflow and its opinionated output shape as a validated, installable unit. Two reference kits ship today: Builder Kit (shaping → delivery pipeline) and Knowledge Kit (gated store with five pipeline flows, pluggable adapters, and an Obsidian rendering layer). <a href="kit-authoring-guide.html">Author your own</a> using the same path.</span>
|
|
25
|
+
</section>
|
|
22
26
|
</div>
|
|
23
27
|
|
|
24
28
|
## How it works
|
|
@@ -114,13 +118,17 @@ Use fix-bug. Reproduce the problem, diagnose root cause, implement the fix, and
|
|
|
114
118
|
<strong>Flow Kit Repository Contract</strong>
|
|
115
119
|
<span>Full validation rules, registry schema, activation diagnostics, and the install/update/force semantics.</span>
|
|
116
120
|
</a>
|
|
121
|
+
<a class="doc-card" href="knowledge-kit.html">
|
|
122
|
+
<strong>Knowledge Kit</strong>
|
|
123
|
+
<span>Gated knowledge storage with five pipeline flows, a representation-neutral store contract, default and Obsidian adapters, 198 tests, and a parameterized contract suite any adapter can run.</span>
|
|
124
|
+
</a>
|
|
117
125
|
<a class="doc-card" href="spec/runtime-hook-surface.html">
|
|
118
126
|
<strong>Runtime Hook Surface</strong>
|
|
119
127
|
<span>Canonical event taxonomy, four policy classes, conformance levels L0/L1/L2, and host mapping tables for adapter authors.</span>
|
|
120
128
|
</a>
|
|
121
129
|
<a class="doc-card" href="vision.html">
|
|
122
130
|
<strong>Vision and Direction</strong>
|
|
123
|
-
<span>
|
|
131
|
+
<span>The kits-as-ecosystem arc (authoring today → domain kits → registry → marketplace), TypeScript framework adapters, and Kontour Console as the unifying telemetry surface.</span>
|
|
124
132
|
</a>
|
|
125
133
|
<a class="doc-card" href="north-star.html">
|
|
126
134
|
<strong>North Star</strong>
|
|
@@ -164,6 +164,132 @@ For conflicts on re-install: if you install a different source with an existing
|
|
|
164
164
|
|
|
165
165
|
See the [Flow Kit Repository Contract](flow-kit-repository-contract.md) for the full validation rules, registry schema, activation diagnostics, and the install/update/force semantics.
|
|
166
166
|
|
|
167
|
+
## Layering: container vs. agent extension
|
|
168
|
+
|
|
169
|
+
A Flow Agents Kit is built on two distinct layers. Understanding the split helps you know which rules come from Flow and which come from Flow Agents.
|
|
170
|
+
|
|
171
|
+
### Layer 1: the Flow Kit container (Flow-owned)
|
|
172
|
+
|
|
173
|
+
The **container contract** is owned by [Kontour Flow](https://kontourai.github.io/flow/flow-kit-container). It governs:
|
|
174
|
+
|
|
175
|
+
- `kit.json` required fields: `schema_version` ("1.0"), `id` (kebab-case), `name`, `flows` (non-empty list with `path` entries).
|
|
176
|
+
- Optional core fields: `description`, `product_name`.
|
|
177
|
+
- Path rules: all declared paths must be relative, must not contain `..`, and must resolve inside the kit directory.
|
|
178
|
+
- The **extension model**: unknown top-level fields are consumer extensions; core validation ignores-but-permits them.
|
|
179
|
+
|
|
180
|
+
Container validation is surfaced in Flow's CLI as `flow validate-kit <kit-dir>`. Flow Agents delegates container validation to Flow when `FLOW_CLI_ROOT` is configured; without it, Flow Agents applies the same rules internally.
|
|
181
|
+
|
|
182
|
+
For the authoritative container spec and JSON Schema, see [kontourai/flow#67](https://github.com/kontourai/flow/pull/67) (the spec PR) and the published `schemas/flow-kit-container.schema.json` in the `@kontourai/flow` package.
|
|
183
|
+
|
|
184
|
+
### Layer 2: the Flow Agents agent extension (Flow Agents-owned)
|
|
185
|
+
|
|
186
|
+
The **agent extension** is owned by Flow Agents. It defines the optional asset classes that turn a Flow Kit into a **Flow Agents Kit**:
|
|
187
|
+
|
|
188
|
+
| Extension field | Asset type |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `skills` | Reusable agent skill procedures |
|
|
191
|
+
| `docs` | Documentation assets |
|
|
192
|
+
| `adapters` | Runtime or framework adapters |
|
|
193
|
+
| `evals` | Evaluation suites |
|
|
194
|
+
| `assets` | General supporting assets |
|
|
195
|
+
|
|
196
|
+
Each extension field is an optional array of entries with `id`, `path`, and optional `description`. Extension fields are validated by Flow Agents using the same path rules as the container layer (relative, no `..`, must exist). Unknown extension entries are recorded as `skipped_assets` during activation rather than treated as errors.
|
|
197
|
+
|
|
198
|
+
### When a kit "is" a Flow Agents Kit
|
|
199
|
+
|
|
200
|
+
A kit is a **Flow Agents Kit** when it satisfies both layers:
|
|
201
|
+
|
|
202
|
+
1. It passes Flow Kit container validation (valid `kit.json` with core fields and at least one `flows` entry).
|
|
203
|
+
2. It optionally declares one or more Flow Agents extension fields (`skills`, `docs`, `adapters`, `evals`, `assets`).
|
|
204
|
+
|
|
205
|
+
A kit with only core fields (no extension fields) is a valid Flow Kit and is also a valid Flow Agents Kit — it just installs and activates only its Flow Definitions.
|
|
206
|
+
|
|
207
|
+
### Why two layers?
|
|
208
|
+
|
|
209
|
+
The split keeps ownership clean:
|
|
210
|
+
|
|
211
|
+
- Flow authors, installs, and validates the container shape. Any tool that understands Flow Kits can install any kit without knowing about Flow Agents.
|
|
212
|
+
- Flow Agents extends the container for agent-specific assets without modifying the core contract. Third-party kit authors who don't need skills or adapters never have to know about the extension layer.
|
|
213
|
+
|
|
214
|
+
## K-levels: kit conformance and consumer-target badges
|
|
215
|
+
|
|
216
|
+
Every Flow Agents Kit declares assets in a manifest. The K-level system classifies what consumers can do with a kit based on observable asset classes — no extra declaration needed for the level itself. Levels derive from what is present.
|
|
217
|
+
|
|
218
|
+
### Conformance levels
|
|
219
|
+
|
|
220
|
+
| Level | What is present | Who can consume it |
|
|
221
|
+
|---|---|---|
|
|
222
|
+
| **K0** | Valid core Flow Kit container: `schema_version`, `id`, `name`, `flows` (non-empty). | Any Flow consumer: gates and definition-of-done are evaluable agentlessly in CI or without an agent framework. |
|
|
223
|
+
| **K1** | K0 + at least one Flow Agents extension field present (`skills`, `docs`, `adapters`, `evals`, or `assets`). | Flow Agents (>= installed version) can activate the kit in at least one agent harness or framework. |
|
|
224
|
+
| **K2** | K1 + `evals` present with at least one entry. | Live evidence layer: eval suites run against an adapter and produce verifiable output records. |
|
|
225
|
+
|
|
226
|
+
Every kit published by Kontour is K0 or higher. K0 is the minimum bar — a kit that fails K0 is not distributable.
|
|
227
|
+
|
|
228
|
+
### The degradation invariant
|
|
229
|
+
|
|
230
|
+
Every Flow Agents Kit **must remain a valid core Flow Kit container when agent-extension fields are ignored**. This is the degradation invariant:
|
|
231
|
+
|
|
232
|
+
- Strip `skills`, `docs`, `adapters`, `evals`, and `assets` from a kit's `kit.json`.
|
|
233
|
+
- The remaining manifest must satisfy the Flow Kit container contract (`schema_version`, `id`, `name`, `flows` non-empty, no `..` traversal).
|
|
234
|
+
- This invariant is enforced by the kit validator (`npm run validate:source -- --kit <dir>`).
|
|
235
|
+
|
|
236
|
+
Why this matters: any Flow consumer (CI gate runner, definition-of-done checker, release audit tool) can evaluate a kit's gates without knowing about Flow Agents. The kit's flow definitions carry the definition-of-done independently of whether an agent ever runs.
|
|
237
|
+
|
|
238
|
+
### Consumer-target derivation
|
|
239
|
+
|
|
240
|
+
Consumer targets are **derived** from observable asset classes — no declaration is required for standard targets:
|
|
241
|
+
|
|
242
|
+
| Observable state | Derived target |
|
|
243
|
+
|---|---|
|
|
244
|
+
| K0 (flows present) | `flow` — any Flow consumer (gate evaluation, definition-of-done) |
|
|
245
|
+
| K1 (agent extension assets present) | `flow-agents` — Flow Agents activates the kit |
|
|
246
|
+
| Unknown top-level key(s) | Listed verbatim as third-party consumer target(s) |
|
|
247
|
+
|
|
248
|
+
Unknown top-level keys are permitted by the container spec's `additionalProperties: true` rule. A third-party tool built on Flow can extend a kit with its own namespace (e.g. `"my-platform.widgets": [...]`) — the key becomes a derived consumer-target badge without requiring any pre-registration.
|
|
249
|
+
|
|
250
|
+
Version constraints (e.g. minimum `flow-agents` version) are the only case where a declaration is needed beyond what derivation can reach.
|
|
251
|
+
|
|
252
|
+
**Marketplace listing format**: `Works with: Flow (gates-only) | Flow Agents | <Consumer X>`
|
|
253
|
+
|
|
254
|
+
### Evidence layering: Surface and Veritas
|
|
255
|
+
|
|
256
|
+
Kit gates reference evidence using `"kind": "surface.claim"`. This is **Flow-native vocabulary**: Flow is built on Surface, so Surface claims are the expected evidence substrate at the Flow level. Surface claims are not a Flow Agents coupling.
|
|
257
|
+
|
|
258
|
+
Veritas is an **optional claim family** — a developer-repo specialization for evidence that has been through a trust pipeline. Kits may be opinionated about requiring Veritas-class evidence. Builder Kit requiring Veritas-class evidence is the kit's own policy choice, defined by Kontour as the kit author, not a platform requirement. Other kits may not require Veritas at all.
|
|
259
|
+
|
|
260
|
+
Layering summary:
|
|
261
|
+
|
|
262
|
+
- **Surface** = claim substrate (Flow-level, always available)
|
|
263
|
+
- **Veritas** = optional claim kind, kit-opinionated (Builder Kit requires it; others need not)
|
|
264
|
+
|
|
265
|
+
### Inspecting a kit's K-level
|
|
266
|
+
|
|
267
|
+
Use the `inspect` subcommand to derive a kit's conformance level and consumer targets:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
npm run flow-kit -- inspect path/to/my-kit
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Output is stable JSON:
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"kit_id": "my-kit",
|
|
278
|
+
"kit_name": "My Kit",
|
|
279
|
+
"conformance": {
|
|
280
|
+
"k0": true,
|
|
281
|
+
"k1": true,
|
|
282
|
+
"k2": false
|
|
283
|
+
},
|
|
284
|
+
"targets": ["flow", "flow-agents"],
|
|
285
|
+
"third_party_extensions": []
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Exit code 0 when the kit is at least K0 (valid core container); exit code 1 when K0 validation fails.
|
|
290
|
+
|
|
291
|
+
The `inspect` command is read-only and safe to run before install.
|
|
292
|
+
|
|
167
293
|
## Direction
|
|
168
294
|
|
|
169
295
|
Flow Kits are designed to be shareable workflow units — authored once, carried across teams and workspaces. The intended growth path is distribution from git remotes and a curated Kontour kit catalog of Kontour-authored kits covering work modes beyond software delivery. Today install is local-path only; remote fetch is explicitly a non-goal in this version.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Knowledge Kit
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Knowledge Kit
|
|
6
|
+
|
|
7
|
+
The Knowledge Kit is a Flow Kit for durable, gated knowledge storage. It packages a store contract, five pipeline flows, pluggable store adapters, and a parameterized contract test suite — all validated and installed through the standard `flow-kit` path.
|
|
8
|
+
|
|
9
|
+
## What it ships
|
|
10
|
+
|
|
11
|
+
**Store contract** — four record types (`raw`, `compiled`, `concept`, `snapshot`) with a defined mutation policy: every write goes through propose→evidence-gate→apply/reject. Required evidence fields are enforced at runtime; a missing field throws `MISSING_EVIDENCE` rather than silently passing. Supersede-not-delete: records that are superseded move to an archive state with full provenance intact, not removed.
|
|
12
|
+
|
|
13
|
+
**Pipeline flows** — five flows cover the full lifecycle:
|
|
14
|
+
|
|
15
|
+
| Flow | What it does |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `knowledge.ingest` | Capture → classify → route raw source material |
|
|
18
|
+
| `knowledge.compile` | Normalize classified raws into durable notes with provenance links |
|
|
19
|
+
| `knowledge.synthesize` | Detect similar clusters → propose a concept summary → evidence-gate → apply/reject |
|
|
20
|
+
| `knowledge.consolidate` | Related-event trigger → decision snapshot → supersede prior snapshots (not delete) |
|
|
21
|
+
| `knowledge.retire` | Gated status lifecycle: active → implemented → retired, with working-set exclusion |
|
|
22
|
+
|
|
23
|
+
**Store adapters** — two adapters ship:
|
|
24
|
+
|
|
25
|
+
- **Default adapter** — flat markdown files with YAML frontmatter, `[[wikilink]]` inline links, and a JSON graph index. Zero runtime dependencies; uses Node.js built-ins only.
|
|
26
|
+
- **Obsidian adapter** — the same store contract rendered into one human-canonical Obsidian note per record. Category dots map to folder hierarchy; configurable frontmatter dimensions (e.g. territory/customer/initiative as filterable fields); living overview notes at the category root with sources nested below; superseded records moved to `archive/` rather than deleted. The file is the record — no separate database, no sync step.
|
|
27
|
+
|
|
28
|
+
The output-shape story is why the adapter model matters: the same five flows and the same mutation gates produce a different rendering layer depending on which adapter is active. Authors choose the output shape that fits how they already think. (The Obsidian adapter is shipped; layout/dimensions refinements and person/entity card support are in development.)
|
|
29
|
+
|
|
30
|
+
**Similarity detectors** — the `synthesize` and `consolidate` flows accept a pluggable `similarityDetector`:
|
|
31
|
+
|
|
32
|
+
| Detector | Approach | Requires |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `defaultSimilarityDetector` | Category prefix overlap + Jaccard link-overlap (≥ 0.10) | Nothing — built-in |
|
|
35
|
+
| `createVectorSimilarityDetector` | Dense embeddings + cosine similarity | ollama (`nomic-embed-text`, default threshold 0.60) |
|
|
36
|
+
|
|
37
|
+
The vector detector is fail-closed: infrastructure failures throw `EMBED_FAILURE` rather than silently returning an empty cluster (which would be indistinguishable from "no similar records found").
|
|
38
|
+
|
|
39
|
+
**Tests** — 198 tests across six suites (contract, ingest/compile, synthesis, consolidation, similarity-vector, retirement). The contract suite is parameterized — any adapter can run it by pointing `KNOWLEDGE_ADAPTER` at the adapter module.
|
|
40
|
+
|
|
41
|
+
**Live-proven** — keyless operation validated via a Strands agent + local ollama acceptance harness. The vector similarity detector is validated against `nomic-embed-text` with documented threshold guidance.
|
|
42
|
+
|
|
43
|
+
## Quick reference
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Install the kit into a workspace
|
|
47
|
+
npx @kontourai/flow-agents flow-kit install-local kits/knowledge --dest /path/to/workspace
|
|
48
|
+
|
|
49
|
+
# Run the contract suite against the default adapter
|
|
50
|
+
node --test kits/knowledge/evals/contract-suite/suite.test.js
|
|
51
|
+
|
|
52
|
+
# Run against an alternative adapter
|
|
53
|
+
KNOWLEDGE_ADAPTER=/path/to/my-adapter.js \
|
|
54
|
+
node --test kits/knowledge/evals/contract-suite/suite.test.js
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Source
|
|
58
|
+
|
|
59
|
+
- Kit manifest: [`kits/knowledge/kit.json`](https://github.com/kontourai/flow-agents/blob/main/kits/knowledge/kit.json)
|
|
60
|
+
- Store contract doc: [`kits/knowledge/docs/store-contract.md`](https://github.com/kontourai/flow-agents/blob/main/kits/knowledge/docs/store-contract.md)
|
|
61
|
+
- Default adapter: [`kits/knowledge/adapters/default-store/index.js`](https://github.com/kontourai/flow-agents/blob/main/kits/knowledge/adapters/default-store/index.js)
|
|
62
|
+
- Obsidian adapter: [`kits/knowledge/adapters/obsidian-store/index.js`](https://github.com/kontourai/flow-agents/blob/main/kits/knowledge/adapters/obsidian-store/index.js)
|
|
63
|
+
- Vector similarity detector: [`kits/knowledge/adapters/similarity-vector/index.js`](https://github.com/kontourai/flow-agents/blob/main/kits/knowledge/adapters/similarity-vector/index.js)
|
|
64
|
+
|
|
65
|
+
## Related
|
|
66
|
+
|
|
67
|
+
- [Kit Authoring Guide](kit-authoring-guide.html) — build your own kit using the same `kit.json` contract
|
|
68
|
+
- [Flow Kit Repository Contract](flow-kit-repository-contract.html) — validation rules and activation diagnostics
|
|
69
|
+
- [Vision and Direction](vision.html) — the kits-as-ecosystem arc and the sequenced path toward domain kits and a registry
|
package/docs/vision.md
CHANGED
|
@@ -16,6 +16,28 @@ One official framework adapter spike exists: `integrations/strands/` is a Python
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
## Flow Kits as an authorable ecosystem
|
|
20
|
+
|
|
21
|
+
### What ships today
|
|
22
|
+
|
|
23
|
+
Kit authoring is shipped. The `kit.json` contract at schema version 1.0 is validated by the `flow-kit` CLI before any install. The [Kit Authoring Guide](kit-authoring-guide.html) walks from an empty directory to a validated, locally installed kit. Two reference kits ship in `kits/`:
|
|
24
|
+
|
|
25
|
+
**Builder Kit** packages the full `idea → backlog → plan → build → review → verify → evidence → release → learning` pipeline as two flows (`builder.shape`, `builder.build`). It installs automatically.
|
|
26
|
+
|
|
27
|
+
**Knowledge Kit** packages durable gated knowledge storage: a store contract with four record types, five pipeline flows (`ingest`, `compile`, `synthesize`, `consolidate`, `retire`), and a mutation policy of propose→evidence-gate→apply/reject with supersede-not-delete. It ships with 198 tests, a parameterized contract suite that any adapter can run against, and a vector similarity detector (ollama embeddings, pluggable interface).
|
|
28
|
+
|
|
29
|
+
The output-shape story is the reason kit authoring matters beyond workflow reuse. The Knowledge Kit store contract is representation-neutral — the pipeline is identical, but the rendering layer is swapped by adapter. Today two adapters ship: the default adapter (flat markdown records with YAML frontmatter and a JSON graph index) and the Obsidian adapter (one human-canonical note per record, category→folder hierarchy, configurable frontmatter dimensions, living overviews, superseded notes in `archive/` not deleted). Authors who choose the Obsidian adapter get the same gated workflow rendered into the shape they already think in. That is what kit authoring unlocks: packaging both the process discipline and the output opinion as a deployable unit. (The Obsidian adapter is shipped; layout/dimensions refinements and person/entity card support are in development.)
|
|
30
|
+
|
|
31
|
+
### Direction: the sequenced path to a kit ecosystem
|
|
32
|
+
|
|
33
|
+
The items below are direction, not committed delivery dates. They record the intended shape of where this work goes.
|
|
34
|
+
|
|
35
|
+
**Domain kits composing the substrate.** The store contract, mutation gate, and adapter model are not knowledge-specific. The next intended kits compose this substrate for specific domains: a Sales Kit (territory/customer/initiative schema, flows for managing account snapshots, side-effect adapters for CRM event logging) and a Research Kit (transcript capture→compile→recall with configurable similarity thresholds). Both are direction — not shipped.
|
|
36
|
+
|
|
37
|
+
**Distribution, sequenced.** A registry of validated kits and a marketplace for kit distribution are the intended end-state. The sequencing is explicit: authoring tooling and covetable reference kits first (shipped), then a registry (direction), then a marketplace (direction). Claims of a shipped marketplace or registry are not warranted.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
19
41
|
## Direction
|
|
20
42
|
|
|
21
43
|
The items below are direction, not committed delivery dates. They record the intended shape of where this work goes.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "k0-flows-only.review",
|
|
3
|
+
"version": "1.0",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "id": "review", "next": "done" },
|
|
6
|
+
{ "id": "done", "next": null }
|
|
7
|
+
],
|
|
8
|
+
"gates": {
|
|
9
|
+
"review-gate": {
|
|
10
|
+
"step": "review",
|
|
11
|
+
"expects": [
|
|
12
|
+
{
|
|
13
|
+
"id": "review-finding",
|
|
14
|
+
"kind": "surface.claim",
|
|
15
|
+
"required": true,
|
|
16
|
+
"description": "Review finding recorded.",
|
|
17
|
+
"claim": {
|
|
18
|
+
"type": "k0.review.finding",
|
|
19
|
+
"subject": "artifact",
|
|
20
|
+
"accepted_statuses": ["trusted", "accepted"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"id": "k0-flows-only",
|
|
4
|
+
"name": "K0 Flows-Only Kit",
|
|
5
|
+
"description": "A kit with only Flow Definitions — K0 conformance, consumable by any Flow consumer.",
|
|
6
|
+
"flows": [
|
|
7
|
+
{
|
|
8
|
+
"id": "k0-flows-only.review",
|
|
9
|
+
"path": "flows/review.flow.json",
|
|
10
|
+
"description": "Review gate evaluable agentlessly."
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "k1-agent-extension.build",
|
|
3
|
+
"version": "1.0",
|
|
4
|
+
"steps": [
|
|
5
|
+
{ "id": "build", "next": "done" },
|
|
6
|
+
{ "id": "done", "next": null }
|
|
7
|
+
],
|
|
8
|
+
"gates": {
|
|
9
|
+
"build-gate": {
|
|
10
|
+
"step": "build",
|
|
11
|
+
"expects": [
|
|
12
|
+
{
|
|
13
|
+
"id": "build-evidence",
|
|
14
|
+
"kind": "surface.claim",
|
|
15
|
+
"required": true,
|
|
16
|
+
"description": "Build evidence recorded.",
|
|
17
|
+
"claim": {
|
|
18
|
+
"type": "k1.build.evidence",
|
|
19
|
+
"subject": "artifact",
|
|
20
|
+
"accepted_statuses": ["trusted", "accepted"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|