@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.
- package/.github/CODEOWNERS +8 -0
- package/.github/workflows/ci.yml +20 -0
- package/.github/workflows/trust-reconcile.yml +62 -4
- package/CHANGELOG.md +38 -0
- package/CONTEXT.md +88 -1
- package/README.md +4 -0
- package/build/src/cli/assignment-provider.d.ts +144 -0
- package/build/src/cli/assignment-provider.js +805 -0
- package/build/src/cli/effective-assignment-provider-settings.d.ts +1 -0
- package/build/src/cli/effective-assignment-provider-settings.js +125 -0
- package/build/src/cli/validate-workflow-artifacts.js +5 -1
- package/build/src/cli/workflow-sidecar.d.ts +2 -2
- package/build/src/cli/workflow-sidecar.js +549 -145
- package/build/src/cli.js +6 -0
- package/build/src/lib/flow-resolver.d.ts +12 -6
- package/build/src/lib/flow-resolver.js +30 -14
- package/build/src/tools/validate-source-tree.js +2 -0
- package/context/contracts/artifact-contract.md +2 -0
- package/context/contracts/assignment-provider-contract.md +248 -0
- package/context/contracts/builder-kit-workflow-state-contract.md +2 -0
- package/context/contracts/decision-registry-contract.md +2 -0
- package/context/contracts/delivery-contract.md +2 -0
- package/context/contracts/execution-contract.md +25 -0
- package/context/contracts/governance-adapter-contract.md +2 -0
- package/context/contracts/knowledge-store-contract.md +197 -0
- package/context/contracts/planning-contract.md +2 -0
- package/context/contracts/probe-docs-write-contract.md +187 -0
- package/context/contracts/review-contract.md +2 -0
- package/context/contracts/sandbox-policy.md +2 -0
- package/context/contracts/standing-directives.md +13 -0
- package/context/contracts/verification-contract.md +2 -0
- package/context/contracts/work-item-contract.md +2 -0
- package/context/scripts/hooks/config-protection.js +14 -1
- package/context/scripts/hooks/lib/config-protection-remedies.js +9 -0
- package/context/scripts/hooks/stop-goal-fit.js +4 -2
- package/context/scripts/hooks/workflow-steering.js +42 -0
- package/context/settings/assignment-provider-settings.json +33 -0
- package/docs/adr/0001-flow-agents-consumes-flow.md +2 -0
- package/docs/adr/0002-flow-kits-as-extension-unit.md +2 -0
- package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +2 -0
- package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
- package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
- package/docs/adr/0006-typescript-first-source-policy.md +2 -0
- package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
- package/docs/adr/0007-skill-audit.md +2 -0
- package/docs/adr/0008-kit-operation-boundary.md +2 -0
- package/docs/adr/0009-canonical-hook-core-kit-boundary.md +2 -0
- package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +2 -0
- package/docs/adr/0011-mcp-posture.md +2 -0
- package/docs/adr/0012-agent-coordination-as-liveness-claims.md +2 -0
- package/docs/adr/0013-context-lifecycle.md +2 -0
- package/docs/adr/0014-core-vs-domain-kit-boundary.md +2 -0
- package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +2 -0
- package/docs/adr/0016-three-hard-boundary-model.md +2 -0
- package/docs/adr/0017-anti-gaming-trust-security-model.md +2 -0
- package/docs/adr/0018-freeze-local-shell-heuristics.md +2 -0
- package/docs/adr/0019-kit-dependency-ownership.md +2 -0
- package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +2 -0
- package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +3 -1
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +182 -0
- package/docs/adr/README.md +49 -0
- package/docs/adr/index.md +34 -0
- package/docs/context-map.md +1 -0
- package/docs/decisions/agent-coordination.md +20 -0
- package/docs/decisions/anti-gaming-trust-security.md +20 -0
- package/docs/decisions/context-lifecycle.md +18 -0
- package/docs/decisions/core-domain-kit-boundary.md +18 -0
- package/docs/decisions/flow-flow-agents-boundary.md +18 -0
- package/docs/decisions/flow-kit.md +20 -0
- package/docs/decisions/flow-skill-kit-tool-boundary.md +20 -0
- package/docs/decisions/graph-knowledge-provider.md +63 -0
- package/docs/decisions/hook-core-kit-boundary.md +18 -0
- package/docs/decisions/index.md +22 -0
- package/docs/decisions/kit-dependency-ownership.md +18 -0
- package/docs/decisions/kit-operation-boundary.md +18 -0
- package/docs/decisions/knowledge-promote-sub-flow.md +59 -0
- package/docs/decisions/knowledge-store-provider.md +51 -0
- package/docs/decisions/kontour-resource-contract.md +18 -0
- package/docs/decisions/mcp-posture.md +18 -0
- package/docs/decisions/model-routing.md +63 -0
- package/docs/decisions/standing-directives.md +66 -0
- package/docs/decisions/three-hard-boundary-model.md +18 -0
- package/docs/decisions/trust-reconcile.md +20 -0
- package/docs/decisions/typescript-source-policy.md +48 -0
- package/docs/decisions/workflow-enforcement.md +18 -0
- package/docs/decisions/workflow-trust-state.md +20 -0
- package/docs/fixture-ownership.md +1 -0
- package/docs/workflow-shared-contracts.md +2 -1
- package/docs/workflow-usage-guide.md +7 -1
- package/evals/ci/run-baseline.sh +10 -0
- package/evals/fixtures/assignment-provider/actor-a.json +6 -0
- package/evals/fixtures/assignment-provider/actor-b.json +6 -0
- package/evals/fixtures/assignment-provider/github-issue-claimed.json +27 -0
- package/evals/fixtures/assignment-provider/github-issue-unassigned.json +7 -0
- package/evals/fixtures/assignment-provider/hostile-effective-state.json +11 -0
- package/evals/fixtures/assignment-provider/liveness-fresh.json +9 -0
- package/evals/fixtures/assignment-provider/liveness-stale.json +9 -0
- package/evals/integration/test_assignment_provider_github.sh +318 -0
- package/evals/integration/test_assignment_provider_local_file.sh +222 -0
- package/evals/integration/test_critique_supersession_roundtrip.sh +182 -0
- package/evals/integration/test_current_json_per_actor.sh +516 -0
- package/evals/integration/test_ensure_session_ownership_guard.sh +435 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_gate_lockdown.sh +10 -0
- package/evals/integration/test_publish_delivery.sh +21 -4
- package/evals/integration/test_pull_work_assignment_join.sh +132 -0
- package/evals/integration/test_pull_work_liveness_preflight.sh +20 -7
- package/evals/integration/test_reconcile_soundness.sh +33 -9
- package/evals/integration/test_trust_reconcile.sh +9 -8
- package/evals/integration/test_trust_reconcile_negatives.sh +608 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +91 -3
- package/evals/run.sh +14 -0
- package/evals/static/test_flowdef_codeowners_coverage.sh +6 -0
- package/evals/static/test_knowledge_providers.sh +32 -0
- package/evals/static/test_workflow_skills.sh +15 -2
- package/kits/builder/skills/deliver/SKILL.md +36 -0
- package/kits/builder/skills/design-probe/SKILL.md +37 -0
- package/kits/builder/skills/pickup-probe/SKILL.md +4 -4
- package/kits/builder/skills/pull-work/SKILL.md +78 -10
- package/kits/knowledge/adapters/default-store/index.js +92 -4
- package/kits/knowledge/adapters/obsidian-store/index.js +69 -4
- package/kits/knowledge/adapters/shared/codec.js +141 -0
- package/kits/knowledge/docs/README.md +121 -2
- package/kits/knowledge/docs/store-contract.md +112 -4
- package/kits/knowledge/evals/contract-suite/suite.test.js +176 -0
- package/kits/knowledge/flows/promote.flow.json +84 -0
- package/kits/knowledge/kit.json +50 -0
- package/kits/knowledge/promote/distill.js +96 -0
- package/kits/knowledge/promote/fixtures/contradiction-registry/CONTEXT.md +7 -0
- package/kits/knowledge/promote/fixtures/contradiction-registry/docs/decisions/cache-eviction-policy.md +12 -0
- package/kits/knowledge/promote/fixtures/contradiction-registry/docs/decisions/cache-eviction.md +12 -0
- package/kits/knowledge/promote/fixtures/repo/CONTEXT.md +11 -0
- package/kits/knowledge/promote/fixtures/repo/docs/decisions/index.md +9 -0
- package/kits/knowledge/promote/fixtures/repo/docs/decisions/liveness-claim.md +12 -0
- package/kits/knowledge/promote/fixtures/session/acceptance.json +9 -0
- package/kits/knowledge/promote/fixtures/session/agents/tool-planner/events.jsonl +1 -0
- package/kits/knowledge/promote/fixtures/session/agents/tool-verifier/events.jsonl +1 -0
- package/kits/knowledge/promote/fixtures/session/agents/tool-worker-1-1/events.jsonl +1 -0
- package/kits/knowledge/promote/fixtures/session/kontourai-flow-agents-287--deliver.md +47 -0
- package/kits/knowledge/promote/fixtures/session/learning.json +35 -0
- package/kits/knowledge/promote/fixtures/session/state.json +19 -0
- package/kits/knowledge/promote/health.js +137 -0
- package/kits/knowledge/promote/index.js +176 -0
- package/kits/knowledge/promote/ingest.js +103 -0
- package/kits/knowledge/promote/lib.js +132 -0
- package/kits/knowledge/promote/link.js +84 -0
- package/kits/knowledge/promote/promote.test.js +174 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/CONTEXT.md +12 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/old-sprocket-shape.md +13 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/sprocket-shape.md +14 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/widget-format.md +14 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/learnings/fixture-learning.md +7 -0
- package/kits/knowledge/providers/conformance/fixtures/work-item/issues.json +30 -0
- package/kits/knowledge/providers/conformance/suite.test.js +143 -0
- package/kits/knowledge/providers/git-repo/index.js +236 -0
- package/kits/knowledge/providers/health/health-pass.test.js +99 -0
- package/kits/knowledge/providers/health/index.js +153 -0
- package/kits/knowledge/providers/index.js +25 -0
- package/kits/knowledge/providers/lib/model.js +91 -0
- package/kits/knowledge/providers/lib/schema-validate.js +119 -0
- package/kits/knowledge/providers/markdown-vault/index.js +169 -0
- package/kits/knowledge/providers/neo4j/connection.js +121 -0
- package/kits/knowledge/providers/neo4j/cypher.js +190 -0
- package/kits/knowledge/providers/neo4j/fake-driver.js +123 -0
- package/kits/knowledge/providers/neo4j/fixtures/spike-ground-truth.json +29 -0
- package/kits/knowledge/providers/neo4j/graph-queries.js +210 -0
- package/kits/knowledge/providers/neo4j/index.js +280 -0
- package/kits/knowledge/providers/neo4j/integration.test.js +122 -0
- package/kits/knowledge/providers/neo4j/neo4j.test.js +219 -0
- package/kits/knowledge/providers/neo4j/sync.js +235 -0
- package/kits/knowledge/providers/work-item/index.js +204 -0
- package/package.json +7 -3
- package/schemas/assignment-provider-settings.schema.json +125 -0
- package/schemas/knowledge/edge.schema.json +54 -0
- package/schemas/knowledge/health-report.schema.json +45 -0
- package/schemas/knowledge/node.schema.json +49 -0
- package/schemas/knowledge/proposal.schema.json +53 -0
- package/scripts/README.md +1 -0
- package/scripts/ci/trust-reconcile.js +521 -24
- package/scripts/freeze-adrs.mjs +364 -0
- package/scripts/hooks/config-protection.js +14 -1
- package/scripts/hooks/evidence-capture.js +4 -1
- package/scripts/hooks/lib/config-protection-remedies.js +9 -0
- package/scripts/hooks/lib/current-pointer.js +123 -0
- package/scripts/hooks/lib/liveness-heartbeat.js +75 -35
- package/scripts/hooks/stop-goal-fit.js +4 -2
- package/scripts/hooks/workflow-steering.js +42 -0
- package/scripts/statusline/flow-agents-statusline.js +3 -1
- package/src/cli/assignment-provider.ts +927 -0
- package/src/cli/effective-assignment-provider-settings.ts +112 -0
- package/src/cli/validate-workflow-artifacts.ts +5 -1
- package/src/cli/workflow-sidecar.ts +577 -139
- package/src/cli.ts +6 -0
- package/src/lib/flow-resolver.ts +35 -14
- package/src/tools/validate-source-tree.ts +2 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Knowledge Store Provider Contract
|
|
2
|
+
|
|
3
|
+
Normative contract for the Knowledge Kit's typed graph MODEL and its pluggable
|
|
4
|
+
storage/sync PROVIDERS. The model (nodes, edges, provenance) is owned by the Kit;
|
|
5
|
+
storage and synchronisation are provider concerns. The same ingest/link/health
|
|
6
|
+
verbs run over every provider, so backlog hygiene is a knowledge-health pass over
|
|
7
|
+
the work-item provider rather than a bespoke tool.
|
|
8
|
+
|
|
9
|
+
Machine-checkable structure lives in `schemas/knowledge/*.schema.json`
|
|
10
|
+
(`node`, `edge`, `proposal`, `health-report`). Reference implementations live in
|
|
11
|
+
`kits/knowledge/providers/` (markdown-vault, git-repo, work-item) with health
|
|
12
|
+
verbs in `kits/knowledge/providers/health/` and the conformance suite in
|
|
13
|
+
`kits/knowledge/providers/conformance/`.
|
|
14
|
+
|
|
15
|
+
This contract applies the portfolio provider pattern (hachure format/adapters;
|
|
16
|
+
Builder Kit work-item contract/providers) to knowledge, which until now was fused
|
|
17
|
+
to one storage shape (the markdown vault).
|
|
18
|
+
|
|
19
|
+
## 1. Graph model
|
|
20
|
+
|
|
21
|
+
### 1.1 Node
|
|
22
|
+
|
|
23
|
+
A node is a typed, identified unit of knowledge. Validated by
|
|
24
|
+
`schemas/knowledge/node.schema.json`.
|
|
25
|
+
|
|
26
|
+
| Field | Required | Meaning |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| `id` | yes | Provider-scoped stable id, unique within a single `readGraph` result. |
|
|
29
|
+
| `type` | yes | Node type (see §1.3). EXTENSIBLE. |
|
|
30
|
+
| `title` | yes | Human-readable title; the basis for duplicate detection. |
|
|
31
|
+
| `body` | no | Primary content; format is provider/type-specific. |
|
|
32
|
+
| `attributes` | no | Provider-specific structured fields (issue state/labels, decision status, note category/tags). |
|
|
33
|
+
| `provenance` | yes | Where this node was read from (§1.4). |
|
|
34
|
+
|
|
35
|
+
### 1.2 Edge
|
|
36
|
+
|
|
37
|
+
An edge is a typed, directed relationship. Validated by
|
|
38
|
+
`schemas/knowledge/edge.schema.json`.
|
|
39
|
+
|
|
40
|
+
| Field | Required | Meaning |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `id` | yes | Provider-scoped stable edge id. |
|
|
43
|
+
| `type` | yes | One of the CLOSED edge vocabulary (§1.3). |
|
|
44
|
+
| `from` | yes | Source node id. |
|
|
45
|
+
| `to` | yes | Target node id. |
|
|
46
|
+
| `resolved` | no | `true` (default) means `to` is expected to resolve to a node in the same graph (an internal dependency link); `false` marks a deliberate external reference (e.g. `evidence-of` a PR outside the store). |
|
|
47
|
+
| `attributes` | no | Provider-specific structured fields. |
|
|
48
|
+
| `provenance` | yes | Where this edge was derived from (§1.4). |
|
|
49
|
+
|
|
50
|
+
### 1.3 Vocabularies
|
|
51
|
+
|
|
52
|
+
**Node types are EXTENSIBLE.** The recommended core vocabulary is `note`,
|
|
53
|
+
`decision`, `issue`, `session`, `person`. Providers MAY emit additional types;
|
|
54
|
+
consumers MUST NOT reject unknown node types.
|
|
55
|
+
|
|
56
|
+
**Edge types are CLOSED** (schema-enforced enum):
|
|
57
|
+
|
|
58
|
+
| Edge type | Direction | Meaning |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `supersedes` | from → to | `from` replaces `to`. |
|
|
61
|
+
| `merged-into` | from → to | `from` was folded into `to`. |
|
|
62
|
+
| `blocks` | from → to | `from` must complete before `to`. |
|
|
63
|
+
| `evidence-of` | from → to | `from` is provenance/evidence for `to`. |
|
|
64
|
+
| `mentions` | from → to | `from` references `to` in prose. |
|
|
65
|
+
| `relates` | from → to | General association. |
|
|
66
|
+
|
|
67
|
+
Adding an edge type is a contract change (schema + this table), because health
|
|
68
|
+
verbs reason over the closed vocabulary (e.g. `blocks` is the dependency-link
|
|
69
|
+
relation).
|
|
70
|
+
|
|
71
|
+
### 1.4 Provenance
|
|
72
|
+
|
|
73
|
+
Provenance appears on BOTH nodes and edges. Every assertion in the graph is
|
|
74
|
+
traceable back to the store it came from — no orphan facts.
|
|
75
|
+
|
|
76
|
+
| Field | Required | Meaning |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| `provider` | yes | Provider id that produced the element. |
|
|
79
|
+
| `source` | yes | Concrete origin: a file path, an issue ref, or a URL. |
|
|
80
|
+
| `locator` | no | Finer locator (line, anchor, frontmatter field, link kind). |
|
|
81
|
+
| `retrieved_at` | yes | ISO-8601 read timestamp. |
|
|
82
|
+
| `agent` | no | Agent/process id that performed the read. |
|
|
83
|
+
|
|
84
|
+
## 2. Provider interface
|
|
85
|
+
|
|
86
|
+
A provider is an ESM module exporting a default class constructed with a
|
|
87
|
+
provider-specific options object. Every provider implements the READ interface
|
|
88
|
+
and the PROPOSALS-ONLY write interface.
|
|
89
|
+
|
|
90
|
+
### 2.1 Read
|
|
91
|
+
|
|
92
|
+
| Method | Signature | Behaviour |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| `capabilities()` | `() => Capabilities` | Static description (§2.3). Synchronous. |
|
|
95
|
+
| `readNodes(options?)` | `(o?) => Promise<Node[]>` | All nodes; `options.type` filters by node type. Every node schema-valid. |
|
|
96
|
+
| `readEdges()` | `() => Promise<Edge[]>` | All edges; every edge schema-valid and in the closed vocabulary. |
|
|
97
|
+
| `queryByType(type)` | `(t) => Promise<Node[]>` | Convenience: the nodes of one type (a consistent subset of `readNodes`). |
|
|
98
|
+
| `readGraph()` | `() => Promise<{nodes, edges}>` | The full graph. |
|
|
99
|
+
|
|
100
|
+
### 2.2 Write — proposals only
|
|
101
|
+
|
|
102
|
+
Knowledge stores are HUMAN-CURATED. No provider auto-writes to a store. The only
|
|
103
|
+
write surface is:
|
|
104
|
+
|
|
105
|
+
| Method | Signature | Behaviour |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `proposeWrite(intent)` | `(i) => Promise<Proposal>` | Returns a `Proposal` (§2.4) describing an intended change. MUST NOT mutate the store, disk, or the remote board. |
|
|
108
|
+
|
|
109
|
+
**How proposals-only is expressed structurally:** the `Proposal` schema fixes
|
|
110
|
+
`status` to the const `"proposed"`. A provider can never return an applied
|
|
111
|
+
proposal, and the conformance suite asserts that `readNodes()` is byte-stable
|
|
112
|
+
across a `proposeWrite` call. Enactment happens DOWNSTREAM of the provider:
|
|
113
|
+
|
|
114
|
+
- **git-repo** renders a decision-registry topic file (frontmatter compatible
|
|
115
|
+
with `schemas/decision-record.schema.json`) shaped for the **promote sub-flow**
|
|
116
|
+
(`docs/decisions/promotion-gate.md`). The promote step is the enactor.
|
|
117
|
+
- **work-item** renders a draft `gh issue comment` / `gh issue edit --add-label`
|
|
118
|
+
the operator files. The provider never runs a mutating `gh` command.
|
|
119
|
+
- **markdown-vault** renders a note in native frontmatter + `[[wikilink]]` form
|
|
120
|
+
for the Knowledge Kit ingest/compile flow to file.
|
|
121
|
+
|
|
122
|
+
This is the same proposals-only discipline as the store contract's
|
|
123
|
+
`propose → apply/reject` gate and the ADR-0003-style "proposals, never silent
|
|
124
|
+
writes to a curated store" precedent. A provider is the propose half; a human or
|
|
125
|
+
a gated flow is the apply half.
|
|
126
|
+
|
|
127
|
+
### 2.3 Capabilities
|
|
128
|
+
|
|
129
|
+
`capabilities()` returns `{ id, node_types, edge_types, writable, write_mode,
|
|
130
|
+
proposal_targets, source_of_truth }`. `writable` is always `false` and
|
|
131
|
+
`write_mode` is always `"proposals-only"` in this version — a graph-database
|
|
132
|
+
provider that owns its store is a separate, gated spike (issue #318), not a
|
|
133
|
+
loophole here.
|
|
134
|
+
|
|
135
|
+
### 2.4 Proposal
|
|
136
|
+
|
|
137
|
+
Validated by `schemas/knowledge/proposal.schema.json`.
|
|
138
|
+
|
|
139
|
+
| Field | Required | Meaning |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `schema_version` | yes | `"1.0"`. |
|
|
142
|
+
| `provider` | yes | Emitting provider id. |
|
|
143
|
+
| `kind` | yes | `create-node` \| `update-node` \| `add-edge` \| `comment` \| `label` \| `decision-topic`. |
|
|
144
|
+
| `target` | yes | Where the change would land (descriptive only). |
|
|
145
|
+
| `payload` | no | The structured node/edge/field proposed. |
|
|
146
|
+
| `rendered` | no | The provider-native form an enactor would apply. |
|
|
147
|
+
| `status` | yes | The const `"proposed"`. |
|
|
148
|
+
| `rationale` | no | Why the change is proposed. |
|
|
149
|
+
| `provenance` | yes | §1.4. |
|
|
150
|
+
|
|
151
|
+
## 3. Health verbs
|
|
152
|
+
|
|
153
|
+
Health verbs are PROVIDER-AGNOSTIC pure functions over a graph
|
|
154
|
+
(`{ nodes, edges }`) — they never inspect provider internals, which is what lets
|
|
155
|
+
one health command run identically over any provider (R4). Each returns a
|
|
156
|
+
schema-valid Knowledge Health Report
|
|
157
|
+
(`schemas/knowledge/health-report.schema.json`). Reference verbs live in
|
|
158
|
+
`kits/knowledge/providers/health/`.
|
|
159
|
+
|
|
160
|
+
| Verb | Report `check` | Behaviour |
|
|
161
|
+
| --- | --- | --- |
|
|
162
|
+
| `detectDuplicates(graph, o?)` | `duplicate-detection` | Flags node pairs whose titles match / are highly similar (token Jaccard ≥ `threshold`, default 0.7; same-type by default). |
|
|
163
|
+
| `checkDependencyLinkIntegrity(graph, o?)` | `dependency-link-integrity` | Flags dependency edges (default type `blocks`) whose endpoints are missing from the graph, unless the edge is `resolved:false` (deliberate external ref). |
|
|
164
|
+
|
|
165
|
+
Every finding cites its evidence (the compared titles + similarity, or the edge
|
|
166
|
+
+ which endpoint is missing). A report's `summary.finding_count` MUST equal
|
|
167
|
+
`findings.length`.
|
|
168
|
+
|
|
169
|
+
## 4. Reference providers
|
|
170
|
+
|
|
171
|
+
| Provider | Reads | Node types | Write proposal |
|
|
172
|
+
| --- | --- | --- | --- |
|
|
173
|
+
| `markdown-vault` | The existing Knowledge Kit store (markdown + frontmatter + `[[wikilinks]]` + graph index) via an existing store adapter — thin wrapper, existing skills unaffected. | `note`, `person` | `create-node` rendered as a native vault note. |
|
|
174
|
+
| `git-repo` | `docs/decisions/*` (tombstones → `supersedes`/`merged-into`; `evidence[]` → `evidence-of`), CONTEXT.md glossary, `docs/learnings/*`. | `decision`, `note` | `decision-topic` shaped for the promote sub-flow. |
|
|
175
|
+
| `work-item` | GitHub issues via an INJECTABLE runner (default `gh`), `flow-agents:work-item-metadata` blockers + prose refs → `blocks`/`relates`. | `issue` | `comment` / `label` draft. |
|
|
176
|
+
|
|
177
|
+
## 5. Conformance
|
|
178
|
+
|
|
179
|
+
`kits/knowledge/providers/conformance/suite.test.js` is the parameterized
|
|
180
|
+
conformance suite EVERY provider must pass (issue #317, AC3). It asserts:
|
|
181
|
+
capabilities declare proposals-only/non-writable; `readNodes` returns unique,
|
|
182
|
+
schema-valid nodes with provider provenance; `readEdges` returns schema-valid
|
|
183
|
+
edges in the closed vocabulary; `queryByType` is a consistent subset of
|
|
184
|
+
`readNodes`; `readGraph` returns `{nodes, edges}`; and `proposeWrite` returns a
|
|
185
|
+
schema-valid `"proposed"` proposal that mutates nothing.
|
|
186
|
+
|
|
187
|
+
`kits/knowledge/providers/health/health-pass.test.js` covers AC1 (the same
|
|
188
|
+
health command over vault + git-repo yields schema-valid reports) and AC2 (a
|
|
189
|
+
backlog pass over the work-item adapter, driven by a SEEDED fixture, flags a
|
|
190
|
+
duplicate and a broken blocker link — never the real board).
|
|
191
|
+
|
|
192
|
+
## 6. Non-goals
|
|
193
|
+
|
|
194
|
+
Graph-database provider (issue #318 spike; adoption gated on a real query failing
|
|
195
|
+
over files); embeddings; auto-writes to any human-curated store; personal-vault
|
|
196
|
+
feature changes. This contract adds a model + read/propose interface over stores
|
|
197
|
+
operated today; it does not change how those stores are curated.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Planning Contract
|
|
2
2
|
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
3
5
|
Planning turns a user goal into an executable implementation plan without writing production code.
|
|
4
6
|
|
|
5
7
|
## Required Inputs
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Probe Docs-Write Contract
|
|
2
|
+
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
5
|
+
Normative contract for how a Probe (`design-probe`, and its Builder Kit
|
|
6
|
+
`pickup-probe` specialization) writes durable documentation as understanding
|
|
7
|
+
crystallizes during probing. This is the docs-write half of the grill-with-docs
|
|
8
|
+
pattern: a Probe already interviews and aligns; this contract says what a Probe
|
|
9
|
+
must leave behind in durable docs, not just in the session artifact.
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Probe outcomes previously landed only in session artifacts (`.kontourai/flow-agents/<slug>/`):
|
|
14
|
+
vocabulary and decisions crystallized while probing had to be re-extracted later,
|
|
15
|
+
or were lost when the session archived. Grill-with-docs' whole point is that the
|
|
16
|
+
interview leaves living documentation behind, not session-local notes. This
|
|
17
|
+
contract makes that the default behavior for every Probe, not an occasional
|
|
18
|
+
manual follow-up.
|
|
19
|
+
|
|
20
|
+
## Scope
|
|
21
|
+
|
|
22
|
+
Applies to `design-probe` (generic) and `pickup-probe` (the Builder Kit
|
|
23
|
+
pickup specialization) — see
|
|
24
|
+
[`kits/builder/skills/design-probe/SKILL.md`](../../kits/builder/skills/design-probe/SKILL.md)
|
|
25
|
+
and
|
|
26
|
+
[`kits/builder/skills/pickup-probe/SKILL.md`](../../kits/builder/skills/pickup-probe/SKILL.md).
|
|
27
|
+
It does not create a new skill; both existing Probe skills already own the
|
|
28
|
+
grill/grill-with-docs behavior (R1 in
|
|
29
|
+
[flow-agents#311](https://github.com/kontourai/flow-agents/issues/311)) — this
|
|
30
|
+
contract only normalizes what they write and where.
|
|
31
|
+
|
|
32
|
+
Out of scope: enforcement gating on Probe docs-writes (a separate issue),
|
|
33
|
+
`CONTEXT.md` restructuring beyond vocabulary sections, and repositories with no
|
|
34
|
+
`CONTEXT.md` (an open question the owner has not settled; non-blocking for
|
|
35
|
+
flow-agents, which always has one).
|
|
36
|
+
|
|
37
|
+
## The Two-Delta Rule
|
|
38
|
+
|
|
39
|
+
**Per crystallized understanding** — a question the Probe asked and the user (or
|
|
40
|
+
discoverable local evidence) definitively answered, not a still-open question or
|
|
41
|
+
an accepted gap — the Probe emits, in the same motion:
|
|
42
|
+
|
|
43
|
+
1. **A vocabulary delta** into `CONTEXT.md` (or the owning `CONTEXT-MAP.md`
|
|
44
|
+
context when one exists) — see [Vocabulary Delta](#1-vocabulary-delta-coin-first).
|
|
45
|
+
2. **A decision delta** into `docs/decisions/<slug>.md` — see
|
|
46
|
+
[Decision Delta](#2-decision-delta-retrieval-at-write).
|
|
47
|
+
3. **Transcript provenance** — the decision delta's `evidence[]` links the
|
|
48
|
+
session artifact that produced it — see
|
|
49
|
+
[Transcript Provenance](#3-transcript-provenance).
|
|
50
|
+
|
|
51
|
+
Not every Probe question needs both deltas. A question resolved by reading
|
|
52
|
+
existing docs/code needs neither (nothing crystallized — it was already
|
|
53
|
+
decided). A question about implementation detail, sequencing, or a transient
|
|
54
|
+
planning choice needs neither (it belongs in the workflow artifact, per
|
|
55
|
+
`context/contracts/artifact-contract.md`, not durable docs). The two-delta rule
|
|
56
|
+
applies specifically to a **decision subject** crystallizing: a durable,
|
|
57
|
+
reusable answer to a named domain question that other work will need to look up
|
|
58
|
+
later. If in doubt, ask: "will a future session need to know this answer without
|
|
59
|
+
re-deriving it?" — if yes, it is a decision subject and both deltas apply.
|
|
60
|
+
|
|
61
|
+
### 1. Vocabulary delta (coin-first)
|
|
62
|
+
|
|
63
|
+
**A decision subject may only use subject terms present in (or added to)
|
|
64
|
+
`CONTEXT.md` in the same session** (R2). Vocabulary is the topic namespace the
|
|
65
|
+
decision registry's slugs are drawn from
|
|
66
|
+
(`context/contracts/decision-registry-contract.md` § Slug rules), so the term
|
|
67
|
+
must exist before the topic file can be named.
|
|
68
|
+
|
|
69
|
+
- If the crystallized understanding's subject noun is **already** a `CONTEXT.md`
|
|
70
|
+
glossary term, the vocabulary delta may be a **refinement** of that entry
|
|
71
|
+
rather than a new one — e.g. removing "subject open" language once a decision
|
|
72
|
+
ratifies it, tightening a definition, or adding an `_Avoid_` term. This still
|
|
73
|
+
counts as a vocabulary delta: the glossary is a living document, not
|
|
74
|
+
write-once.
|
|
75
|
+
- If the subject noun is **absent**, **coin it first**: add a tight one- or
|
|
76
|
+
two-sentence `### <Term>` entry to `CONTEXT.md`'s Glossary section, in the
|
|
77
|
+
same style as existing entries (definition, optional `_Avoid_` line). Do this
|
|
78
|
+
before creating the decision file — the term is the slug's namespace, not an
|
|
79
|
+
afterthought.
|
|
80
|
+
- Keep the vocabulary delta to glossary-style terminology. Implementation
|
|
81
|
+
detail, rationale, and derivation stay out of `CONTEXT.md` (unchanged from the
|
|
82
|
+
Probe skills' existing docs discipline).
|
|
83
|
+
- If a `CONTEXT-MAP.md` exists and a more specific context owns the term, update
|
|
84
|
+
that context instead of assuming the root glossary owns every term.
|
|
85
|
+
|
|
86
|
+
### 2. Decision delta (retrieval-at-write)
|
|
87
|
+
|
|
88
|
+
Once the vocabulary exists, record the decision per
|
|
89
|
+
`context/contracts/decision-registry-contract.md` in full — this contract does
|
|
90
|
+
not repeat the frontmatter/slug/tombstone rules, only the Probe-specific
|
|
91
|
+
sequencing:
|
|
92
|
+
|
|
93
|
+
1. **Consult `docs/decisions/index.md`** (the always-loaded slug + one-liner
|
|
94
|
+
index) before writing anything, and propose **revise-vs-create**:
|
|
95
|
+
- An existing topic slug already covers the subject (including a
|
|
96
|
+
`needs-decision` stub seeded by ADR-freeze tooling, issue #314) → **revise
|
|
97
|
+
that file in place.** Flip `needs-decision` → `current` (or edit a `current`
|
|
98
|
+
file's body/`decided` date for a superseding decision), add the new
|
|
99
|
+
`evidence[]` refs, do not create a second file for the same subject.
|
|
100
|
+
- No topic covers the subject → confirm the vocabulary delta above landed,
|
|
101
|
+
then **create** `docs/decisions/<slug>.md` with the frontmatter shape
|
|
102
|
+
`context/contracts/decision-registry-contract.md` defines.
|
|
103
|
+
2. Run `npm run gen:decisions-index && npm run check:decisions` after every
|
|
104
|
+
decision-delta write. A Probe session is not done touching the registry until
|
|
105
|
+
both exit 0.
|
|
106
|
+
3. Never propose a numbered ADR. `docs/adr/` is frozen history
|
|
107
|
+
(`docs/adr/README.md`); a Probe's decision delta always targets the
|
|
108
|
+
topic-keyed registry, never a new `docs/adr/NNNN-*.md` file.
|
|
109
|
+
|
|
110
|
+
### 3. Transcript provenance
|
|
111
|
+
|
|
112
|
+
Every decision file a Probe creates or edits carries an `evidence[]` entry of
|
|
113
|
+
`kind: session-archive` pointing at the Probe's own session artifact (R3) — the
|
|
114
|
+
Markdown file recording the interview, decisions, and accepted gaps
|
|
115
|
+
(`.kontourai/flow-agents/<slug>/<slug>--design-probe.md`,
|
|
116
|
+
`<slug>--pull-work.md`, or the equivalent Probe record the active workflow
|
|
117
|
+
artifact uses).
|
|
118
|
+
|
|
119
|
+
- Record the **live session path** at write time, even though the topic file
|
|
120
|
+
and the decision-registry contract describe `session-archive` as pointing at
|
|
121
|
+
an *archived* artifact "promoted at retirement" — the Probe runs at shaping
|
|
122
|
+
time, before the session archives. This is a deliberate, documented seam: the
|
|
123
|
+
promote-then-archive gate (issue #312) is the retirement-time mechanism that
|
|
124
|
+
moves the session directory to `<artifact-root>/archive/<slug>/` and is
|
|
125
|
+
responsible for keeping already-written evidence refs accurate across that
|
|
126
|
+
move. The Probe's job is emitting the initial link, not the later archival
|
|
127
|
+
move; do not block a decision delta on the session having archived first.
|
|
128
|
+
- When the Probe is also citing a frozen ADR as prior provenance (e.g. upgrading
|
|
129
|
+
a `needs-decision` stub whose `evidence[]` already names one), keep that `adr`
|
|
130
|
+
evidence entry and add the `session-archive` entry alongside it — provenance
|
|
131
|
+
accumulates, it does not replace.
|
|
132
|
+
- Also add a `pr`/`issue` evidence entry once a PR or issue number exists for the
|
|
133
|
+
work, following the same pattern every other decision file in the registry
|
|
134
|
+
uses.
|
|
135
|
+
|
|
136
|
+
## Worked Example: Upgrading a Needs-Decision Stub
|
|
137
|
+
|
|
138
|
+
The ADR-freeze cutover (issue #314, landed in #368) seeded 17 `needs-decision`
|
|
139
|
+
stubs, each naming a subject with provenance in a frozen ADR but no ratified
|
|
140
|
+
living decision. These are the natural first targets for this contract: a Probe
|
|
141
|
+
that settles one of these subjects from existing evidence (the frozen ADR
|
|
142
|
+
already states a decision that remains true — nothing new needs deciding, only
|
|
143
|
+
recording) does the following:
|
|
144
|
+
|
|
145
|
+
1. **Read the stub** (`docs/decisions/<slug>.md`, `status: needs-decision`) and
|
|
146
|
+
its `evidence[]`-linked frozen ADR(s).
|
|
147
|
+
2. **Vocabulary delta**: the subject noun is already a `CONTEXT.md` term (seeded
|
|
148
|
+
alongside the stub during the freeze cutover) — refine its definition to
|
|
149
|
+
state the decision is now living rather than "subject open in the Decision
|
|
150
|
+
Registry," and keep the link to `docs/decisions/<slug>.md`.
|
|
151
|
+
3. **Decision delta**: consult `docs/decisions/index.md` — the slug already
|
|
152
|
+
exists (revise, not create). Edit `docs/decisions/<slug>.md`:
|
|
153
|
+
`status: needs-decision` → `current`, set `decided` to today, keep the
|
|
154
|
+
existing `adr` evidence ref(s), append a `session-archive` ref (this Probe's
|
|
155
|
+
session artifact) and a `pr` ref once the PR exists, and write the `# <Title>`
|
|
156
|
+
body stating the decision (drawn from the ADR) plus lean rationale.
|
|
157
|
+
4. Run `npm run gen:decisions-index && npm run check:decisions`; both must exit
|
|
158
|
+
0 before the Probe records the gate claim.
|
|
159
|
+
5. **Transcript provenance**: the session-archive evidence ref from step 3 is the
|
|
160
|
+
provenance link (R3).
|
|
161
|
+
|
|
162
|
+
This exercises all three rules end-to-end without inventing a new decision —
|
|
163
|
+
the frozen ADR already made the call; the Probe's job is only recording that it
|
|
164
|
+
is still true and giving it a living home.
|
|
165
|
+
|
|
166
|
+
## Relationship To Other Contracts
|
|
167
|
+
|
|
168
|
+
- **`context/contracts/decision-registry-contract.md`** is the normative
|
|
169
|
+
frontmatter/slug/tombstone/validation contract for every `docs/decisions/`
|
|
170
|
+
file, Probe-written or not. This contract only adds *when* and *in what
|
|
171
|
+
sequence* a Probe writes to that registry.
|
|
172
|
+
- **`context/contracts/artifact-contract.md`** governs the Probe's session
|
|
173
|
+
artifact itself (the thing this contract's transcript-provenance rule links
|
|
174
|
+
to) and the promote/archive lifecycle referenced above.
|
|
175
|
+
- **`docs/adr/README.md`** is where this contract is announced as the mechanism
|
|
176
|
+
that supersedes the prior "propose ADRs sparingly" Probe guidance — Probes
|
|
177
|
+
never write numbered ADRs; they write decision-registry deltas.
|
|
178
|
+
|
|
179
|
+
## Validation
|
|
180
|
+
|
|
181
|
+
- `npm run check:decisions` / `npm run gen:decisions-index` — the decision
|
|
182
|
+
delta must pass the registry validator and keep the index current (wired into
|
|
183
|
+
the required `source-and-static` CI lane).
|
|
184
|
+
- `evals/static/test_workflow_skills.sh` — asserts `design-probe` and
|
|
185
|
+
`pickup-probe` reference this contract and describe the two-delta behavior.
|
|
186
|
+
- `evals/static/test_decisions.sh` — regression coverage for the validator and
|
|
187
|
+
generator this contract's decision deltas depend on.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Review Contract
|
|
2
2
|
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
3
5
|
Review is report-only critique. It asks whether the changed code is maintainable, secure, consistent with project standards, and structurally sound before verification proves behavior.
|
|
4
6
|
|
|
5
7
|
## Purpose
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Sandbox Policy Contract
|
|
2
2
|
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
3
5
|
Workflow artifacts must name the execution boundary required for the work. The sandbox mode is a planning and delegation contract, not a substitute for the runtime permission model.
|
|
4
6
|
|
|
5
7
|
## Canonical Modes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Standing Directives Contract
|
|
2
|
+
|
|
3
|
+
Ratified owner directives are corrections and policies the owner has explicitly settled across sessions. They override default engineering conservatism (e.g. "keep the old path working just in case") wherever they apply. Each directive below is a durable, quotable statement — cite the number, not a paraphrase from memory. This file exists so a delegated agent that only sees one task in a plan still receives the same standing policy the orchestrator ratified; correction that lives only in one session's ephemeral context does not reach the agents who need it.
|
|
4
|
+
|
|
5
|
+
Read this file alongside the phase contract for the work you are doing. When a standing directive and a phase contract's default guidance conflict, the standing directive wins; if you believe a directive does not fit the situation, stop and report the conflict rather than silently picking a side.
|
|
6
|
+
|
|
7
|
+
## Directives
|
|
8
|
+
|
|
9
|
+
1. **No legacy fallbacks: migrate hard.** A compatibility path that keeps defective or superseded behavior reachable is prohibited regardless of how short-lived or self-healing it is; the remedy for old artifacts is a loud typed error naming the regeneration command. (Ratified July 2026. Evidence: [datum#3](https://github.com/kontourai/datum/pull/3) hard cutover to `.datum/config.json`; the fallback-removal follow-up to [flow-agents#344](https://github.com/kontourai/flow-agents/pull/344), tracked as [flow-agents#347](https://github.com/kontourai/flow-agents/issues/347).)
|
|
10
|
+
2. **Durable vs runtime paths: never invert them.** `.kontourai/` is gitignored runtime state; `.<product>/` (e.g. `.veritas/`, `.datum/`) is a product's durable, committed directory in consuming repos. (Ratified July 2026. Evidence: `.veritas/` precedent and the `.kontourai/` vs `.veritas/` distinction in [docs/repository-structure.md](../../docs/repository-structure.md); [datum#3](https://github.com/kontourai/datum/pull/3) `.datum/config.json`; [surface#110](https://github.com/kontourai/surface/issues/110), [surface#113](https://github.com/kontourai/surface/pull/113).)
|
|
11
|
+
3. **Evidence must be executable.** Check summaries/excerpts are runnable-from-repo-root commands (`true # ...` for prose that has no command); probe scripts live inside the session dir; reconcilable claims use manifest-exact bare labels. (Ratified July 2026.)
|
|
12
|
+
4. **Standalone-first, then integrate.** Build pieces to provide value alone, then push them through a real product integration to pressure-test the abstractions; file upstream issues for friction found during integration rather than working around it. (Ratified July 2026.)
|
|
13
|
+
5. **Regression rule for cutovers.** Proper before/after tests are required for any cutover; large regressions (e.g. more than 40% record loss, or a whole field-class disappearing) are surfaced as an explicit owner decision with data, never papered over — but a large regression does not block the cutover by default. (Ratified July 2026.)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Verification Contract
|
|
2
2
|
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
3
5
|
Verification is report-only. It proves whether the implementation satisfies the plan, the Definition Of Done, and the original user outcome.
|
|
4
6
|
|
|
5
7
|
## Required Inputs
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Work Item Contract
|
|
2
2
|
|
|
3
|
+
> Read [`context/contracts/standing-directives.md`](standing-directives.md) — ratified owner directives that override default engineering conservatism.
|
|
4
|
+
|
|
3
5
|
This contract defines the provider-neutral vocabulary for selecting, planning, and handing off backlog work. It is the source shape for provider-backed workflows such as `pull-work`; provider-specific adapters map into this model without making GitHub, Jira, Linear, or any other provider the generic language.
|
|
4
6
|
|
|
5
7
|
## Provider Roles
|
|
@@ -131,6 +131,18 @@ function checkProtectedPathPattern(filePath) {
|
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// .kontourai/flow-agents/current/<actor>.json (#291) — the per-actor projection of the same
|
|
135
|
+
// pointer above. An agent could forge active_flow_id/active_step_id here exactly as it could
|
|
136
|
+
// via the legacy global file, so this is protected identically (same reason text).
|
|
137
|
+
// SAFE: the workflow CLI writes this via writePerActorCurrent → fs.writeFileSync,
|
|
138
|
+
// NOT via the Write/Edit tool — blocking the tool path does not break legit sidecar.
|
|
139
|
+
if (/(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/current\/[^/]+\.json$/.test(norm)) {
|
|
140
|
+
return {
|
|
141
|
+
name: '.kontourai/flow-agents/current/<actor>.json',
|
|
142
|
+
reason: 'an agent could forge active_flow_id/active_step_id to route the gate to a permissive FlowDefinition',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
134
146
|
// .kontourai/flow-agents/.goal-fit-block-streak.json controls soft-block
|
|
135
147
|
// release counting. An agent could force early advisory-gate release by
|
|
136
148
|
// writing a high count.
|
|
@@ -403,12 +415,13 @@ function checkCommandForBypass(command) {
|
|
|
403
415
|
* Regex that matches a normalized (forward-slash) path ending with a protected
|
|
404
416
|
* kill-switch file: shell profiles, .claude/settings*.json,
|
|
405
417
|
* .kontourai/flow-agents/current.json,
|
|
418
|
+
* .kontourai/flow-agents/current/<actor>.json (#291 per-actor projection),
|
|
406
419
|
* .kontourai/flow-agents/.goal-fit-block-streak.json,
|
|
407
420
|
* .kontourai/flow-agents/<slug>/state.json,
|
|
408
421
|
* .kontourai/flow-agents/<slug>/trust.bundle, and deprecated runtime-shaped
|
|
409
422
|
* .flow-agents equivalents.
|
|
410
423
|
*/
|
|
411
|
-
const REDIRECT_PROTECTED_RE = /(?:^|\/|~\/)(\.bash_profile|\.bashrc|\.profile|\.zprofile|\.zshrc)$|(?:^|\/)\.claude\/settings(?:\.local)?\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/current\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/\.goal-fit-block-streak\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/[^/]+\/state\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/[^/]+\/trust\.bundle$|(?:^|\/)delivery\/trust\.bundle$|(?:^|\/)delivery\/trust\.checkpoint\.json$/;
|
|
424
|
+
const REDIRECT_PROTECTED_RE = /(?:^|\/|~\/)(\.bash_profile|\.bashrc|\.profile|\.zprofile|\.zshrc)$|(?:^|\/)\.claude\/settings(?:\.local)?\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/current\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/current\/[^/]+\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/\.goal-fit-block-streak\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/[^/]+\/state\.json$|(?:^|\/)(?:\.kontourai\/flow-agents|\.flow-agents)\/[^/]+\/trust\.bundle$|(?:^|\/)delivery\/trust\.bundle$|(?:^|\/)delivery\/trust\.checkpoint\.json$/;
|
|
412
425
|
|
|
413
426
|
/**
|
|
414
427
|
* Return true when a token (an unquoted redirect target or tee argument) matches
|
|
@@ -22,6 +22,8 @@ const SANCTIONED_REMEDIES = {
|
|
|
22
22
|
'There is no sanctioned automated writer for this file. Ask a human maintainer to edit it directly. Never disable this hook to make the write.',
|
|
23
23
|
'.kontourai/flow-agents/current.json':
|
|
24
24
|
'Use `npm run workflow:sidecar -- ensure-session` (or `advance-state`), which writes this file for you. Never disable this hook to make the write.',
|
|
25
|
+
'.kontourai/flow-agents/current/<actor>.json':
|
|
26
|
+
'Use `npm run workflow:sidecar -- ensure-session` (or `advance-state`), which writes this file for you. Never disable this hook to make the write.',
|
|
25
27
|
'.kontourai/flow-agents/.goal-fit-block-streak.json':
|
|
26
28
|
'This file is only mutated internally by the goal-fit Stop hook; there is no sanctioned agent writer. Never disable this hook to make the write.',
|
|
27
29
|
'.kontourai/flow-agents/<slug>/state.json':
|
|
@@ -61,6 +63,13 @@ const REMEDY_COMMAND_CANDIDATES = [
|
|
|
61
63
|
{ name: '.kontourai/flow-agents/<slug>/trust.bundle', needles: ['trust.bundle'] },
|
|
62
64
|
{ name: '.kontourai/flow-agents/<slug>/state.json', needles: ['state.json'] },
|
|
63
65
|
{ name: '.kontourai/flow-agents/.goal-fit-block-streak.json', needles: ['.goal-fit-block-streak.json'] },
|
|
66
|
+
// #291: the per-actor projection (current/<actor>.json) is checked BEFORE the legacy
|
|
67
|
+
// current.json needle below — its needle is a directory-scoped substring
|
|
68
|
+
// ('flow-agents/current/') that never overlaps with the bare 'current.json' basename, so
|
|
69
|
+
// ordering between these two entries has no effect on real inputs, but keeping the more
|
|
70
|
+
// specific per-actor entry first mirrors this table's existing "specific before generic"
|
|
71
|
+
// convention.
|
|
72
|
+
{ name: '.kontourai/flow-agents/current/<actor>.json', needles: ['flow-agents/current/', '.flow-agents/current/'] },
|
|
64
73
|
{ name: '.kontourai/flow-agents/current.json', needles: ['current.json'] },
|
|
65
74
|
{ name: '.claude/settings.local.json', needles: ['settings.local.json'] },
|
|
66
75
|
{ name: '.claude/settings.json', needles: ['.claude/settings.json'] },
|
|
@@ -42,6 +42,8 @@ const {
|
|
|
42
42
|
flowAgentsArtifactRoot,
|
|
43
43
|
flowAgentsArtifactRootsForRead,
|
|
44
44
|
} = require('./lib/local-artifact-paths');
|
|
45
|
+
const { resolveActor } = require('./lib/actor-identity.js');
|
|
46
|
+
const { readCurrentPointer } = require('./lib/current-pointer.js');
|
|
45
47
|
|
|
46
48
|
const MAX_STDIN = 1024 * 1024;
|
|
47
49
|
const ACTIVE_STATUSES = new Set([
|
|
@@ -1531,7 +1533,7 @@ async function bundleEnforcement(artifactDir, activeFlowStep) {
|
|
|
1531
1533
|
* to scanning all of .kontourai/flow-agents (newest-mtime).
|
|
1532
1534
|
*/
|
|
1533
1535
|
function preferredArtifactDir(flowAgentsDir) {
|
|
1534
|
-
const current =
|
|
1536
|
+
const { payload: current } = readCurrentPointer(flowAgentsDir, resolveActor(process.env).actor);
|
|
1535
1537
|
if (!current) return null;
|
|
1536
1538
|
const slug = current.artifact_dir || current.active_slug;
|
|
1537
1539
|
if (typeof slug !== 'string' || !slug.trim()) return null;
|
|
@@ -1549,7 +1551,7 @@ function hasSidecarPresence(artifactDir) {
|
|
|
1549
1551
|
// return that slug so analyze() can log the staleness rather than silently falling back to
|
|
1550
1552
|
// a global mtime scan that could resurface an abandoned/never-real session as active.
|
|
1551
1553
|
function staleCurrentSlug(flowAgentsDir) {
|
|
1552
|
-
const current =
|
|
1554
|
+
const { payload: current } = readCurrentPointer(flowAgentsDir, resolveActor(process.env).actor);
|
|
1553
1555
|
if (!current) return null;
|
|
1554
1556
|
const slug = current.artifact_dir || current.active_slug;
|
|
1555
1557
|
if (typeof slug !== 'string' || !slug.trim()) return null;
|
|
@@ -19,6 +19,7 @@ const path = require('path');
|
|
|
19
19
|
const { readLivenessEvents, freshHolders } = require('./lib/liveness-read');
|
|
20
20
|
const { resolveActor } = require('./lib/actor-identity');
|
|
21
21
|
const { flowAgentsArtifactRootsForRead } = require('./lib/local-artifact-paths');
|
|
22
|
+
const { readCurrentPointer } = require('./lib/current-pointer');
|
|
22
23
|
|
|
23
24
|
const STEERING = {
|
|
24
25
|
'tool-planner': [
|
|
@@ -102,7 +103,48 @@ function readJson(file) {
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* #291 (Conflict #2, Wave 2 Task 2.4): actor-scoped preference consulted BEFORE the global
|
|
108
|
+
* newest-mtime scan below. For each read-root, resolve the "current" pointer via
|
|
109
|
+
* `readCurrentPointer` (per-actor `current/<actor>.json` preferred, legacy global
|
|
110
|
+
* `current.json` fallback — the same single choke point every other Wave 2 consumer uses) and,
|
|
111
|
+
* if it names a session dir whose `state.json` is still in an ACTIVE_STATE_STATUSES status,
|
|
112
|
+
* return that state immediately — so actor A's own ambient steering hint always prefers A's own
|
|
113
|
+
* current task over a globally-newer-mtime `state.json` some other actor (B) happened to touch
|
|
114
|
+
* more recently. Returns null (never throws) when no root yields an actor-scoped resolution,
|
|
115
|
+
* so the caller falls through to the unchanged global scan below — this is the compat-shim
|
|
116
|
+
* guarantee: when `actorKey` is empty/unresolved and/or no per-actor file exists yet, this
|
|
117
|
+
* degrades to the SAME legacy-`current.json`-or-nothing lookup the compat shim already performs
|
|
118
|
+
* for every other Wave 2 reader, so a single-session/CI/legacy-only fixture cannot fail to
|
|
119
|
+
* resolve here in a way it wouldn't already have resolved via the global scan.
|
|
120
|
+
*
|
|
121
|
+
* @param {string} root
|
|
122
|
+
* @param {string} actorKey
|
|
123
|
+
* @returns {{file: string, payload: object, mtimeMs: number}|null}
|
|
124
|
+
*/
|
|
125
|
+
function actorScopedWorkflowState(root, actorKey) {
|
|
126
|
+
for (const flowAgentsDir of flowAgentsArtifactRootsForRead(root)) {
|
|
127
|
+
const { payload: current } = readCurrentPointer(flowAgentsDir, actorKey);
|
|
128
|
+
if (!current) continue;
|
|
129
|
+
const slug = current.artifact_dir || current.active_slug;
|
|
130
|
+
if (typeof slug !== 'string' || !slug.trim()) continue;
|
|
131
|
+
const safe = slug.replace(/\.\.+/g, '').replace(/^[/\\]+/, '');
|
|
132
|
+
const dir = path.join(flowAgentsDir, safe);
|
|
133
|
+
if (!dir.startsWith(flowAgentsDir + path.sep) || !fs.existsSync(dir)) continue;
|
|
134
|
+
const file = path.join(dir, 'state.json');
|
|
135
|
+
let stat;
|
|
136
|
+
try { stat = fs.statSync(file); } catch { continue; }
|
|
137
|
+
const payload = readJson(file);
|
|
138
|
+
if (!payload || !ACTIVE_STATE_STATUSES.has(payload.status)) continue;
|
|
139
|
+
return { file, payload, mtimeMs: stat.mtimeMs };
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
105
144
|
function latestWorkflowState(root) {
|
|
145
|
+
const preferred = actorScopedWorkflowState(root, resolveActor(process.env).actor);
|
|
146
|
+
if (preferred) return preferred;
|
|
147
|
+
|
|
106
148
|
const states = flowAgentsArtifactRootsForRead(root)
|
|
107
149
|
.flatMap(artifactRoot => walkStateFiles(artifactRoot))
|
|
108
150
|
.map(file => {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../schemas/assignment-provider-settings.schema.json",
|
|
3
|
+
"schema_version": "1.0",
|
|
4
|
+
"projects": [
|
|
5
|
+
{
|
|
6
|
+
"project": {
|
|
7
|
+
"repo": {
|
|
8
|
+
"owner": "kontourai",
|
|
9
|
+
"name": "flow-agents",
|
|
10
|
+
"url": "https://github.com/kontourai/flow-agents"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"provider": {
|
|
14
|
+
"kind": "github",
|
|
15
|
+
"repo": {
|
|
16
|
+
"owner": "kontourai",
|
|
17
|
+
"name": "flow-agents",
|
|
18
|
+
"url": "https://github.com/kontourai/flow-agents"
|
|
19
|
+
},
|
|
20
|
+
"capabilities": ["assignees", "labels", "comments"]
|
|
21
|
+
},
|
|
22
|
+
"policy": {
|
|
23
|
+
"label_name": "agent:claimed",
|
|
24
|
+
"claim_comment_marker": "<!-- flow-agents:assignment-claim -->",
|
|
25
|
+
"human_assignee_policy": {
|
|
26
|
+
"behavior": "ask_first",
|
|
27
|
+
"idle_threshold_days": 3
|
|
28
|
+
},
|
|
29
|
+
"comment_refresh_on_phase_transition": false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
title: ADR 0001: Flow Agents Consumes Flow For Workflow Enforcement
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
> **FROZEN — immutable history.** Superseding/current decisions live in [`docs/decisions/`](../decisions/index.md). Do not edit.
|
|
6
|
+
|
|
5
7
|
# ADR 0001: Flow Agents Consumes Flow For Workflow Enforcement
|
|
6
8
|
|
|
7
9
|
Date: 2026-05-24
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
title: ADR 0002: Flow Kits As The Extension Unit
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
> **FROZEN — immutable history.** Superseding/current decisions live in [`docs/decisions/`](../decisions/index.md). Do not edit.
|
|
6
|
+
|
|
5
7
|
# ADR 0002: Flow Kits As The Extension Unit
|
|
6
8
|
|
|
7
9
|
Flow Agents will use **Flow Kit** as the product and implementation term for installable workflow bundles, replacing the older "pack" vocabulary. A Flow Kit may contain Flow Definitions, skills, adapters, provider contracts, docs, and evals; the Kit Catalog lists available kits and installable assets, but the workflow semantics live in kit-owned Flow Definition files. This avoids overloading packaging metadata with process semantics and gives custom workflow authoring a clearer product language.
|