@kontourai/flow-agents 2.4.0 → 3.1.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.
Files changed (195) hide show
  1. package/.github/CODEOWNERS +8 -0
  2. package/.github/workflows/ci.yml +20 -0
  3. package/.github/workflows/trust-reconcile.yml +62 -4
  4. package/CHANGELOG.md +38 -0
  5. package/CONTEXT.md +88 -1
  6. package/README.md +4 -0
  7. package/build/src/cli/assignment-provider.d.ts +144 -0
  8. package/build/src/cli/assignment-provider.js +805 -0
  9. package/build/src/cli/effective-assignment-provider-settings.d.ts +1 -0
  10. package/build/src/cli/effective-assignment-provider-settings.js +125 -0
  11. package/build/src/cli/validate-workflow-artifacts.js +5 -1
  12. package/build/src/cli/workflow-sidecar.d.ts +2 -2
  13. package/build/src/cli/workflow-sidecar.js +549 -145
  14. package/build/src/cli.js +6 -0
  15. package/build/src/lib/flow-resolver.d.ts +12 -6
  16. package/build/src/lib/flow-resolver.js +30 -14
  17. package/build/src/tools/validate-source-tree.js +2 -0
  18. package/context/contracts/artifact-contract.md +2 -0
  19. package/context/contracts/assignment-provider-contract.md +248 -0
  20. package/context/contracts/builder-kit-workflow-state-contract.md +2 -0
  21. package/context/contracts/decision-registry-contract.md +2 -0
  22. package/context/contracts/delivery-contract.md +2 -0
  23. package/context/contracts/execution-contract.md +25 -0
  24. package/context/contracts/governance-adapter-contract.md +2 -0
  25. package/context/contracts/knowledge-store-contract.md +197 -0
  26. package/context/contracts/planning-contract.md +2 -0
  27. package/context/contracts/probe-docs-write-contract.md +187 -0
  28. package/context/contracts/review-contract.md +2 -0
  29. package/context/contracts/sandbox-policy.md +2 -0
  30. package/context/contracts/standing-directives.md +13 -0
  31. package/context/contracts/verification-contract.md +2 -0
  32. package/context/contracts/work-item-contract.md +2 -0
  33. package/context/scripts/hooks/config-protection.js +14 -1
  34. package/context/scripts/hooks/lib/config-protection-remedies.js +9 -0
  35. package/context/scripts/hooks/stop-goal-fit.js +4 -2
  36. package/context/scripts/hooks/workflow-steering.js +42 -0
  37. package/context/settings/assignment-provider-settings.json +33 -0
  38. package/docs/adr/0001-flow-agents-consumes-flow.md +2 -0
  39. package/docs/adr/0002-flow-kits-as-extension-unit.md +2 -0
  40. package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +2 -0
  41. package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
  42. package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
  43. package/docs/adr/0006-typescript-first-source-policy.md +2 -0
  44. package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
  45. package/docs/adr/0007-skill-audit.md +2 -0
  46. package/docs/adr/0008-kit-operation-boundary.md +2 -0
  47. package/docs/adr/0009-canonical-hook-core-kit-boundary.md +2 -0
  48. package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +2 -0
  49. package/docs/adr/0011-mcp-posture.md +2 -0
  50. package/docs/adr/0012-agent-coordination-as-liveness-claims.md +2 -0
  51. package/docs/adr/0013-context-lifecycle.md +2 -0
  52. package/docs/adr/0014-core-vs-domain-kit-boundary.md +2 -0
  53. package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +2 -0
  54. package/docs/adr/0016-three-hard-boundary-model.md +2 -0
  55. package/docs/adr/0017-anti-gaming-trust-security-model.md +2 -0
  56. package/docs/adr/0018-freeze-local-shell-heuristics.md +2 -0
  57. package/docs/adr/0019-kit-dependency-ownership.md +2 -0
  58. package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +2 -0
  59. package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +3 -1
  60. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +182 -0
  61. package/docs/adr/README.md +49 -0
  62. package/docs/adr/index.md +34 -0
  63. package/docs/context-map.md +1 -0
  64. package/docs/decisions/agent-coordination.md +20 -0
  65. package/docs/decisions/anti-gaming-trust-security.md +20 -0
  66. package/docs/decisions/context-lifecycle.md +18 -0
  67. package/docs/decisions/core-domain-kit-boundary.md +18 -0
  68. package/docs/decisions/flow-flow-agents-boundary.md +18 -0
  69. package/docs/decisions/flow-kit.md +20 -0
  70. package/docs/decisions/flow-skill-kit-tool-boundary.md +20 -0
  71. package/docs/decisions/graph-knowledge-provider.md +63 -0
  72. package/docs/decisions/hook-core-kit-boundary.md +18 -0
  73. package/docs/decisions/index.md +22 -0
  74. package/docs/decisions/kit-dependency-ownership.md +18 -0
  75. package/docs/decisions/kit-operation-boundary.md +18 -0
  76. package/docs/decisions/knowledge-promote-sub-flow.md +59 -0
  77. package/docs/decisions/knowledge-store-provider.md +51 -0
  78. package/docs/decisions/kontour-resource-contract.md +18 -0
  79. package/docs/decisions/mcp-posture.md +18 -0
  80. package/docs/decisions/model-routing.md +63 -0
  81. package/docs/decisions/standing-directives.md +66 -0
  82. package/docs/decisions/three-hard-boundary-model.md +18 -0
  83. package/docs/decisions/trust-reconcile.md +20 -0
  84. package/docs/decisions/typescript-source-policy.md +48 -0
  85. package/docs/decisions/workflow-enforcement.md +18 -0
  86. package/docs/decisions/workflow-trust-state.md +20 -0
  87. package/docs/fixture-ownership.md +1 -0
  88. package/docs/workflow-shared-contracts.md +2 -1
  89. package/docs/workflow-usage-guide.md +7 -1
  90. package/evals/ci/run-baseline.sh +10 -0
  91. package/evals/fixtures/assignment-provider/actor-a.json +6 -0
  92. package/evals/fixtures/assignment-provider/actor-b.json +6 -0
  93. package/evals/fixtures/assignment-provider/github-issue-claimed.json +27 -0
  94. package/evals/fixtures/assignment-provider/github-issue-unassigned.json +7 -0
  95. package/evals/fixtures/assignment-provider/hostile-effective-state.json +11 -0
  96. package/evals/fixtures/assignment-provider/liveness-fresh.json +9 -0
  97. package/evals/fixtures/assignment-provider/liveness-stale.json +9 -0
  98. package/evals/integration/test_assignment_provider_github.sh +318 -0
  99. package/evals/integration/test_assignment_provider_local_file.sh +222 -0
  100. package/evals/integration/test_critique_supersession_roundtrip.sh +182 -0
  101. package/evals/integration/test_current_json_per_actor.sh +516 -0
  102. package/evals/integration/test_ensure_session_ownership_guard.sh +435 -0
  103. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  104. package/evals/integration/test_gate_lockdown.sh +10 -0
  105. package/evals/integration/test_publish_delivery.sh +21 -4
  106. package/evals/integration/test_pull_work_assignment_join.sh +132 -0
  107. package/evals/integration/test_pull_work_liveness_preflight.sh +20 -7
  108. package/evals/integration/test_reconcile_soundness.sh +33 -9
  109. package/evals/integration/test_trust_reconcile.sh +9 -8
  110. package/evals/integration/test_trust_reconcile_negatives.sh +608 -0
  111. package/evals/integration/test_workflow_sidecar_writer.sh +91 -3
  112. package/evals/run.sh +14 -0
  113. package/evals/static/test_flowdef_codeowners_coverage.sh +6 -0
  114. package/evals/static/test_knowledge_providers.sh +32 -0
  115. package/evals/static/test_workflow_skills.sh +15 -2
  116. package/kits/builder/skills/deliver/SKILL.md +36 -0
  117. package/kits/builder/skills/design-probe/SKILL.md +37 -0
  118. package/kits/builder/skills/pickup-probe/SKILL.md +4 -4
  119. package/kits/builder/skills/pull-work/SKILL.md +78 -10
  120. package/kits/knowledge/adapters/default-store/index.js +92 -4
  121. package/kits/knowledge/adapters/obsidian-store/index.js +69 -4
  122. package/kits/knowledge/adapters/shared/codec.js +141 -0
  123. package/kits/knowledge/docs/README.md +121 -2
  124. package/kits/knowledge/docs/store-contract.md +112 -4
  125. package/kits/knowledge/evals/contract-suite/suite.test.js +176 -0
  126. package/kits/knowledge/flows/promote.flow.json +84 -0
  127. package/kits/knowledge/kit.json +50 -0
  128. package/kits/knowledge/promote/distill.js +96 -0
  129. package/kits/knowledge/promote/fixtures/contradiction-registry/CONTEXT.md +7 -0
  130. package/kits/knowledge/promote/fixtures/contradiction-registry/docs/decisions/cache-eviction-policy.md +12 -0
  131. package/kits/knowledge/promote/fixtures/contradiction-registry/docs/decisions/cache-eviction.md +12 -0
  132. package/kits/knowledge/promote/fixtures/repo/CONTEXT.md +11 -0
  133. package/kits/knowledge/promote/fixtures/repo/docs/decisions/index.md +9 -0
  134. package/kits/knowledge/promote/fixtures/repo/docs/decisions/liveness-claim.md +12 -0
  135. package/kits/knowledge/promote/fixtures/session/acceptance.json +9 -0
  136. package/kits/knowledge/promote/fixtures/session/agents/tool-planner/events.jsonl +1 -0
  137. package/kits/knowledge/promote/fixtures/session/agents/tool-verifier/events.jsonl +1 -0
  138. package/kits/knowledge/promote/fixtures/session/agents/tool-worker-1-1/events.jsonl +1 -0
  139. package/kits/knowledge/promote/fixtures/session/kontourai-flow-agents-287--deliver.md +47 -0
  140. package/kits/knowledge/promote/fixtures/session/learning.json +35 -0
  141. package/kits/knowledge/promote/fixtures/session/state.json +19 -0
  142. package/kits/knowledge/promote/health.js +137 -0
  143. package/kits/knowledge/promote/index.js +176 -0
  144. package/kits/knowledge/promote/ingest.js +103 -0
  145. package/kits/knowledge/promote/lib.js +132 -0
  146. package/kits/knowledge/promote/link.js +84 -0
  147. package/kits/knowledge/promote/promote.test.js +174 -0
  148. package/kits/knowledge/providers/conformance/fixtures/git-repo/CONTEXT.md +12 -0
  149. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/old-sprocket-shape.md +13 -0
  150. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/sprocket-shape.md +14 -0
  151. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/widget-format.md +14 -0
  152. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/learnings/fixture-learning.md +7 -0
  153. package/kits/knowledge/providers/conformance/fixtures/work-item/issues.json +30 -0
  154. package/kits/knowledge/providers/conformance/suite.test.js +143 -0
  155. package/kits/knowledge/providers/git-repo/index.js +236 -0
  156. package/kits/knowledge/providers/health/health-pass.test.js +99 -0
  157. package/kits/knowledge/providers/health/index.js +153 -0
  158. package/kits/knowledge/providers/index.js +25 -0
  159. package/kits/knowledge/providers/lib/model.js +91 -0
  160. package/kits/knowledge/providers/lib/schema-validate.js +119 -0
  161. package/kits/knowledge/providers/markdown-vault/index.js +169 -0
  162. package/kits/knowledge/providers/neo4j/connection.js +121 -0
  163. package/kits/knowledge/providers/neo4j/cypher.js +190 -0
  164. package/kits/knowledge/providers/neo4j/fake-driver.js +123 -0
  165. package/kits/knowledge/providers/neo4j/fixtures/spike-ground-truth.json +29 -0
  166. package/kits/knowledge/providers/neo4j/graph-queries.js +210 -0
  167. package/kits/knowledge/providers/neo4j/index.js +280 -0
  168. package/kits/knowledge/providers/neo4j/integration.test.js +122 -0
  169. package/kits/knowledge/providers/neo4j/neo4j.test.js +219 -0
  170. package/kits/knowledge/providers/neo4j/sync.js +235 -0
  171. package/kits/knowledge/providers/work-item/index.js +204 -0
  172. package/package.json +7 -3
  173. package/schemas/assignment-provider-settings.schema.json +125 -0
  174. package/schemas/knowledge/edge.schema.json +54 -0
  175. package/schemas/knowledge/health-report.schema.json +45 -0
  176. package/schemas/knowledge/node.schema.json +49 -0
  177. package/schemas/knowledge/proposal.schema.json +53 -0
  178. package/scripts/README.md +1 -0
  179. package/scripts/ci/trust-reconcile.js +521 -24
  180. package/scripts/freeze-adrs.mjs +364 -0
  181. package/scripts/hooks/config-protection.js +14 -1
  182. package/scripts/hooks/evidence-capture.js +4 -1
  183. package/scripts/hooks/lib/config-protection-remedies.js +9 -0
  184. package/scripts/hooks/lib/current-pointer.js +123 -0
  185. package/scripts/hooks/lib/liveness-heartbeat.js +75 -35
  186. package/scripts/hooks/stop-goal-fit.js +4 -2
  187. package/scripts/hooks/workflow-steering.js +42 -0
  188. package/scripts/statusline/flow-agents-statusline.js +3 -1
  189. package/src/cli/assignment-provider.ts +927 -0
  190. package/src/cli/effective-assignment-provider-settings.ts +112 -0
  191. package/src/cli/validate-workflow-artifacts.ts +5 -1
  192. package/src/cli/workflow-sidecar.ts +577 -139
  193. package/src/cli.ts +6 -0
  194. package/src/lib/flow-resolver.ts +35 -14
  195. package/src/tools/validate-source-tree.ts +2 -0
