@kontourai/flow-agents 2.4.0 → 3.0.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 +12 -0
- package/.github/workflows/trust-reconcile.yml +62 -4
- package/CHANGELOG.md +23 -0
- package/CONTEXT.md +21 -0
- package/build/src/cli/assignment-provider.d.ts +1 -0
- package/build/src/cli/assignment-provider.js +748 -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.js +157 -110
- package/build/src/cli.js +6 -0
- package/build/src/tools/validate-source-tree.js +1 -0
- package/context/contracts/artifact-contract.md +2 -0
- package/context/contracts/assignment-provider-contract.md +239 -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/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/settings/assignment-provider-settings.json +33 -0
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +180 -0
- package/docs/context-map.md +1 -0
- package/docs/decisions/index.md +3 -0
- package/docs/decisions/knowledge-store-provider.md +51 -0
- package/docs/decisions/model-routing.md +63 -0
- package/docs/decisions/standing-directives.md +66 -0
- package/docs/fixture-ownership.md +1 -0
- package/docs/workflow-shared-contracts.md +2 -1
- package/docs/workflow-usage-guide.md +6 -0
- package/evals/ci/run-baseline.sh +6 -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/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_fixture_retirement_audit.sh +2 -2
- 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 +14 -6
- 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 +79 -0
- package/evals/run.sh +10 -0
- package/evals/static/test_flowdef_codeowners_coverage.sh +6 -0
- package/evals/static/test_knowledge_providers.sh +23 -0
- package/kits/builder/skills/deliver/SKILL.md +19 -0
- package/kits/builder/skills/pull-work/SKILL.md +78 -10
- package/kits/knowledge/kit.json +35 -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 +125 -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 +24 -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/work-item/index.js +204 -0
- package/package.json +4 -2
- 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/ci/trust-reconcile.js +521 -24
- package/src/cli/assignment-provider.ts +845 -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 +147 -106
- package/src/cli.ts +6 -0
- package/src/tools/validate-source-tree.ts +1 -0
|
@@ -281,3 +281,183 @@ ADR 0018 §Decision.
|
|
|
281
281
|
- #274 — the issue this ADR resolves; #269 — the first live capture-backed bundle publish
|
|
282
282
|
cited in Context; #265 — the last PR to merge before that (i.e. the boundary of "every PR
|
|
283
283
|
before #265 passed Trust Reconcile trivially via absence").
|
|
284
|
+
|
|
285
|
+
## Addendum (2026-07-03): compound `scope` (space-separated AND) — security-review hardening
|
|
286
|
+
|
|
287
|
+
A security review of the #300 implementation found that `ref:`/`branch-prefix:` scope
|
|
288
|
+
conditions match against `GITHUB_HEAD_REF` (or the `TRUST_RECONCILE_REF` override), and on a
|
|
289
|
+
**fork PR `GITHUB_HEAD_REF` is pusher-controlled** — any contributor who can open a PR can
|
|
290
|
+
name their branch to satisfy a `ref:`- or `branch-prefix:`-only scope. A `delivery/DECLARED`
|
|
291
|
+
entry written to exempt one specific bot (e.g. `ref:release-please--branches--main`) is
|
|
292
|
+
therefore satisfiable by anyone who pushes a branch with that exact name, not only by the
|
|
293
|
+
automation it names. `author:` (bound to the platform-set `GITHUB_ACTOR`, not attacker-chosen
|
|
294
|
+
in the same way) does not have this weakness alone, but a single `author:` condition cannot
|
|
295
|
+
express "and also narrow the blast radius to this branch pattern" — the two properties needed
|
|
296
|
+
combining, not substituting for each other.
|
|
297
|
+
|
|
298
|
+
**Decision:** `scope` may contain multiple space-separated conditions, each one of the four
|
|
299
|
+
forms specified in §2 (`ref:`, `commit:`/`commit:a..b`, `author:`, `branch-prefix:`); a
|
|
300
|
+
compound scope matches only if **every** condition matches (AND, not OR). A single-condition
|
|
301
|
+
scope is unchanged and remains valid (backward compatible — it is the N=1 case of the same
|
|
302
|
+
rule). Matching is still string equality/prefix only, per condition — no `RegExp` is
|
|
303
|
+
constructed from marker content in either the single- or compound-condition path. An
|
|
304
|
+
unrecognized-prefix condition anywhere in a compound scope makes the **whole** scope never
|
|
305
|
+
match, the same fail-closed behavior a malformed single-condition scope already had.
|
|
306
|
+
|
|
307
|
+
**`ref:`/`branch-prefix:` alone are insufficient for identity exemptions and MUST be combined
|
|
308
|
+
with `author:`.** Per the finding above, a scope meant to identify a specific bot/automation
|
|
309
|
+
actor (as opposed to a specific commit range or branch, where the identity question does not
|
|
310
|
+
arise) should not rely on `ref:`/`branch-prefix:` in isolation — combine it with `author:` so
|
|
311
|
+
the platform-set actor identity, not just a self-chosen branch name, has to match too. The
|
|
312
|
+
migration marker this ADR's Consequences named for release-please is updated accordingly:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"scope": "author:github-actions[bot] branch-prefix:release-please--",
|
|
317
|
+
"reason": "release-please automation PR; no agent delivery involved",
|
|
318
|
+
"approved_by": "briananderson1222",
|
|
319
|
+
"declared_at": "<ISO timestamp>"
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
(`dependabot[bot]`'s entry, `author:dependabot[bot]`, was already a single `author:` condition
|
|
324
|
+
— security-review-confirmed sound as-is and left unchanged.)
|
|
325
|
+
|
|
326
|
+
**`/delivery/DECLARED` CODEOWNERS landed with #300/#301.** `.github/CODEOWNERS` now lists
|
|
327
|
+
`/delivery/DECLARED` under the same owner as `/scripts/ci/`, `/package.json`, and
|
|
328
|
+
`/evals/run.sh` (§2's original intent, made concrete); `evals/static/test_flowdef_codeowners_coverage.sh`
|
|
329
|
+
carries a regression-lock assertion for the entry. This closes the file-level half of #301;
|
|
330
|
+
server-side enforcement of CODEOWNERS review on `main` remains tracked separately (#225).
|
|
331
|
+
|
|
332
|
+
## Addendum (2026-07-03, part 2): bundle-ownership staleness check — "for THIS change"
|
|
333
|
+
|
|
334
|
+
Owner-approved follow-up to the compound-scope addendum above, closing a second gap the same
|
|
335
|
+
security review surfaced, this time against a **live incident**: PR #278 (the same open
|
|
336
|
+
dependabot PR cited in Baseline/Consequences) carries a `delivery/trust.checkpoint.json`
|
|
337
|
+
inherited byte-for-byte from `main` (dependabot never runs the deliver skill on its own
|
|
338
|
+
branch — the file only exists there because `main`'s tree already had it). Prior to this
|
|
339
|
+
addendum, `discoverBundle()` finding *any* file at `delivery/trust.bundle` or
|
|
340
|
+
`delivery/trust.checkpoint.json` — regardless of whether it says anything about the change
|
|
341
|
+
actually being reconciled — was sufficient to route into Step 2. A stale, inherited
|
|
342
|
+
checkpoint from a completely unrelated prior PR would therefore either (a) reconcile
|
|
343
|
+
against claims/evidence that have nothing to do with the current diff (if it were a full
|
|
344
|
+
`trust.bundle`), or (b), as in the checkpoint-only case, hit the existing
|
|
345
|
+
`checkpoint-bypass` divergence — **coincidentally fail-closed today only because a bare
|
|
346
|
+
checkpoint has no `evidence[]`/`claims[]` to reconcile**, not because the reconciler
|
|
347
|
+
recognized the file as stale. A future stale *full* `trust.bundle` (not just a checkpoint)
|
|
348
|
+
inherited the same way would not have that accidental protection.
|
|
349
|
+
|
|
350
|
+
**Decision:** "bundle-required" (ADR 0022 §1) means a bundle **for this change**, not any
|
|
351
|
+
bundle merely reachable at the checkout. `discoverBundle()`'s result is now checked against
|
|
352
|
+
a commit-identity binding before Step 2 is allowed to run against it:
|
|
353
|
+
|
|
354
|
+
- **Binding chosen:** `trust.checkpoint.json`'s `commit_sha` field (stamped by
|
|
355
|
+
`sealTrustCheckpoint()` in `src/cli/workflow-sidecar.ts` from `git rev-parse HEAD` at seal
|
|
356
|
+
time) — the strongest identity signal available today. `trust.bundle` itself carries no
|
|
357
|
+
commit/branch metadata (schema: `{schemaVersion, source, claims, evidence, policies,
|
|
358
|
+
events}` — confirmed by inspection), so when the auto-discovered file is `trust.bundle`,
|
|
359
|
+
the check falls through to its sibling `delivery/trust.checkpoint.json` (same
|
|
360
|
+
`resolveDeliveryCandidates()` seam; `publishDelivery()` always writes both together).
|
|
361
|
+
- **Match rule:** the bundle's `commit_sha` equals this change's own sha
|
|
362
|
+
(`TRUST_RECONCILE_SHA`/`GITHUB_SHA`), OR is a git-ancestor of it, via the same `git
|
|
363
|
+
merge-base --is-ancestor` primitive `commit:` scopes already use. Exact equality alone was
|
|
364
|
+
considered and rejected: `sealTrustCheckpoint()` necessarily stamps a commit that precedes
|
|
365
|
+
its own delivery commit (a checkpoint cannot contain its own future commit's sha), so
|
|
366
|
+
requiring exact equality would reject every legitimately fresh delivery, not just stale
|
|
367
|
+
ones. Ancestor-or-equal correctly accepts "sealed earlier in this same open PR's own
|
|
368
|
+
linear history" while — because this repo squash-merges PRs into `main` — correctly
|
|
369
|
+
rejecting a checkpoint inherited from `main`: a squash-merged commit's checkpoint records
|
|
370
|
+
the pre-squash feature-branch tip as `commit_sha`, which is provably **not** an ancestor of
|
|
371
|
+
any later branch (squash discards the original commit graph), so any checkpoint that ever
|
|
372
|
+
lands on `main` becomes permanently unmatchable by a later, unrelated branch — exactly the
|
|
373
|
+
PR #278 shape.
|
|
374
|
+
- **Fail closed on ambiguity:** no extractable `commit_sha` (missing from both the bundle and
|
|
375
|
+
its sibling checkpoint), or this change's own sha unresolvable — never treated as owned. A
|
|
376
|
+
stale/unowned bundle prints a loud, grep-stable line (`stale bundle ignored — attests
|
|
377
|
+
<theirs>, this change is <ours>`) and is then treated exactly as if `discoverBundle()` had
|
|
378
|
+
returned nothing: it falls through to `delivery/DECLARED` resolution.
|
|
379
|
+
- **Scope:** this check applies ONLY to **auto-discovered** bundles. An explicit
|
|
380
|
+
`--bundle`/`TRUST_RECONCILE_BUNDLE` remains untouched — it is a deliberate caller choice
|
|
381
|
+
(test fixtures, a programmatic caller passing a specific bundle), not something silently
|
|
382
|
+
picked up from committed git tree state, and the real `.github/workflows/trust-reconcile.yml`
|
|
383
|
+
never passes `--bundle`; it always relies on auto-discovery, which is exactly the path the
|
|
384
|
+
incident went through.
|
|
385
|
+
|
|
386
|
+
**Residual, honestly named:** this binding is still self-reported (the checkpoint's own
|
|
387
|
+
`commit_sha`, not a cryptographic commit signature) and still only as strong as
|
|
388
|
+
`sealTrustCheckpoint()`'s local `git rev-parse HEAD` call — an adversarial agent that
|
|
389
|
+
controls its own local git state before delivery could, in principle, stamp an arbitrary
|
|
390
|
+
sha. This does not reopen the PR #278 gap (a human/bot PR that never runs the deliver skill
|
|
391
|
+
at all still cannot forge a checkpoint it never writes), but it is not a full defense against
|
|
392
|
+
a fully agent-controlled delivery pipeline; that class of residual is the same
|
|
393
|
+
"trust-the-humans boundary" ADR 0017/0020 already name, not newly introduced here.
|
|
394
|
+
|
|
395
|
+
## Addendum (2026-07-03, part 3): CI-context contract for the staleness check
|
|
396
|
+
|
|
397
|
+
A re-review found a launch-blocking gap in the staleness check (Addendum part 2, above):
|
|
398
|
+
`isAncestorCommit()`'s `git merge-base --is-ancestor` call is only meaningful if the CI
|
|
399
|
+
checkout actually has the parent commit objects, and if the sha compared against is a
|
|
400
|
+
commit a checkpoint could plausibly have been sealed at. Neither held under
|
|
401
|
+
`.github/workflows/trust-reconcile.yml`'s prior configuration: the default
|
|
402
|
+
`actions/checkout` fetch depth is a **shallow clone (`fetch-depth: 1`)**, which has no
|
|
403
|
+
parent objects — `git merge-base` against a missing object exits 128, so the ancestor
|
|
404
|
+
check would be unresolvable for every real PR, not just stale ones, falsely staling
|
|
405
|
+
**every legitimately fresh delivery** on a required, admin-enforced check. Separately, on
|
|
406
|
+
a `pull_request` trigger `GITHUB_SHA` resolves to GitHub's synthetic merge commit
|
|
407
|
+
(`refs/pull/N/merge`), a commit that never existed at the time any real `seal-checkpoint`
|
|
408
|
+
run stamped `commit_sha` — so even a correctly-resolving ancestor check would compare
|
|
409
|
+
against the wrong target. Both are now fixed at the workflow layer, not by loosening the
|
|
410
|
+
reconciler's own fail-closed contract: the checkout step sets `fetch-depth: 0` (full
|
|
411
|
+
history), and the trust-reconcile step sets `TRUST_RECONCILE_SHA:
|
|
412
|
+
${{ github.event.pull_request.head.sha || github.sha }}` so the ownership comparison
|
|
413
|
+
always uses the PR's own head commit on a `pull_request` trigger (falling back to
|
|
414
|
+
`github.sha`, which is already correct, on `push`/`workflow_dispatch`). The checkout ref
|
|
415
|
+
itself is unchanged — Step 1 (fresh verify) runs against the same tree it always has;
|
|
416
|
+
only the identity used for the Step-2 ownership comparison changes. This is a narrow,
|
|
417
|
+
deliberate, owner-recorded functional amendment to the "workflow YAML: comments only"
|
|
418
|
+
scoping the original Wave 1 plan set for this file — an unavoidable consequence of
|
|
419
|
+
closing the HIGH finding, not scope creep. `scripts/ci/trust-reconcile.js`'s own contract
|
|
420
|
+
is unchanged and stays fail-closed either way: a shallow/missing-object condition still
|
|
421
|
+
resolves to "not an ancestor" (never silently accepted), so a misconfigured downstream
|
|
422
|
+
adopter that keeps the shallow default degrades safely (falsely stales real bundles,
|
|
423
|
+
loudly, with a diagnosable line) rather than unsafely (accepting a bundle it cannot
|
|
424
|
+
actually verify).
|
|
425
|
+
|
|
426
|
+
## Addendum (2026-07-03, part 4): event-scoped enforcement — gates gate PRs, not `main`
|
|
427
|
+
|
|
428
|
+
Final review found a second, more severe HIGH launch-blocker in the staleness check
|
|
429
|
+
(Addendum part 3, above): even with full-history checkout and correct sha binding, a
|
|
430
|
+
`push` run on `main` immediately after a squash-merge would still falsely stale the
|
|
431
|
+
just-merged, genuinely legitimate bundle — because `git merge --squash` (this repo's
|
|
432
|
+
merge strategy) discards the original commit graph, the resulting squash commit on `main`
|
|
433
|
+
has **no** git ancestry back to the feature-branch commit its checkpoint was sealed
|
|
434
|
+
against, by design, not by defect. Empirically reproduced with a synthetic squash pair.
|
|
435
|
+
Left unfixed, every delivery would fail the required Trust Reconcile check on `main`
|
|
436
|
+
immediately post-merge, and Phase 2 attestation minting (which runs only after Step 2
|
|
437
|
+
passes) would stop entirely — a regression far worse than the bug this whole ADR closes.
|
|
438
|
+
|
|
439
|
+
**Decision (reviewer option (a) — enforcement is event-scoped):** bundle-required
|
|
440
|
+
enforcement (§1's `bundle-required-no-declared-marker` fail-closed branch, and the
|
|
441
|
+
staleness-gate consequence of it, Addendum part 2) applies only when a run is **gating a
|
|
442
|
+
proposed change** — detected via `TRUST_RECONCILE_EVENT` (set from `github.event_name` by
|
|
443
|
+
the workflow), with a conservative default: an absent/unrecognized event value is treated
|
|
444
|
+
as gating (enforce), so every existing local/test caller and any misconfigured CI event
|
|
445
|
+
keeps today's stricter behavior. Only the literal value `push` is treated as a post-merge
|
|
446
|
+
run. On `push`, an absent or stale auto-discovered bundle is a loud, exit-0 no-op
|
|
447
|
+
(`push event: ... — skipping Step 2 (gating happened on the PR run)`) rather than a
|
|
448
|
+
failure or a `delivery/DECLARED` requirement — there is no "change" being gated on a push
|
|
449
|
+
to a protected branch (direct pushes are already excluded by branch protection; a push
|
|
450
|
+
run's own job is Step 1 fresh-verify, which is unaffected by this scoping and always
|
|
451
|
+
runs, plus Step 2 reconcile IF the bundle happens to still attest that exact commit,
|
|
452
|
+
forming the Phase 2 attestation basis). `pull_request` gating is completely unaffected —
|
|
453
|
+
the exact same squash-shape fixture used to prove the push no-op also proves the
|
|
454
|
+
identical shape still fails closed when `TRUST_RECONCILE_EVENT=pull_request`, confirming
|
|
455
|
+
this scoping narrows *when* enforcement applies, not *how strictly* it applies when it
|
|
456
|
+
does. Residual (HIGH, documented not solved): `TRUST_RECONCILE_EVENT`'s
|
|
457
|
+
source value (`github.event_name`) is authoritative GitHub Actions context, but the
|
|
458
|
+
workflow FILE that assigns it to the env var is itself PR-editable content on a
|
|
459
|
+
`pull_request` run — a PR could hardcode `TRUST_RECONCILE_EVENT: push` in its own copy of
|
|
460
|
+
the workflow to fake the post-merge no-op path; this is closed by required code-owner
|
|
461
|
+
review on `.github/workflows/trust-reconcile.yml` (#225, not yet server-side enforced),
|
|
462
|
+
the same residual class as every other self-asserted CI input this ADR already carries.
|
|
463
|
+
Step 1 (fresh verify) is unaffected by event scoping either way and always runs.
|
package/docs/context-map.md
CHANGED
|
@@ -51,6 +51,7 @@ Machine-readable workflow state lives beside Markdown artifacts in `.kontourai/f
|
|
|
51
51
|
|
|
52
52
|
| Schema | Title | ID |
|
|
53
53
|
| --- | --- | --- |
|
|
54
|
+
| assignment-provider-settings.schema.json | Flow Agents Assignment Provider Settings | https://flow-agents.dev/schemas/assignment-provider-settings.schema.json |
|
|
54
55
|
| backlog-provider-settings.schema.json | Flow Agents Backlog Provider Settings | https://flow-agents.dev/schemas/backlog-provider-settings.schema.json |
|
|
55
56
|
| decision-record.schema.json | Flow Agents Decision Record | https://flow-agents.dev/schemas/decision-record.schema.json |
|
|
56
57
|
| flow-agents-settings.schema.json | Flow Agents Settings | https://flow-agents.dev/schemas/flow-agents-settings.schema.json |
|
package/docs/decisions/index.md
CHANGED
|
@@ -11,4 +11,7 @@ Numbered ADRs under `docs/adr/` are frozen history and are not listed here.
|
|
|
11
11
|
| Topic | Status | Decision |
|
|
12
12
|
| --- | --- | --- |
|
|
13
13
|
| [decision-records](./decision-records.md) | current | Decision records |
|
|
14
|
+
| [knowledge-store-provider](./knowledge-store-provider.md) | current | Knowledge store provider |
|
|
15
|
+
| [model-routing](./model-routing.md) | current | Model routing |
|
|
14
16
|
| [promotion-gate](./promotion-gate.md) | current | Promotion gate |
|
|
17
|
+
| [standing-directives](./standing-directives.md) | current | Standing directives |
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: current
|
|
3
|
+
subject: Knowledge store provider
|
|
4
|
+
decided: 2026-07-02
|
|
5
|
+
evidence:
|
|
6
|
+
- kind: issue
|
|
7
|
+
ref: https://github.com/kontourai/flow-agents/issues/317
|
|
8
|
+
- kind: doc
|
|
9
|
+
ref: context/contracts/knowledge-store-contract.md
|
|
10
|
+
- kind: session-archive
|
|
11
|
+
ref: .kontourai/flow-agents/decision-registry-shape/decision-registry-shape--idea-to-backlog.md
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Knowledge store provider
|
|
15
|
+
|
|
16
|
+
The Knowledge Kit owns a storage-independent **Knowledge Graph** model — typed
|
|
17
|
+
nodes (`note`, `decision`, `issue`, `session`, `person`; extensible), a closed
|
|
18
|
+
edge vocabulary (`supersedes`, `merged-into`, `blocks`, `evidence-of`,
|
|
19
|
+
`mentions`, `relates`), and provenance on both — with storage and sync as
|
|
20
|
+
pluggable **providers**. The same read and health verbs run over every provider.
|
|
21
|
+
|
|
22
|
+
## Decision
|
|
23
|
+
|
|
24
|
+
- The normative model + provider interface live in
|
|
25
|
+
`context/contracts/knowledge-store-contract.md`, backed by JSON schemas at
|
|
26
|
+
`schemas/knowledge/{node,edge,proposal,health-report}.schema.json`.
|
|
27
|
+
- The write side is **proposals-only**, expressed structurally: a provider's
|
|
28
|
+
`proposeWrite` can only return a proposal whose `status` is the const
|
|
29
|
+
`"proposed"` and MUST NOT mutate the store. Enactment is downstream — the
|
|
30
|
+
git-repo provider renders a decision-registry topic file for the **promote
|
|
31
|
+
sub-flow**; work-item renders a draft `gh` comment/label; markdown-vault
|
|
32
|
+
renders a native frontmatter+wikilink note. This mirrors the store contract's
|
|
33
|
+
`propose → apply/reject` gate rather than forking a new write path.
|
|
34
|
+
- Three reference providers ship: **markdown-vault** (a thin read+propose wrapper
|
|
35
|
+
over the existing store adapter — existing skills unaffected), **git-repo**
|
|
36
|
+
(decision-registry tombstones/evidence, CONTEXT.md glossary, learnings), and
|
|
37
|
+
**work-item** (GitHub issues via an injectable `gh` runner, so backlog hygiene
|
|
38
|
+
becomes a knowledge-health pass).
|
|
39
|
+
- Health verbs (duplicate detection, dependency-link integrity) are
|
|
40
|
+
**provider-agnostic** functions over `{nodes, edges}` and emit schema-valid
|
|
41
|
+
health reports. A parameterized conformance suite gates every provider.
|
|
42
|
+
|
|
43
|
+
## Rationale
|
|
44
|
+
|
|
45
|
+
The portfolio provider pattern (hachure format/adapters; Builder Kit
|
|
46
|
+
work-item contract/providers) had not been applied to knowledge, which was fused
|
|
47
|
+
to the markdown-vault shape. Rule-of-three passes on stores operated today: a
|
|
48
|
+
duplicate backlog issue (traverse#14) caught manually is now caught mechanically
|
|
49
|
+
by a graph pass over the work-item adapter. A graph-database provider stays a
|
|
50
|
+
gated spike (#318) — adoption waits on a real query failing over file providers,
|
|
51
|
+
not speculation.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: current
|
|
3
|
+
subject: Model routing
|
|
4
|
+
decided: 2026-07-03
|
|
5
|
+
evidence:
|
|
6
|
+
- kind: issue
|
|
7
|
+
ref: https://github.com/kontourai/flow-agents/issues/364
|
|
8
|
+
- kind: url
|
|
9
|
+
ref: https://claude.ai/code/session_01CEPxsn2GuWdysEMACRHsFQ
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Model routing
|
|
13
|
+
|
|
14
|
+
Which model backs which delegate role is **data resolved by the orchestrator
|
|
15
|
+
at delegation time**, not code baked into generated files or per-agent
|
|
16
|
+
frontmatter.
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
- Model-routing policy is committed data in `.datum/config.json` (read by the
|
|
21
|
+
`@kontourai/datum` registry, `datum.schema.json`): a `providers` map
|
|
22
|
+
(`anthropic`, `zai`) and a `roles` map keyed by delegate role name
|
|
23
|
+
(`delegate-mechanical`, `delegate-implementation`, `delegate-design`,
|
|
24
|
+
`orchestrator`, `extraction-default`) to a `model@provider` ref.
|
|
25
|
+
- **The orchestrator resolves the role via datum at delegation time** —
|
|
26
|
+
`datum resolve <role> --json` or the library `resolve()` — and passes the
|
|
27
|
+
resolved model **explicitly** when spawning each delegate. The consumption
|
|
28
|
+
instruction lives in `context/contracts/execution-contract.md` § Delegation:
|
|
29
|
+
Model Routing (where orchestrators actually read it), with a matching
|
|
30
|
+
summary table in `kits/builder/skills/deliver/SKILL.md` § Model Routing.
|
|
31
|
+
`.datum/config.json` is the single source of truth for the mapping; the
|
|
32
|
+
contracts/skills only say where to read it.
|
|
33
|
+
- No generated files and no per-agent frontmatter changes. Roles are the
|
|
34
|
+
stable reference; which model a role resolves to can change without
|
|
35
|
+
touching any delegation call site, skill, or agent definition.
|
|
36
|
+
- When datum or the config is absent, delegation falls back to the runtime's
|
|
37
|
+
inherited model and the fallback is noted in the session/task artifact
|
|
38
|
+
rather than blocking delegation.
|
|
39
|
+
|
|
40
|
+
## Rejected alternatives
|
|
41
|
+
|
|
42
|
+
- **Generated per-agent frontmatter** (a build step stamping a model into
|
|
43
|
+
each agent definition file) — rejected because it reintroduces a generated
|
|
44
|
+
artifact that drifts from the data it was generated from and requires a
|
|
45
|
+
regeneration step on every routing-policy change, which this program has
|
|
46
|
+
standing directives against (no generated files as the routing surface).
|
|
47
|
+
- **Environment-variable-only selection** (e.g. one global
|
|
48
|
+
`ANTHROPIC_MODEL`-style override with no role granularity) — rejected
|
|
49
|
+
because it collapses distinct delegate shapes (mechanical vs.
|
|
50
|
+
precisely-planned implementation vs. design-latitude vs. orchestrator) onto
|
|
51
|
+
a single model choice, losing the per-role routing this design exists to
|
|
52
|
+
express, and because it is not resolvable per-delegate-spawn within a
|
|
53
|
+
single process the way an explicit per-call model override is.
|
|
54
|
+
|
|
55
|
+
## Rationale
|
|
56
|
+
|
|
57
|
+
Routing policy changes independently of, and more often than, the skills and
|
|
58
|
+
contracts that consume it (new models, new providers, retuned role-to-model
|
|
59
|
+
mappings). Keeping it as data in `.datum/config.json` — the same registry
|
|
60
|
+
shape the `extraction-default` role already establishes as portfolio
|
|
61
|
+
precedent — means a routing change is a config edit reviewed like any other
|
|
62
|
+
data change, not a source/doc change scattered across every delegating
|
|
63
|
+
skill.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: current
|
|
3
|
+
subject: Standing directives
|
|
4
|
+
decided: 2026-07-03
|
|
5
|
+
evidence:
|
|
6
|
+
- kind: issue
|
|
7
|
+
ref: https://github.com/kontourai/flow-agents/issues/348
|
|
8
|
+
- kind: pr
|
|
9
|
+
ref: https://github.com/kontourai/datum/pull/3
|
|
10
|
+
- kind: pr
|
|
11
|
+
ref: https://github.com/kontourai/flow-agents/pull/344
|
|
12
|
+
- kind: issue
|
|
13
|
+
ref: https://github.com/kontourai/flow-agents/issues/347
|
|
14
|
+
- kind: issue
|
|
15
|
+
ref: https://github.com/kontourai/surface/issues/110
|
|
16
|
+
- kind: pr
|
|
17
|
+
ref: https://github.com/kontourai/surface/pull/113
|
|
18
|
+
- kind: doc
|
|
19
|
+
ref: docs/repository-structure.md
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Standing directives
|
|
23
|
+
|
|
24
|
+
Ratified owner directives get a durable, quotable home at
|
|
25
|
+
`context/contracts/standing-directives.md` instead of living only in the
|
|
26
|
+
ephemeral context of whichever orchestrator session ratified them.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
- `context/contracts/standing-directives.md` holds a short, numbered list of
|
|
31
|
+
owner-ratified directives, each with a one-line rationale and the date it
|
|
32
|
+
was ratified.
|
|
33
|
+
- Every other file under `context/contracts/` carries a one-line pointer in
|
|
34
|
+
its header area to `standing-directives.md`, so any agent that loads a
|
|
35
|
+
phase contract also receives the standing directives regardless of which
|
|
36
|
+
task in a plan it was delegated.
|
|
37
|
+
- A standing directive overrides default engineering conservatism (e.g.
|
|
38
|
+
keeping a compatibility path reachable "just in case") wherever it applies.
|
|
39
|
+
An agent that believes a directive does not fit a situation stops and
|
|
40
|
+
reports the conflict rather than silently choosing a side.
|
|
41
|
+
- The seeded July 2026 directives: no legacy fallbacks (migrate hard, loud
|
|
42
|
+
typed error naming the regeneration command); durable-vs-runtime path
|
|
43
|
+
convention (`.kontourai/` is gitignored runtime state, `.<product>/` is a
|
|
44
|
+
product's durable committed directory); evidence must be executable;
|
|
45
|
+
standalone-first then integrate; and the regression rule for cutovers
|
|
46
|
+
(large regressions are surfaced as an explicit owner decision with data,
|
|
47
|
+
never papered over, but do not block the cutover by default).
|
|
48
|
+
|
|
49
|
+
## Rationale
|
|
50
|
+
|
|
51
|
+
A delegated agent working one slice of a plan only sees the task prompt it
|
|
52
|
+
was given. If a ratified correction — such as "no legacy fallbacks" — lives
|
|
53
|
+
only in the orchestrator's context from the session that ratified it, the
|
|
54
|
+
delegate never receives it and can reintroduce the exact behavior the owner
|
|
55
|
+
already corrected. Wiring a pointer into every contract header makes receipt
|
|
56
|
+
structural rather than dependent on the orchestrator remembering to restate
|
|
57
|
+
the correction in every task prompt.
|
|
58
|
+
|
|
59
|
+
The individual directives are evidenced by hard-cutover precedent
|
|
60
|
+
([datum#3](https://github.com/kontourai/datum/pull/3)), the fallback-removal
|
|
61
|
+
follow-up to [flow-agents#344](https://github.com/kontourai/flow-agents/pull/344)
|
|
62
|
+
tracked as [flow-agents#347](https://github.com/kontourai/flow-agents/issues/347),
|
|
63
|
+
and the durable-vs-runtime path convention already documented in
|
|
64
|
+
[docs/repository-structure.md](../repository-structure.md) and applied in
|
|
65
|
+
[surface#110](https://github.com/kontourai/surface/issues/110) /
|
|
66
|
+
[surface#113](https://github.com/kontourai/surface/pull/113).
|
|
@@ -13,6 +13,7 @@ run `npm run validate:source --` and `npm run fixture:retirement-audit --`.
|
|
|
13
13
|
|
|
14
14
|
| Fixture directory | Classification | Owners | Cleanup rule |
|
|
15
15
|
| --- | --- | --- | --- |
|
|
16
|
+
| `evals/fixtures/assignment-provider` | AssignmentProvider local-file and GitHub render/status fixtures (#290) | `evals/integration/test_assignment_provider_local_file.sh`, `evals/integration/test_assignment_provider_github.sh`, `evals/integration/test_pull_work_assignment_join.sh` | Keep while the assignment provider contract's claim/release/supersede/status/list operations and the assignment ⋈ liveness join are tested against local-file and GitHub fixture inputs. |
|
|
16
17
|
| `evals/fixtures/backlog-provider-settings` | settings precedence fixtures | `evals/integration/test_effective_backlog_settings.sh` | Keep while backlog provider settings resolution supports global defaults and project overrides. |
|
|
17
18
|
| `evals/fixtures/builder-kit-workflow-state` | Builder Kit workflow-state fixtures | `evals/static/test_workflow_skills.sh` | Keep while Builder Kit state contract and resume behavior are documented in workflow skill contracts. |
|
|
18
19
|
| `evals/fixtures/console-learning-projection` | console learning projection fixtures | `evals/integration/test_console_learning_projection.sh` | Keep while learning projection supports correction and open-route examples. |
|
|
@@ -10,6 +10,7 @@ The workflow system now separates durable process contracts from role-specific i
|
|
|
10
10
|
|
|
11
11
|
Shared contracts live in `context/contracts/`:
|
|
12
12
|
|
|
13
|
+
- `standing-directives.md`
|
|
13
14
|
- `artifact-contract.md`
|
|
14
15
|
- `planning-contract.md`
|
|
15
16
|
- `execution-contract.md`
|
|
@@ -17,7 +18,7 @@ Shared contracts live in `context/contracts/`:
|
|
|
17
18
|
- `verification-contract.md`
|
|
18
19
|
- `delivery-contract.md`
|
|
19
20
|
|
|
20
|
-
These files define the stable rules for artifacts, planning, execution, review, verification, delivery loops, Goal Fit, and final acceptance.
|
|
21
|
+
These files define the stable rules for artifacts, planning, execution, review, verification, delivery loops, Goal Fit, and final acceptance. `standing-directives.md` is cross-cutting: it holds ratified owner directives that override default engineering conservatism, pointed to from the header of every other contract file so delegated agents receive it regardless of which phase contract they load.
|
|
21
22
|
|
|
22
23
|
The durable resource shape for selected scope, workflow runs, run plans, status conditions, provider-backed Work Items, and sidecar compatibility direction is documented in the Kontour Resource Contract:
|
|
23
24
|
https://github.com/kontourai/flow-agents/blob/main/docs/kontour-resource-contract.md
|
|
@@ -114,6 +114,12 @@ Expected artifact:
|
|
|
114
114
|
|
|
115
115
|
When a repository has backlog provider settings, `pull-work` should use those settings without requiring the user to name the board. In Flow Agents, `npm run effective-backlog-settings -- --repo-path . --json` resolves `kontourai/flow-agents` to GitHub Project `kontourai/1`, so a prompt like `use pull-work` is enough for the configured provider path.
|
|
116
116
|
|
|
117
|
+
### Assignment ownership: the third provider leg
|
|
118
|
+
|
|
119
|
+
Beside the `WorkItemProvider` (what work exists) and `BoardProvider` (how it is grouped/ranked) settings above, `pull-work` also reads `AssignmentProvider` settings to decide who currently owns a candidate work item before offering it. This is durable, human-visible ownership — a GitHub issue assignee, an `agent:claimed` label, and a versioned machine-readable claim comment (or, for tracker-less repos and evals, an equivalent local JSON record) — joined against the ephemeral liveness presence layer so a crashed session's stale claim never blocks a second session from picking up the same work.
|
|
120
|
+
|
|
121
|
+
Settings live at `context/settings/assignment-provider-settings.json` (validated by `schemas/assignment-provider-settings.schema.json`), mirroring the same `defaults`/`projects[]` shape as the backlog provider settings above; resolve them with `npm run effective-assignment-provider-settings -- --repo-path . --json`. See `context/contracts/assignment-provider-contract.md` for the full `claim`/`release`/`supersede`/`status`/`list` vocabulary, the assignment ⋈ liveness join table, and the human-assignee ask-first policy.
|
|
122
|
+
|
|
117
123
|
Direct `pull-work` remains a normal workflow primitive. The Builder Kit build path adds the pickup Probe/design-probe handoff before planning; it does not require Surface/Veritas trust-backed gates and does not replace direct primitive use.
|
|
118
124
|
|
|
119
125
|
Builder Kit build is the product-level entry point for implementation pickup. In that mode, `pull-work` may guide the next step automatically as `pull-work -> design-probe / pickup-probe`; direct `pull-work` still stops with a `plan-work` handoff unless you ask to continue.
|
package/evals/ci/run-baseline.sh
CHANGED
|
@@ -22,6 +22,9 @@ CHECKS=(
|
|
|
22
22
|
"Workflow sidecar writer integration|bash evals/integration/test_workflow_sidecar_writer.sh"
|
|
23
23
|
"Sidecar field preservation integration|bash evals/integration/test_sidecar_field_preservation.sh"
|
|
24
24
|
"Actor identity resolver integration|bash evals/integration/test_actor_identity.sh"
|
|
25
|
+
"Assignment provider local-file integration|bash evals/integration/test_assignment_provider_local_file.sh"
|
|
26
|
+
"Assignment provider github integration|bash evals/integration/test_assignment_provider_github.sh"
|
|
27
|
+
"Pull work assignment join integration|bash evals/integration/test_pull_work_assignment_join.sh"
|
|
25
28
|
"Goal Fit hook integration|bash evals/integration/test_goal_fit_hook.sh"
|
|
26
29
|
"Hook category behavior integration|bash evals/integration/test_hook_category_behaviors.sh"
|
|
27
30
|
"Workflow steering hook integration|bash evals/integration/test_workflow_steering_hook.sh"
|
|
@@ -72,6 +75,9 @@ LANE_WORKFLOW_CONTRACTS=(
|
|
|
72
75
|
"Workflow sidecar writer integration"
|
|
73
76
|
"Sidecar field preservation integration"
|
|
74
77
|
"Actor identity resolver integration"
|
|
78
|
+
"Assignment provider local-file integration"
|
|
79
|
+
"Assignment provider github integration"
|
|
80
|
+
"Pull work assignment join integration"
|
|
75
81
|
)
|
|
76
82
|
|
|
77
83
|
LANE_RUNTIME_AND_KIT=(
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"number": 4242,
|
|
3
|
+
"state": "OPEN",
|
|
4
|
+
"assignees": [
|
|
5
|
+
{
|
|
6
|
+
"login": "flow-agents-fixture-bot"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"labels": [
|
|
10
|
+
{
|
|
11
|
+
"name": "agent:claimed"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "kind:enhancement"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"comments": [
|
|
18
|
+
{
|
|
19
|
+
"id": 90001,
|
|
20
|
+
"body": "An unrelated human comment on this fixture issue."
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": 90002,
|
|
24
|
+
"body": "<!-- flow-agents:assignment-claim -->\n**Assignment claim** — Claimed by an automated agent session (claude-code).\n\n- actor: `claude-code:fixture-actor-a-session:fixture-host`\n- claimed_at: 2026-06-01T12:00:00Z\n- ttl_seconds: 1800\n- branch: `agent/claude-code-fixture-actor-a-session-fixture-host/flow-agents-4242`\n\n```json\n{\n \"schema_version\": \"1.0\",\n \"role\": \"AssignmentClaimRecord\",\n \"subject_id\": \"kontourai/flow-agents#4242\",\n \"actor\": {\n \"runtime\": \"claude-code\",\n \"session_id\": \"fixture-actor-a-session\",\n \"host\": \"fixture-host\",\n \"human\": null\n },\n \"claimed_at\": \"2026-06-01T12:00:00Z\",\n \"ttl_seconds\": 1800,\n \"branch\": \"agent/claude-code-fixture-actor-a-session-fixture-host/flow-agents-4242\",\n \"artifact_dir\": \".kontourai/flow-agents/flow-agents-4242\",\n \"status\": \"claimed\"\n}\n```"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|