@@ -0,0 +1,204 @@
1
+ /**
2
+ * work-item knowledge-store provider (source/sink adapter, not storage).
3
+ *
4
+ * Maps a GitHub issue backlog into the typed graph so backlog hygiene becomes a
5
+ * knowledge-health pass:
6
+ * - issues -> `issue` nodes (state / labels / title).
7
+ * - flow-agents:work-item-metadata `blockers` -> `blocks` edges.
8
+ * - prose "blocked by #N" -> `blocks` edges; other #N refs -> `relates`.
9
+ *
10
+ * Reads via an INJECTABLE runner so tests drive it from recorded fixtures and
11
+ * never touch the real board. Write side is proposals-only: proposeWrite renders
12
+ * a draft issue comment / label change for an operator to file — it never calls
13
+ * a mutating gh command.
14
+ *
15
+ * @module providers/work-item
16
+ */
17
+
18
+ import { execFile } from "node:child_process";
19
+ import { node, edge, proposal, provenance } from "../lib/model.js";
20
+
21
+ const PROVIDER_ID = "work-item";
22
+
23
+ /** Default runner: shells out to `gh` (read-only list). */
24
+ function defaultRunner(args) {
25
+ return new Promise((resolve, reject) => {
26
+ execFile("gh", args, { maxBuffer: 32 * 1024 * 1024 }, (err, stdout, stderr) => {
27
+ if (err) return reject(new Error(`gh ${args.join(" ")} failed: ${stderr || err.message}`));
28
+ resolve(stdout);
29
+ });
30
+ });
31
+ }
32
+
33
+ /** Parse the flow-agents:work-item-metadata JSON block from an issue body. */
34
+ export function parseWorkItemMetadata(body) {
35
+ if (!body) return null;
36
+ const start = body.indexOf("flow-agents:work-item-metadata");
37
+ if (start === -1) return null;
38
+ const jsonStart = body.indexOf("{", start);
39
+ const end = body.indexOf("-->", jsonStart);
40
+ if (jsonStart === -1 || end === -1) return null;
41
+ try {
42
+ return JSON.parse(body.slice(jsonStart, end).trim());
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ /** Extract a blocker issue number from a metadata blocker entry. */
49
+ function blockerNumber(entry) {
50
+ if (typeof entry === "number") return entry;
51
+ if (typeof entry === "string") {
52
+ const m = entry.match(/#?(\d+)/);
53
+ return m ? Number(m[1]) : null;
54
+ }
55
+ if (entry && typeof entry === "object") {
56
+ if (typeof entry.number === "number") return entry.number;
57
+ if (typeof entry.issue === "number") return entry.issue;
58
+ if (typeof entry.ref === "string") {
59
+ const m = entry.ref.match(/#?(\d+)/);
60
+ return m ? Number(m[1]) : null;
61
+ }
62
+ }
63
+ return null;
64
+ }
65
+
66
+ export class WorkItemProvider {
67
+ constructor({ repo, runner, agent } = {}) {
68
+ this.repo = repo || "";
69
+ this.runner = runner || defaultRunner;
70
+ this.agent = agent;
71
+ this.id = PROVIDER_ID;
72
+ this._cache = null;
73
+ }
74
+
75
+ capabilities() {
76
+ return {
77
+ id: PROVIDER_ID,
78
+ node_types: ["issue"],
79
+ edge_types: ["blocks", "relates"],
80
+ writable: false,
81
+ write_mode: "proposals-only",
82
+ proposal_targets: ["comment", "label"],
83
+ source_of_truth: "GitHub issues (read via gh; write = draft comments/labels)",
84
+ };
85
+ }
86
+
87
+ async _issues() {
88
+ if (this._cache) return this._cache;
89
+ const args = ["issue", "list"];
90
+ if (this.repo) args.push("--repo", this.repo);
91
+ args.push("--state", "all", "--json", "number,title,state,labels,body", "--limit", "200");
92
+ const out = await this.runner(args);
93
+ const parsed = JSON.parse(out);
94
+ this._cache = Array.isArray(parsed) ? parsed : [];
95
+ return this._cache;
96
+ }
97
+
98
+ async readNodes(options = {}) {
99
+ const issues = await this._issues();
100
+ const now = new Date().toISOString();
101
+ const nodes = issues.map((iss) =>
102
+ node({
103
+ id: `issue:${iss.number}`,
104
+ type: "issue",
105
+ title: iss.title || `issue #${iss.number}`,
106
+ body: iss.body || "",
107
+ attributes: {
108
+ number: iss.number,
109
+ state: iss.state || "OPEN",
110
+ labels: (iss.labels || []).map((l) => (typeof l === "string" ? l : l.name)),
111
+ },
112
+ provenance: provenance({
113
+ provider: PROVIDER_ID,
114
+ source: this.repo ? `${this.repo}#${iss.number}` : `#${iss.number}`,
115
+ retrievedAt: now,
116
+ agent: this.agent,
117
+ }),
118
+ }),
119
+ );
120
+ if (options.type) return nodes.filter((n) => n.type === options.type);
121
+ return nodes;
122
+ }
123
+
124
+ async readEdges() {
125
+ const issues = await this._issues();
126
+ const now = new Date().toISOString();
127
+ const edges = [];
128
+ const seen = new Set();
129
+ const push = (e) => {
130
+ if (seen.has(e.id)) return;
131
+ seen.add(e.id);
132
+ edges.push(e);
133
+ };
134
+
135
+ for (const iss of issues) {
136
+ const self = `issue:${iss.number}`;
137
+ const prov = (loc) => provenance({ provider: PROVIDER_ID, source: this.repo ? `${this.repo}#${iss.number}` : `#${iss.number}`, locator: loc, retrievedAt: now, agent: this.agent });
138
+
139
+ // metadata blockers -> blocks (blocker blocks this)
140
+ const md = parseWorkItemMetadata(iss.body);
141
+ for (const b of (md && Array.isArray(md.blockers) ? md.blockers : [])) {
142
+ const n = blockerNumber(b);
143
+ if (n == null) continue;
144
+ push(edge({ id: `issue:${n}--blocks--${iss.number}`, type: "blocks", from: `issue:${n}`, to: self, attributes: { origin: "metadata" }, provenance: prov("metadata.blockers") }));
145
+ }
146
+
147
+ const body = iss.body || "";
148
+ // prose "blocked by #N" / "depends on #N" -> blocks
149
+ for (const m of body.matchAll(/\b(?:blocked by|depends on|blocker:)\s*#(\d+)/gi)) {
150
+ const n = Number(m[1]);
151
+ push(edge({ id: `issue:${n}--blocks--${iss.number}`, type: "blocks", from: `issue:${n}`, to: self, attributes: { origin: "prose" }, provenance: prov("body.prose") }));
152
+ }
153
+ // other bare #N prose refs -> relates
154
+ for (const m of body.matchAll(/(?<![\w/])#(\d+)\b/g)) {
155
+ const n = Number(m[1]);
156
+ if (n === iss.number) continue;
157
+ const bid = `issue:${iss.number}--relates--${n}`;
158
+ if (seen.has(`issue:${n}--blocks--${iss.number}`)) continue; // already a stronger blocks edge
159
+ push(edge({ id: bid, type: "relates", from: self, to: `issue:${n}`, attributes: { origin: "prose" }, provenance: prov("body.prose") }));
160
+ }
161
+ }
162
+ return edges;
163
+ }
164
+
165
+ async queryByType(type) {
166
+ return this.readNodes({ type });
167
+ }
168
+
169
+ async readGraph() {
170
+ const [nodes, edges] = await Promise.all([this.readNodes(), this.readEdges()]);
171
+ return { nodes, edges };
172
+ }
173
+
174
+ /**
175
+ * Propose an issue comment or label change. Renders the draft an operator
176
+ * would file with gh — the provider never runs a mutating gh command.
177
+ */
178
+ async proposeWrite(intent = {}) {
179
+ const { issue, kind = "comment", commentBody, labels = [] } = intent;
180
+ if (issue == null) throw new Error("work-item proposeWrite requires intent.issue (number)");
181
+ if (kind === "label") {
182
+ return proposal({
183
+ provider: PROVIDER_ID,
184
+ kind: "label",
185
+ target: { repo: this.repo, issue },
186
+ payload: { labels },
187
+ rendered: `gh issue edit ${issue}${this.repo ? ` --repo ${this.repo}` : ""} ${labels.map((l) => `--add-label ${JSON.stringify(l)}`).join(" ")}`,
188
+ rationale: intent.rationale || "Proposed label change (draft — operator files it).",
189
+ provenance: provenance({ provider: PROVIDER_ID, source: this.repo ? `${this.repo}#${issue}` : `#${issue}`, agent: this.agent }),
190
+ });
191
+ }
192
+ return proposal({
193
+ provider: PROVIDER_ID,
194
+ kind: "comment",
195
+ target: { repo: this.repo, issue },
196
+ payload: { body: commentBody || "" },
197
+ rendered: `gh issue comment ${issue}${this.repo ? ` --repo ${this.repo}` : ""} --body ${JSON.stringify(commentBody || "")}`,
198
+ rationale: intent.rationale || "Proposed issue comment (draft — operator files it).",
199
+ provenance: provenance({ provider: PROVIDER_ID, source: this.repo ? `${this.repo}#${issue}` : `#${issue}`, agent: this.agent }),
200
+ });
201
+ }
202
+ }
203
+
204
+ export default WorkItemProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontourai/flow-agents",
3
- "version": "2.4.0",
3
+ "version": "3.1.0",
4
4
  "description": "Flow Agents — a Kontour product that applies Flow and Veritas discipline as a portable process layer inside the agent tools you already use: Claude Code, Codex, Kiro, opencode, pi, and GitHub Actions — with framework adapters (AWS Strands preview) on the same policy-engine contract.",
5
5
  "keywords": [
6
6
  "agents",
@@ -110,6 +110,8 @@
110
110
  "workflow:sidecar": "npm run build --silent && node build/src/cli/workflow-sidecar.js",
111
111
  "workflow:validate-artifacts": "npm run build --silent && node build/src/cli/validate-workflow-artifacts.js",
112
112
  "effective-backlog-settings": "npm run build --silent && node build/src/cli.js effective-backlog-settings",
113
+ "assignment-provider": "npm run build --silent && node build/src/cli.js assignment-provider",
114
+ "effective-assignment-provider-settings": "npm run build --silent && node build/src/cli.js effective-assignment-provider-settings",
113
115
  "pull-work-provider": "npm run build --silent && node build/src/cli.js pull-work-provider",
114
116
  "publish-change": "npm run build --silent && node build/src/cli.js publish-change",
115
117
  "promote-workflow-artifact": "npm run build --silent && node build/src/cli.js promote-workflow-artifact",
@@ -137,11 +139,12 @@
137
139
  "check:content-boundary": "node scripts/check-content-boundary.cjs",
138
140
  "check:decisions": "node scripts/check-decisions.cjs check",
139
141
  "gen:decisions-index": "node scripts/check-decisions.cjs gen-index",
142
+ "freeze:adrs": "node scripts/freeze-adrs.mjs",
140
143
  "prepack": "npm run build --silent && npm run validate:source --",
141
144
  "kit": "npm run build --silent && node build/src/cli.js kit"
142
145
  },
143
146
  "devDependencies": {
144
- "@types/node": "^26.0.1",
147
+ "@types/node": "^26.1.0",
145
148
  "promptfoo": "^0.121.17",
146
149
  "typescript": "^6.0.3"
147
150
  },
@@ -150,6 +153,7 @@
150
153
  },
151
154
  "optionalDependencies": {
152
155
  "@kontourai/surface": "^2.0.0",
153
- "hachure": "^0.9.0"
156
+ "hachure": "^0.9.0",
157
+ "neo4j-driver": "^5.28.0"
154
158
  }
155
159
  }
@@ -0,0 +1,125 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://flow-agents.dev/schemas/assignment-provider-settings.schema.json",
4
+ "title": "Flow Agents Assignment Provider Settings",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "schema_version": { "const": "1.0" },
11
+ "defaults": { "$ref": "#/$defs/assignment_settings" },
12
+ "projects": {
13
+ "type": "array",
14
+ "items": { "$ref": "#/$defs/project_assignment_settings" }
15
+ }
16
+ },
17
+ "anyOf": [{ "required": ["defaults"] }, { "required": ["projects"] }],
18
+ "$defs": {
19
+ "project_assignment_settings": {
20
+ "allOf": [
21
+ { "$ref": "#/$defs/assignment_settings" },
22
+ { "type": "object", "required": ["project"] }
23
+ ]
24
+ },
25
+ "assignment_settings": {
26
+ "type": "object",
27
+ "additionalProperties": false,
28
+ "required": ["provider", "policy"],
29
+ "properties": {
30
+ "project": { "$ref": "#/$defs/project_match" },
31
+ "provider": { "$ref": "#/$defs/provider" },
32
+ "policy": { "$ref": "#/$defs/policy" }
33
+ }
34
+ },
35
+ "project_match": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": ["repo"],
39
+ "properties": {
40
+ "repo": { "$ref": "#/$defs/repo" },
41
+ "paths": { "type": "array", "items": { "type": "string", "minLength": 1 } }
42
+ }
43
+ },
44
+ "provider": {
45
+ "oneOf": [
46
+ { "$ref": "#/$defs/github_provider" },
47
+ { "$ref": "#/$defs/local_file_provider" }
48
+ ]
49
+ },
50
+ "github_provider": {
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": ["kind", "repo", "capabilities"],
54
+ "properties": {
55
+ "kind": { "const": "github" },
56
+ "repo": { "$ref": "#/$defs/repo" },
57
+ "capabilities": { "$ref": "#/$defs/capabilities" }
58
+ }
59
+ },
60
+ "local_file_provider": {
61
+ "type": "object",
62
+ "additionalProperties": false,
63
+ "required": ["kind", "capabilities"],
64
+ "properties": {
65
+ "kind": { "const": "local-file" },
66
+ "capabilities": { "$ref": "#/$defs/capabilities" }
67
+ }
68
+ },
69
+ "policy": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": ["label_name", "claim_comment_marker", "human_assignee_policy"],
73
+ "properties": {
74
+ "label_name": {
75
+ "type": "string",
76
+ "minLength": 1,
77
+ "description": "Label attached to a claimed subject on providers that support labels. Default: \"agent:claimed\"."
78
+ },
79
+ "claim_comment_marker": {
80
+ "type": "string",
81
+ "minLength": 1,
82
+ "description": "Fixed marker used to locate the machine-readable claim comment among human comments. Default: \"<!-- flow-agents:assignment-claim -->\"."
83
+ },
84
+ "human_assignee_policy": { "$ref": "#/$defs/human_assignee_policy" },
85
+ "comment_refresh_on_phase_transition": {
86
+ "type": "boolean",
87
+ "description": "Whether an explicit render-claim/render-supersede call at a phase transition also refreshes the claim comment body. Default: false. No new mechanism; this reuses the same render path already built for claim."
88
+ }
89
+ }
90
+ },
91
+ "human_assignee_policy": {
92
+ "type": "object",
93
+ "additionalProperties": false,
94
+ "required": ["behavior", "idle_threshold_days"],
95
+ "properties": {
96
+ "behavior": {
97
+ "enum": ["ask_first", "never_reclaim"],
98
+ "description": "Default: \"ask_first\". A human assignee is never auto-superseded regardless of this value; this knob only governs whether idle human assignments are ever surfaced for possible reclaim confirmation (ask_first) or left untouched (never_reclaim)."
99
+ },
100
+ "idle_threshold_days": {
101
+ "type": "integer",
102
+ "minimum": 0,
103
+ "description": "Days of no linked activity before an idle human assignment is surfaced for confirmation. Default: 3."
104
+ }
105
+ }
106
+ },
107
+ "capabilities": {
108
+ "type": "array",
109
+ "items": {
110
+ "enum": ["assignees", "labels", "comments"]
111
+ },
112
+ "uniqueItems": true
113
+ },
114
+ "repo": {
115
+ "type": "object",
116
+ "additionalProperties": false,
117
+ "required": ["owner", "name"],
118
+ "properties": {
119
+ "owner": { "type": "string", "minLength": 1 },
120
+ "name": { "type": "string", "minLength": 1 },
121
+ "url": { "type": "string" }
122
+ }
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://flow-agents.dev/schemas/knowledge/edge.schema.json",
4
+ "title": "Knowledge Graph Edge",
5
+ "description": "A typed, directed edge between two nodes. The edge-type vocabulary is CLOSED (unlike node types): supersedes, merged-into, blocks, evidence-of, mentions, relates. Every edge carries provenance. The `to` endpoint MAY reference a node that is not present in the graph (an external or dangling reference); `resolved` records whether the provider expects the target to resolve, which the dependency-link-integrity health verb uses to separate deliberate external refs from broken links.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["id", "type", "from", "to", "provenance"],
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "Provider-scoped stable identifier for the edge."
14
+ },
15
+ "type": {
16
+ "type": "string",
17
+ "enum": ["supersedes", "merged-into", "blocks", "evidence-of", "mentions", "relates"],
18
+ "description": "supersedes: from replaces to. merged-into: from was folded into to. blocks: from must complete before to. evidence-of: from is provenance/evidence for to. mentions: from references to in prose. relates: general undirected-ish association."
19
+ },
20
+ "from": {
21
+ "type": "string",
22
+ "minLength": 1,
23
+ "description": "Source node id."
24
+ },
25
+ "to": {
26
+ "type": "string",
27
+ "minLength": 1,
28
+ "description": "Target node id. MAY be a node id not present in the graph when `resolved` is false (external ref)."
29
+ },
30
+ "resolved": {
31
+ "type": "boolean",
32
+ "description": "true (default when omitted) means the provider expects `to` to resolve to a node in the same graph; a missing target is then a broken link. false means `to` is a deliberate external reference (e.g. evidence-of a PR outside the store) and MUST NOT be flagged as broken."
33
+ },
34
+ "attributes": {
35
+ "type": "object",
36
+ "description": "Provider-specific structured fields for the edge."
37
+ },
38
+ "provenance": { "$ref": "#/$defs/provenance" }
39
+ },
40
+ "$defs": {
41
+ "provenance": {
42
+ "type": "object",
43
+ "additionalProperties": false,
44
+ "required": ["provider", "source", "retrieved_at"],
45
+ "properties": {
46
+ "provider": { "type": "string", "minLength": 1 },
47
+ "source": { "type": "string", "minLength": 1 },
48
+ "locator": { "type": "string" },
49
+ "retrieved_at": { "type": "string", "minLength": 1 },
50
+ "agent": { "type": "string" }
51
+ }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://flow-agents.dev/schemas/knowledge/health-report.schema.json",
4
+ "title": "Knowledge Health Report",
5
+ "description": "The schema-valid output of a provider-agnostic health verb (duplicate detection, dependency-link integrity). The same report shape is produced regardless of which provider supplied the graph, which is what lets one health command run identically over vault, git-repo, and work-item.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema_version", "check", "provider", "generated_at", "summary", "findings"],
9
+ "properties": {
10
+ "schema_version": { "const": "1.0" },
11
+ "check": {
12
+ "type": "string",
13
+ "enum": ["duplicate-detection", "dependency-link-integrity"],
14
+ "description": "Which health verb produced the report."
15
+ },
16
+ "provider": { "type": "string", "minLength": 1, "description": "Provider id the graph was read from." },
17
+ "generated_at": { "type": "string", "minLength": 1 },
18
+ "summary": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "required": ["nodes_examined", "edges_examined", "finding_count"],
22
+ "properties": {
23
+ "nodes_examined": { "type": "integer" },
24
+ "edges_examined": { "type": "integer" },
25
+ "finding_count": { "type": "integer" }
26
+ }
27
+ },
28
+ "findings": {
29
+ "type": "array",
30
+ "items": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["kind", "severity", "message", "evidence"],
34
+ "properties": {
35
+ "kind": { "type": "string", "minLength": 1 },
36
+ "severity": { "type": "string", "enum": ["info", "warning", "error"] },
37
+ "node_ids": { "type": "array", "items": { "type": "string" } },
38
+ "edge_ids": { "type": "array", "items": { "type": "string" } },
39
+ "message": { "type": "string", "minLength": 1 },
40
+ "evidence": { "type": "object" }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://flow-agents.dev/schemas/knowledge/node.schema.json",
4
+ "title": "Knowledge Graph Node",
5
+ "description": "A typed node in the Knowledge Kit graph model. type is EXTENSIBLE: the recommended core vocabulary is note, decision, issue, session, person, but providers MAY emit additional types. Every node carries provenance so any assertion can be traced back to the store it came from.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["id", "type", "title", "provenance"],
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "minLength": 1,
13
+ "description": "Provider-scoped stable identifier, unique within a single readGraph result."
14
+ },
15
+ "type": {
16
+ "type": "string",
17
+ "minLength": 1,
18
+ "description": "Node type. Recommended core vocabulary: note, decision, issue, session, person. Extensible — unknown types MUST NOT be rejected."
19
+ },
20
+ "title": {
21
+ "type": "string",
22
+ "minLength": 1,
23
+ "description": "Human-readable title used for display and duplicate detection."
24
+ },
25
+ "body": {
26
+ "type": "string",
27
+ "description": "Primary content. Format is provider/type-specific."
28
+ },
29
+ "attributes": {
30
+ "type": "object",
31
+ "description": "Provider-specific structured fields (e.g. issue state/labels, decision status, note category/tags)."
32
+ },
33
+ "provenance": { "$ref": "#/$defs/provenance" }
34
+ },
35
+ "$defs": {
36
+ "provenance": {
37
+ "type": "object",
38
+ "additionalProperties": false,
39
+ "required": ["provider", "source", "retrieved_at"],
40
+ "properties": {
41
+ "provider": { "type": "string", "minLength": 1, "description": "Provider id that produced this element (e.g. markdown-vault, git-repo, work-item)." },
42
+ "source": { "type": "string", "minLength": 1, "description": "The concrete origin: a file path, an issue ref, or a URL." },
43
+ "locator": { "type": "string", "description": "Optional finer locator within the source (line, anchor, frontmatter field)." },
44
+ "retrieved_at": { "type": "string", "minLength": 1, "description": "ISO-8601 timestamp the element was read." },
45
+ "agent": { "type": "string", "description": "Optional agent/process id that performed the read." }
46
+ }
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://flow-agents.dev/schemas/knowledge/proposal.schema.json",
4
+ "title": "Knowledge Store Write Proposal",
5
+ "description": "The ONLY write artifact a knowledge store provider may emit. Providers over human-curated stores are proposals-only: proposeWrite() never mutates the store and can only ever return a Proposal whose status is the const \"proposed\". Enactment happens downstream (a human edit, the decision-registry promote step, or a gh comment/label the operator files) — never inside the provider. The `rendered` field carries the provider-native form the enactor would apply (decision-registry topic frontmatter for git-repo; an issue comment/label body for work-item; a vault note with frontmatter+wikilinks for markdown-vault).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schema_version", "provider", "kind", "target", "status", "provenance"],
9
+ "properties": {
10
+ "schema_version": { "const": "1.0" },
11
+ "provider": { "type": "string", "minLength": 1 },
12
+ "kind": {
13
+ "type": "string",
14
+ "enum": ["create-node", "update-node", "add-edge", "comment", "label", "decision-topic"],
15
+ "description": "The shape of the proposed change."
16
+ },
17
+ "target": {
18
+ "type": "object",
19
+ "description": "Where the change would land if enacted (file path, issue number, store id). Descriptive only — the provider does not touch it."
20
+ },
21
+ "payload": {
22
+ "type": "object",
23
+ "description": "The structured node/edge/field the proposal would introduce."
24
+ },
25
+ "rendered": {
26
+ "type": "string",
27
+ "description": "The provider-native rendered form an enactor would apply (topic-file frontmatter, gh comment body, vault note)."
28
+ },
29
+ "status": {
30
+ "const": "proposed",
31
+ "description": "Always the literal \"proposed\". A provider can NEVER return an applied proposal — this const is the structural expression of proposals-only discipline."
32
+ },
33
+ "rationale": {
34
+ "type": "string",
35
+ "description": "Why the change is proposed."
36
+ },
37
+ "provenance": { "$ref": "#/$defs/provenance" }
38
+ },
39
+ "$defs": {
40
+ "provenance": {
41
+ "type": "object",
42
+ "additionalProperties": false,
43
+ "required": ["provider", "source", "retrieved_at"],
44
+ "properties": {
45
+ "provider": { "type": "string", "minLength": 1 },
46
+ "source": { "type": "string", "minLength": 1 },
47
+ "locator": { "type": "string" },
48
+ "retrieved_at": { "type": "string", "minLength": 1 },
49
+ "agent": { "type": "string" }
50
+ }
51
+ }
52
+ }
53
+ }
package/scripts/README.md CHANGED
@@ -70,6 +70,7 @@ renamed, or changes category, update the table and the validator together.
70
70
  | `lib/actor-identity.js` | shared hook library | `evals/integration/test_actor_identity.sh` | Shared runtime-agnostic actor identity resolver (`resolveActor`) consumed by `workflow-steering.js` and `workflow-sidecar.js` to retire the shared `"local"` liveness-actor default (issue #287). |
71
71
  | `lib/audit-transport.sh` | shared hook library | `evals/integration/test_hook_category_behaviors.sh`, `evals/integration/test_telemetry.sh` | Shared audit event transport functions. |
72
72
  | `lib/config-protection-remedies.js` | shared hook library | `evals/integration/test_hook_category_behaviors.sh` | Sanctioned-remedy table for `config-protection.js` (WS8/ADR 0020 extraction). |
73
+ | `lib/current-pointer.js` | shared hook library | `evals/integration/test_current_json_per_actor.sh` | Shared per-actor "current" pointer reader/writer (`readCurrentPointer`, `writePerActorCurrent`) — the compat-shim fallback (per-actor `current/<actor>.json` first, legacy global `current.json` fallback) every actor-aware consumer routes through (issue #291). |
73
74
  | `lib/hook-flags.js` | shared hook library | `evals/integration/test_hook_category_behaviors.sh` | Shared profile/disable flag parsing. |
74
75
  | `lib/liveness-heartbeat.js` | shared hook library | `evals/integration/test_liveness_heartbeat.sh` | Shared tool-activity liveness heartbeat (`maybeEmitHeartbeat`); rides `postToolUse` across all four telemetry hook wrappers, throttled and fail-open (issue #288). |
75
76
  | `lib/liveness-policy.js` | shared hook library | `evals/integration/test_workflow_sidecar_writer.sh`, `evals/integration/test_liveness_heartbeat.sh` | Shared liveness on/off predicate (`isLivenessEnabled`, default-on/opt-out) and TTL/heartbeat-throttle default resolution (issue #288). |