mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-08-28 10:54:59 +00:00
Compare commits
194 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
350faa9ff5 | ||
|
|
dfae7911cc | ||
|
|
3851fea93e | ||
|
|
871101addc | ||
|
|
e04abf96a6 | ||
|
|
eee4dd9a2a | ||
|
|
1cdcd6ec60 | ||
|
|
6ab3feb35f | ||
|
|
0d9118ada2 | ||
|
|
7abe012f45 | ||
|
|
93ea9577d2 | ||
|
|
7cc8f253c7 | ||
|
|
49e25838e1 | ||
|
|
415f4077b9 | ||
|
|
c9db917422 | ||
|
|
d5428baec0 | ||
|
|
25be48454b | ||
|
|
2fa9b2ea89 | ||
|
|
3a022ddc44 | ||
|
|
dcdcb0f65d | ||
|
|
5493c022cd | ||
|
|
6e3b8bfe79 | ||
|
|
619d1bae16 | ||
|
|
87d203d2f5 | ||
|
|
ce9bc5c750 | ||
|
|
72ace38440 | ||
|
|
97ac04b968 | ||
|
|
2cbfd3b215 | ||
|
|
8ca792ad87 | ||
|
|
8a294be775 | ||
|
|
392e65d551 | ||
|
|
a38d42c005 | ||
|
|
36e079e83c | ||
|
|
c6fca49cf7 | ||
|
|
3bcf48fb5f | ||
|
|
454850db9e | ||
|
|
7fd9fd1360 | ||
|
|
f5e361ab8f | ||
|
|
06e1343958 | ||
|
|
0c1f5b35f1 | ||
|
|
0c0764c449 | ||
|
|
5a1e0d0d1a | ||
|
|
06d7fa61f8 | ||
|
|
14b6d591a5 | ||
|
|
2b28b51a73 | ||
|
|
2912e116c0 | ||
|
|
91623dba84 | ||
|
|
542d6dd760 | ||
|
|
2a581a451a | ||
|
|
93e946693d | ||
|
|
bd03e2e17c | ||
|
|
b1367aa33c | ||
|
|
9c533c4345 | ||
|
|
f3abf3642e | ||
|
|
61bf2163b5 | ||
|
|
e24b75cf3b | ||
|
|
1faff783ff | ||
|
|
eb12b67921 | ||
|
|
e7ae87f6d2 | ||
|
|
77a67f3e2d | ||
|
|
2cfb89396d | ||
|
|
ab61456774 | ||
|
|
6e84033f2e | ||
|
|
3917456bf6 | ||
|
|
da78f22fd6 | ||
|
|
e5aba49e61 | ||
|
|
0267be8dbe | ||
|
|
5c0c124860 | ||
|
|
d8b1872ba8 | ||
|
|
4401a88025 | ||
|
|
dc51cb629d | ||
|
|
53743869fc | ||
|
|
38845fab6e | ||
|
|
67915a5cee | ||
|
|
bfa8d9a686 | ||
|
|
b5483bea18 | ||
|
|
f1bbfffd11 | ||
|
|
f217f3fad8 | ||
|
|
9e46e0d10b | ||
|
|
baf599e007 | ||
|
|
2a1c987cc0 | ||
|
|
292652b0f4 | ||
|
|
b0fbecfe7d | ||
|
|
ab635669a4 | ||
|
|
276eeaac0e | ||
|
|
e858daf777 | ||
|
|
3e7ddaf758 | ||
|
|
407ffa5b9f | ||
|
|
2578fde055 | ||
|
|
7b61b9a778 | ||
|
|
7150ecc739 | ||
|
|
d8f1e0abf3 | ||
|
|
0338570cde | ||
|
|
71bfd482b7 | ||
|
|
aeff2aa278 | ||
|
|
e4d81b5cda | ||
|
|
528b348c59 | ||
|
|
886d349966 | ||
|
|
c38c94a43c | ||
|
|
d1625de22d | ||
|
|
c177b45cff | ||
|
|
472d1fc5a3 | ||
|
|
8f4955cb53 | ||
|
|
0876dea7eb | ||
|
|
e3fd3163df | ||
|
|
b6a77ec226 | ||
|
|
1e6869613b | ||
|
|
aa17223a06 | ||
|
|
8219fefa8d | ||
|
|
43d1b74c7f | ||
|
|
b19d161a03 | ||
|
|
ac7d574a23 | ||
|
|
17055b2cd3 | ||
|
|
7139ad73bd | ||
|
|
76dbfe8e2f | ||
|
|
3450f6034d | ||
|
|
c814bf7390 | ||
|
|
a7bae41805 | ||
|
|
b7ae9ecda9 | ||
|
|
4bf1b13299 | ||
|
|
e69bfe8c14 | ||
|
|
e15bd320e8 | ||
|
|
eb83f92be8 | ||
|
|
75943a0af7 | ||
|
|
bdca535027 | ||
|
|
ddde62e969 | ||
|
|
57dbd930bb | ||
|
|
8580e6202a | ||
|
|
49b03e373e | ||
|
|
baec6e8137 | ||
|
|
f860184f62 | ||
|
|
8b4a5d0e19 | ||
|
|
4a2ecc5bd1 | ||
|
|
b74c4db259 | ||
|
|
9cf0ccb96c | ||
|
|
461c1fbbbf | ||
|
|
34e4c8d13b | ||
|
|
085c492bba | ||
|
|
5bb58b1bbc | ||
|
|
abf407a8ac | ||
|
|
fae9ad32b7 | ||
|
|
8dc6bf32c9 | ||
|
|
880914b97b | ||
|
|
3ee290c0dc | ||
|
|
3c9426b831 | ||
|
|
c59a400406 | ||
|
|
4f8e5dc14f | ||
|
|
0d5ebb3a7a | ||
|
|
c91d7c5de3 | ||
|
|
1c5d44a2af | ||
|
|
dcd4e61f66 | ||
|
|
4aba9229c9 | ||
|
|
a7a59ae494 | ||
|
|
4ccec233ca | ||
|
|
9f03c0bc06 | ||
|
|
450f2df4a2 | ||
|
|
6e7bb4ffee | ||
|
|
5da7e7db82 | ||
|
|
3fffd5f81d | ||
|
|
c0c15a4497 | ||
|
|
f7aea9a4d4 | ||
|
|
74647256bc | ||
|
|
6de0ded9c6 | ||
|
|
c3da853006 | ||
|
|
508c45fcd3 | ||
|
|
bf60aabc4a | ||
|
|
5fc3fbdb5e | ||
|
|
3a73662b9c | ||
|
|
2f18229ff2 | ||
|
|
edcd33c9f5 | ||
|
|
b8590bbcb2 | ||
|
|
fb8a0b1a55 | ||
|
|
4ed08b32c1 | ||
|
|
25516df934 | ||
|
|
866afdfc57 | ||
|
|
32943b9b08 | ||
|
|
bebc6aa3d0 | ||
|
|
faeec2752a | ||
|
|
1c86d90386 | ||
|
|
19e95f0d80 | ||
|
|
d261ee605b | ||
|
|
311879be50 | ||
|
|
55b166f3bc | ||
|
|
2f9375a540 | ||
|
|
0df8ae8a32 | ||
|
|
989ae8c81e | ||
|
|
f5d1c2010c | ||
|
|
0dd8d57ac5 | ||
|
|
26eb23f784 | ||
|
|
0e9cfa75a2 | ||
|
|
f0f5af3f6f | ||
|
|
53c5ba2df3 | ||
|
|
20e56aeb59 | ||
|
|
02732bdc62 |
654 changed files with 101741 additions and 10765 deletions
|
|
@ -7,6 +7,11 @@ node_modules
|
|||
server/dist
|
||||
web/dist
|
||||
shared/dist
|
||||
desktop/.desktop-release
|
||||
desktop/release
|
||||
.veritas-desktop-dev
|
||||
playwright-report
|
||||
test-results
|
||||
|
||||
# Git
|
||||
.git
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ VERITAS_ADMIN_KEY=
|
|||
# http://127.0.0.1:5173,http://127.0.0.1:3000
|
||||
# CORS_ORIGINS=http://localhost:5173,http://localhost:3000
|
||||
|
||||
# Optional operator HTTP proxy for selective run-scoped egress.
|
||||
# Destination policy is still evaluated and the pinned IP is sent through CONNECT.
|
||||
# Credentials are held in memory and are not persisted in launch or telemetry evidence.
|
||||
# VERITAS_EGRESS_UPSTREAM_PROXY=http://proxy-user:proxy-password@proxy.internal:3128
|
||||
|
||||
# ── Logging ──────────────────────────────────────────────────
|
||||
# Pino log level: fatal | error | warn | info | debug | trace | silent
|
||||
# LOG_LEVEL=info
|
||||
|
|
|
|||
25
.github/PULL_REQUEST_TEMPLATE.md
vendored
25
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
A clear and concise description of what this PR does.
|
||||
|
||||
## Scope
|
||||
|
||||
**Linked issue:** Closes #
|
||||
|
||||
**In scope:** One independently shippable behavior and the documentation needed
|
||||
to use it.
|
||||
|
||||
**Linked follow-ups:** List separable UI, integration, refactor, or hardening
|
||||
work that was intentionally kept out of this PR, or write `None`.
|
||||
|
||||
## Type of Change
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
|
|
@ -15,6 +25,15 @@ A clear and concise description of what this PR does.
|
|||
**How has this been tested?**
|
||||
Describe the tests you ran to verify your changes. Provide instructions so reviewers can reproduce.
|
||||
|
||||
**Verification tier:**
|
||||
|
||||
- [ ] Documentation or static checks only
|
||||
- [ ] Explicit focused diagnostic (manual workflow dispatch)
|
||||
- [ ] Full milestone gate (`ci:full`, critical security, integration, or release)
|
||||
|
||||
**Why this tier is sufficient:** Explain the changed behavior, covered failure
|
||||
modes, and why broader gates are or are not required.
|
||||
|
||||
**Test commands:**
|
||||
|
||||
```bash
|
||||
|
|
@ -23,9 +42,13 @@ Describe the tests you ran to verify your changes. Provide instructions so revie
|
|||
|
||||
## Checklist
|
||||
|
||||
- [ ] This PR contains one coherent, independently shippable behavior
|
||||
- [ ] Separable follow-up work is linked instead of folded into this PR
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] I have added or updated coverage for the next declared test milestone
|
||||
- [ ] I have updated the documentation accordingly
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] Any breaking changes have been documented in the PR description
|
||||
- [ ] I did not rerun unchanged passing gates after documentation or formatting-only edits
|
||||
- [ ] Optional desktop, artifact, and release workflows are marked relevant only when this PR touches their product boundary
|
||||
|
|
|
|||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
|
|
@ -12,6 +12,12 @@ updates:
|
|||
- 'BradGroux'
|
||||
labels:
|
||||
- 'dependencies'
|
||||
ignore:
|
||||
# jsdom 30 requires Node >=22.22.2 and currently breaks the Mantine UI suite.
|
||||
# Keep receiving jsdom 29 patches until the runtime floor is deliberately raised.
|
||||
- dependency-name: 'jsdom'
|
||||
update-types:
|
||||
- 'version-update:semver-major'
|
||||
groups:
|
||||
# Group minor/patch updates to reduce PR noise
|
||||
production-dependencies:
|
||||
|
|
|
|||
466
.github/workflows/ci.yml
vendored
466
.github/workflows/ci.yml
vendored
|
|
@ -9,25 +9,153 @@ on:
|
|||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
test_scope:
|
||||
description: Unit-test verification tier
|
||||
required: true
|
||||
default: full
|
||||
type: choice
|
||||
options:
|
||||
- focused
|
||||
- full
|
||||
base_sha:
|
||||
description: Optional base commit for a focused run (defaults to HEAD^)
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
group: >-
|
||||
${{
|
||||
format(
|
||||
'{0}-{1}-{2}',
|
||||
github.workflow,
|
||||
github.ref,
|
||||
github.event_name == 'pull_request' &&
|
||||
contains(fromJSON('["labeled","unlabeled"]'), github.event.action) &&
|
||||
github.event.label.name != 'ci:full' &&
|
||||
format('cosmetic-{0}', github.run_id) ||
|
||||
'authoritative'
|
||||
)
|
||||
}}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
NODE_VERSION: '22'
|
||||
|
||||
jobs:
|
||||
# ─── Deterministic Test Scope ───────────────────────────────────
|
||||
select-tests:
|
||||
name: Select Test Scope
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
scope: ${{ steps.scope.outputs.scope }}
|
||||
packages: ${{ steps.scope.outputs.packages }}
|
||||
base_sha: ${{ steps.scope.outputs.base_sha }}
|
||||
diff_range: ${{ steps.scope.outputs.diff_range }}
|
||||
reason: ${{ steps.scope.outputs.reason }}
|
||||
coverage_packages: ${{ steps.scope.outputs.coverage_packages }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Verify CI scope controls
|
||||
run: >-
|
||||
node --test
|
||||
scripts/check-actions-pinned.test.mjs
|
||||
scripts/check-delivery-cadence.test.mjs
|
||||
scripts/check-security-gates.test.mjs
|
||||
scripts/check-tracked-ignore.test.mjs
|
||||
scripts/select-ci-test-scope.test.mjs
|
||||
|
||||
- name: Guard delivery cadence
|
||||
run: node scripts/check-delivery-cadence.mjs
|
||||
|
||||
- name: Guard immutable GitHub Actions references
|
||||
run: node scripts/check-actions-pinned.mjs
|
||||
|
||||
- name: Guard continuous security gates
|
||||
run: node scripts/check-security-gates.mjs
|
||||
|
||||
- name: Reject tracked files covered by ignore rules
|
||||
run: node scripts/check-tracked-ignore.mjs
|
||||
|
||||
- name: Select verification tier
|
||||
id: scope
|
||||
shell: bash
|
||||
env:
|
||||
CI_EVENT_NAME: ${{ github.event_name }}
|
||||
CI_MANUAL_SCOPE: ${{ inputs.test_scope || '' }}
|
||||
CI_PR_LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }}
|
||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha || '' }}
|
||||
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }}
|
||||
PUSH_BEFORE_SHA: ${{ github.event.before || '' }}
|
||||
DISPATCH_BASE_SHA: ${{ inputs.base_sha || '' }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
case "$CI_EVENT_NAME" in
|
||||
pull_request)
|
||||
CI_BASE_SHA="$PR_BASE_SHA"
|
||||
CI_HEAD_SHA="$PR_HEAD_SHA"
|
||||
;;
|
||||
push)
|
||||
CI_BASE_SHA="$PUSH_BEFORE_SHA"
|
||||
CI_HEAD_SHA="$GITHUB_SHA"
|
||||
if [[ "$CI_BASE_SHA" =~ ^0+$ ]]; then
|
||||
CI_BASE_SHA="$(git rev-parse "${GITHUB_SHA}^")"
|
||||
fi
|
||||
;;
|
||||
workflow_dispatch)
|
||||
CI_HEAD_SHA="$GITHUB_SHA"
|
||||
if [[ -n "$DISPATCH_BASE_SHA" ]]; then
|
||||
if [[ ! "$DISPATCH_BASE_SHA" =~ ^[0-9a-fA-F]{7,40}$ ]]; then
|
||||
echo "::error::base_sha must be a 7-40 character hexadecimal commit ID"
|
||||
exit 1
|
||||
fi
|
||||
CI_BASE_SHA="$(git rev-parse --verify "${DISPATCH_BASE_SHA}^{commit}")"
|
||||
else
|
||||
CI_BASE_SHA="$(git rev-parse "${GITHUB_SHA}^")"
|
||||
fi
|
||||
;;
|
||||
schedule)
|
||||
CI_BASE_SHA="$GITHUB_SHA"
|
||||
CI_HEAD_SHA="$GITHUB_SHA"
|
||||
;;
|
||||
*)
|
||||
echo "::error::Unsupported CI event: $CI_EVENT_NAME"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
export CI_BASE_SHA CI_HEAD_SHA
|
||||
node scripts/select-ci-test-scope.mjs
|
||||
|
||||
# ─── Lint & Type Check ───────────────────────────────────────────
|
||||
lint-and-typecheck:
|
||||
name: Lint & Type Check
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -38,6 +166,9 @@ jobs:
|
|||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Check service filesystem boundary
|
||||
run: pnpm check:service-filesystem-boundary
|
||||
|
||||
- name: Build shared (dependency for typecheck)
|
||||
run: pnpm --filter @veritas-kanban/shared build
|
||||
|
||||
|
|
@ -53,120 +184,320 @@ jobs:
|
|||
- name: Type check all packages
|
||||
run: pnpm typecheck
|
||||
|
||||
# ─── Changed PR Tests ────────────────────────────────────────────
|
||||
# ─── Focused Related Tests ──────────────────────────────────────
|
||||
test-changed:
|
||||
name: Changed Tests
|
||||
if: github.event_name == 'pull_request'
|
||||
needs: select-tests
|
||||
if: >-
|
||||
always() &&
|
||||
(
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Require a successful scope decision
|
||||
env:
|
||||
SELECTOR_RESULT: ${{ needs.select-tests.result }}
|
||||
SELECTED_SCOPE: ${{ needs.select-tests.outputs.scope }}
|
||||
SELECTED_PACKAGES: ${{ needs.select-tests.outputs.packages }}
|
||||
run: |
|
||||
if [[ "$SELECTOR_RESULT" != "success" ]]; then
|
||||
echo "::error::Select Test Scope did not complete successfully"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "$SELECTED_SCOPE" =~ ^(none|focused|full)$ ]]; then
|
||||
echo "::error::Select Test Scope returned an invalid scope"
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$SELECTED_SCOPE" == "focused" && -z "$SELECTED_PACKAGES" ]]; then
|
||||
echo "::error::Focused scope requires at least one workspace"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build shared test dependency
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
run: pnpm --filter @veritas-kanban/shared build
|
||||
|
||||
- name: Run changed workspace tests
|
||||
- name: Run related tests in affected workspaces
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
DIFF_RANGE: ${{ needs.select-tests.outputs.diff_range }}
|
||||
SELECTED_PACKAGES: ${{ needs.select-tests.outputs.packages }}
|
||||
VERITAS_DISABLE_WATCHERS: '1'
|
||||
shell: bash
|
||||
run: |
|
||||
selected_count=0
|
||||
for package_name in server web cli mcp; do
|
||||
package_tests=()
|
||||
while IFS= read -r test_file; do
|
||||
package_tests+=("${test_file#"$package_name/"}")
|
||||
done < <(
|
||||
git diff --name-only --diff-filter=ACMR "$BASE_SHA...HEAD" |
|
||||
grep -E "^${package_name}/.*\\.(test|spec)\\.[cm]?[jt]sx?$" || true
|
||||
)
|
||||
if (( ${#package_tests[@]} > 0 )); then
|
||||
for test_file in "${package_tests[@]}"; do
|
||||
pnpm --filter "@veritas-kanban/${package_name}" exec vitest run \
|
||||
--passWithNoTests \
|
||||
"$test_file"
|
||||
selected_count=$((selected_count + 1))
|
||||
done
|
||||
fi
|
||||
done
|
||||
if (( selected_count == 0 )); then
|
||||
echo "No changed server, web, CLI, or MCP test files."
|
||||
fi
|
||||
set -euo pipefail
|
||||
|
||||
- name: Run changed desktop tests
|
||||
IFS=',' read -r -a packages <<< "$SELECTED_PACKAGES"
|
||||
executed_packages=()
|
||||
{
|
||||
echo "### Changed Tests"
|
||||
echo
|
||||
echo "- Diff range: \`$DIFF_RANGE\`"
|
||||
echo "- Selected workspaces: \`$SELECTED_PACKAGES\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
for package_name in "${packages[@]}"; do
|
||||
related_files=()
|
||||
while IFS= read -r changed_file; do
|
||||
related_files+=("./${changed_file#"$package_name/"}")
|
||||
done < <(
|
||||
git diff --name-only --diff-filter=ACMR "$DIFF_RANGE" -- "$package_name/"
|
||||
)
|
||||
|
||||
if (( ${#related_files[@]} == 0 )); then
|
||||
continue
|
||||
fi
|
||||
|
||||
case "$package_name" in
|
||||
server|web|cli|mcp)
|
||||
package_filter="@veritas-kanban/${package_name}"
|
||||
extra_args=()
|
||||
if [[ "$package_name" == "web" ]]; then
|
||||
extra_args+=(--testTimeout 15000)
|
||||
fi
|
||||
;;
|
||||
desktop)
|
||||
package_filter="@veritas-kanban/desktop"
|
||||
extra_args=(--config vitest.config.ts)
|
||||
;;
|
||||
*)
|
||||
echo "::error::Unknown selected workspace: $package_name"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
pnpm --filter "$package_filter" exec vitest related \
|
||||
--run \
|
||||
--maxWorkers=4 \
|
||||
--passWithNoTests \
|
||||
"${extra_args[@]}" \
|
||||
"${related_files[@]}"
|
||||
executed_packages+=("$package_name")
|
||||
done
|
||||
|
||||
{
|
||||
if (( ${#executed_packages[@]} > 0 )); then
|
||||
echo "- Related coverage executed for: \`${executed_packages[*]}\`"
|
||||
else
|
||||
echo "- No added, copied, modified, or renamed workspace inputs required related coverage."
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Record focused-tier skip
|
||||
if: needs.select-tests.outputs.scope != 'focused'
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
SELECTED_SCOPE: ${{ needs.select-tests.outputs.scope }}
|
||||
SELECTION_REASON: ${{ needs.select-tests.outputs.reason }}
|
||||
run: |
|
||||
desktop_tests=()
|
||||
while IFS= read -r test_file; do
|
||||
desktop_tests+=("$test_file")
|
||||
done < <(
|
||||
git diff --name-only --diff-filter=ACMR "$BASE_SHA...HEAD" |
|
||||
grep -E '^desktop/.*\.(test|spec)\.[cm]?[jt]sx?$' |
|
||||
sed 's#^desktop/##' || true
|
||||
)
|
||||
if (( ${#desktop_tests[@]} == 0 )); then
|
||||
echo "No changed desktop test files."
|
||||
else
|
||||
for test_file in "${desktop_tests[@]}"; do
|
||||
pnpm --filter @veritas-kanban/desktop exec vitest run \
|
||||
--config vitest.config.ts \
|
||||
--passWithNoTests \
|
||||
"$test_file"
|
||||
done
|
||||
fi
|
||||
{
|
||||
echo "### Changed Tests"
|
||||
echo
|
||||
echo "- Decision: skipped related coverage because scope is \`$SELECTED_SCOPE\`."
|
||||
echo "- Selection reason: $SELECTION_REASON"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# ─── Full Workspace Unit Tests ───────────────────────────────────
|
||||
test-workspace:
|
||||
name: Workspace Unit Tests
|
||||
needs: select-tests
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
always() &&
|
||||
(
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Require a successful scope decision
|
||||
env:
|
||||
SELECTOR_RESULT: ${{ needs.select-tests.result }}
|
||||
SELECTED_SCOPE: ${{ needs.select-tests.outputs.scope }}
|
||||
run: |
|
||||
if [[ "$SELECTOR_RESULT" != "success" ]]; then
|
||||
echo "::error::Select Test Scope did not complete successfully"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "$SELECTED_SCOPE" =~ ^(none|focused|full)$ ]]; then
|
||||
echo "::error::Select Test Scope returned an invalid scope"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build shared (dependency for workspace tests)
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
run: pnpm --filter @veritas-kanban/shared build
|
||||
|
||||
- name: Run workspace unit tests
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
run: pnpm test:unit
|
||||
|
||||
- name: Run desktop readiness regression tests
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
run: pnpm desktop:test:readiness
|
||||
|
||||
- name: Run dual-storage parity tests
|
||||
run: pnpm --filter @veritas-kanban/server test -- src/__tests__/storage/dual-storage-parity.test.ts
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
run: >-
|
||||
pnpm --filter @veritas-kanban/server exec vitest run
|
||||
src/__tests__/storage/dual-storage-parity.test.ts
|
||||
|
||||
- name: Record full-suite evidence
|
||||
if: >-
|
||||
always() &&
|
||||
needs.select-tests.result == 'success' &&
|
||||
needs.select-tests.outputs.scope == 'full'
|
||||
env:
|
||||
DIFF_RANGE: ${{ needs.select-tests.outputs.diff_range }}
|
||||
SELECTION_REASON: ${{ needs.select-tests.outputs.reason }}
|
||||
CURRENT_JOB_STATUS: ${{ job.status }}
|
||||
run: |
|
||||
{
|
||||
echo "### Workspace Unit Tests"
|
||||
echo
|
||||
echo "- Diff range: \`${DIFF_RANGE:-not required}\`"
|
||||
echo "- Selection reason: $SELECTION_REASON"
|
||||
echo "- Workflow checkout SHA: \`$GITHUB_SHA\`"
|
||||
echo "- Current job status: \`$CURRENT_JOB_STATUS\`"
|
||||
echo "- Unit-test workspaces: \`server, web, cli, mcp\`"
|
||||
echo "- Workspace workers: \`4 maximum per Vitest project\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Record full-tier skip
|
||||
if: needs.select-tests.outputs.scope != 'full'
|
||||
env:
|
||||
SELECTED_SCOPE: ${{ needs.select-tests.outputs.scope }}
|
||||
SELECTION_REASON: ${{ needs.select-tests.outputs.reason }}
|
||||
run: |
|
||||
{
|
||||
echo "### Workspace Unit Tests"
|
||||
echo
|
||||
echo "- Decision: skipped the complete suite because scope is \`$SELECTED_SCOPE\`."
|
||||
echo "- Selection reason: $SELECTION_REASON"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# ─── Critical-path Coverage ─────────────────────────────────────
|
||||
critical-path-coverage:
|
||||
name: Critical Path Coverage
|
||||
needs: select-tests
|
||||
if: >-
|
||||
always() &&
|
||||
(
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Require a successful scope decision
|
||||
env:
|
||||
SELECTOR_RESULT: ${{ needs.select-tests.result }}
|
||||
run: |
|
||||
if [[ "$SELECTOR_RESULT" != "success" ]]; then
|
||||
echo "::error::Select Test Scope did not complete successfully"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
if: needs.select-tests.outputs.coverage_packages != ''
|
||||
with:
|
||||
# Policy downgrade and changed-critical-file checks compare against the event base SHA.
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
if: needs.select-tests.outputs.coverage_packages != ''
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
if: needs.select-tests.outputs.coverage_packages != ''
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
if: needs.select-tests.outputs.coverage_packages != ''
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Verify coverage policy
|
||||
if: needs.select-tests.outputs.coverage_packages != ''
|
||||
env:
|
||||
COVERAGE_BASE_REF: ${{ needs.select-tests.outputs.base_sha }}
|
||||
run: pnpm check:coverage-policy
|
||||
|
||||
- name: Measure and ratchet critical paths
|
||||
if: needs.select-tests.outputs.coverage_packages != ''
|
||||
env:
|
||||
COVERAGE_PACKAGES: ${{ needs.select-tests.outputs.coverage_packages }}
|
||||
COVERAGE_BASE_REF: ${{ needs.select-tests.outputs.base_sha }}
|
||||
run: pnpm test:coverage --packages "$COVERAGE_PACKAGES"
|
||||
|
||||
- name: Upload coverage reports
|
||||
if: always() && needs.select-tests.outputs.coverage_packages != ''
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: critical-path-coverage-${{ github.sha }}
|
||||
path: coverage/
|
||||
if-no-files-found: warn
|
||||
retention-days: 14
|
||||
|
||||
- name: Record coverage skip
|
||||
if: needs.select-tests.outputs.coverage_packages == ''
|
||||
run: |
|
||||
{
|
||||
echo "### Critical-path coverage ratchets"
|
||||
echo
|
||||
echo "No governed critical-path package changed in this verification scope."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# ─── Build ───────────────────────────────────────────────────────
|
||||
build:
|
||||
name: Build
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -177,6 +508,9 @@ jobs:
|
|||
- name: Build shared (dependency for all builds)
|
||||
run: pnpm --filter @veritas-kanban/shared build
|
||||
|
||||
- name: Verify native Vite config loading
|
||||
run: pnpm check:vite-native-config
|
||||
|
||||
- name: Build all packages
|
||||
run: pnpm build
|
||||
|
||||
|
|
@ -211,13 +545,17 @@ jobs:
|
|||
# ─── Security Audit ──────────────────────────────────────────────
|
||||
security-audit:
|
||||
name: Security Audit
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
!contains(fromJSON('["labeled","unlabeled"]'), github.event.action) ||
|
||||
github.event.label.name == 'ci:full'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
|
|||
85
.github/workflows/desktop-artifacts.yml
vendored
85
.github/workflows/desktop-artifacts.yml
vendored
|
|
@ -1,31 +1,9 @@
|
|||
name: Desktop Artifacts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'desktop/**'
|
||||
- 'server/**'
|
||||
- 'web/**'
|
||||
- 'shared/**'
|
||||
- 'docs/DESKTOP-RELEASE.md'
|
||||
- 'scripts/desktop-after-pack.mjs'
|
||||
- 'scripts/prepare-desktop-release.mjs'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- '.github/workflows/desktop-artifacts.yml'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'desktop/**'
|
||||
- 'docs/DESKTOP-RELEASE.md'
|
||||
- 'scripts/desktop-after-pack.mjs'
|
||||
- 'scripts/prepare-desktop-release.mjs'
|
||||
- 'package.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- '.github/workflows/desktop-artifacts.yml'
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
|
@ -38,13 +16,25 @@ env:
|
|||
jobs:
|
||||
mac-unsigned:
|
||||
name: Unsigned macOS Artifact
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Record milestone selection
|
||||
run: |
|
||||
{
|
||||
echo "### Unsigned macOS artifact milestone"
|
||||
echo
|
||||
echo "- Trigger: \`$GITHUB_EVENT_NAME\`"
|
||||
echo "- Reason: explicit \`ci:full\` or manual milestone"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -65,7 +55,7 @@ jobs:
|
|||
run: node ./node_modules/electron-builder/cli.js --mac dmg zip --publish never --config.mac.identity=null --config.mac.notarize=false
|
||||
|
||||
- name: Upload desktop artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: veritas-kanban-mac-unsigned
|
||||
path: |
|
||||
|
|
@ -78,13 +68,25 @@ jobs:
|
|||
|
||||
linux-unsigned:
|
||||
name: Unsigned Linux Artifacts
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Record milestone selection
|
||||
run: |
|
||||
{
|
||||
echo "### Unsigned Linux artifact milestone"
|
||||
echo
|
||||
echo "- Trigger: \`$GITHUB_EVENT_NAME\`"
|
||||
echo "- Reason: explicit \`ci:full\` or manual milestone"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -108,7 +110,7 @@ jobs:
|
|||
run: node ./node_modules/electron-builder/cli.js --linux AppImage deb rpm --x64 --publish never
|
||||
|
||||
- name: Upload Linux preview desktop artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: veritas-kanban-linux-unsigned
|
||||
path: |
|
||||
|
|
@ -122,13 +124,26 @@ jobs:
|
|||
|
||||
windows-unsigned:
|
||||
name: Unsigned Windows Artifacts
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
runs-on: windows-2025
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Record milestone selection
|
||||
shell: bash
|
||||
run: |
|
||||
{
|
||||
echo "### Unsigned Windows artifact milestone"
|
||||
echo
|
||||
echo "- Trigger: \`$GITHUB_EVENT_NAME\`"
|
||||
echo "- Reason: explicit \`ci:full\` or manual milestone"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -149,7 +164,7 @@ jobs:
|
|||
run: node ./node_modules/electron-builder/cli.js --win nsis zip --x64 --publish never
|
||||
|
||||
- name: Upload Windows preview desktop artifacts
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: veritas-kanban-windows-unsigned
|
||||
path: |
|
||||
|
|
|
|||
15
.github/workflows/desktop-release.yml
vendored
15
.github/workflows/desktop-release.yml
vendored
|
|
@ -24,6 +24,7 @@ concurrency:
|
|||
|
||||
env:
|
||||
NODE_VERSION: '22'
|
||||
VERITAS_BUILD_SHA: ${{ github.sha }}
|
||||
VERITAS_UPDATE_CHANNEL: ${{ github.event.inputs.channel || 'stable' }}
|
||||
|
||||
jobs:
|
||||
|
|
@ -31,15 +32,23 @@ jobs:
|
|||
name: Signed and Notarized macOS Artifact
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
||||
- name: Validate published release body
|
||||
if: github.event_name == 'release'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
version="${GITHUB_REF_NAME#v}"
|
||||
pnpm validate:release -- --version "${version}" --github --skip-build-output
|
||||
|
||||
- name: Verify signing and notarization secrets are configured
|
||||
id: notarization-secrets
|
||||
env:
|
||||
|
|
|
|||
39
.github/workflows/docker-image.yml
vendored
Normal file
39
.github/workflows/docker-image.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
name: Docker Image Contract
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: docker-image-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
contract:
|
||||
name: Build, Size, and Runtime Contract
|
||||
if: >-
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Record milestone selection
|
||||
run: |
|
||||
{
|
||||
echo "### Docker image milestone"
|
||||
echo
|
||||
echo "- Trigger: \`$GITHUB_EVENT_NAME\`"
|
||||
echo "- Reason: explicit \`ci:full\` or manual milestone"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Build production image
|
||||
run: docker build --target production --tag veritas-kanban:contract .
|
||||
|
||||
- name: Enforce image and runtime contract
|
||||
run: node scripts/check-docker-image.mjs veritas-kanban:contract
|
||||
44
.github/workflows/scheduled-qa.yml
vendored
44
.github/workflows/scheduled-qa.yml
vendored
|
|
@ -1,6 +1,9 @@
|
|||
name: Scheduled QA
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
schedule:
|
||||
- cron: '17 8 * * 1'
|
||||
workflow_dispatch:
|
||||
|
|
@ -36,14 +39,26 @@ env:
|
|||
jobs:
|
||||
playwright:
|
||||
name: Playwright E2E
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Record milestone selection
|
||||
run: |
|
||||
{
|
||||
echo "### Playwright E2E milestone"
|
||||
echo
|
||||
echo "- Trigger: \`$GITHUB_EVENT_NAME\`"
|
||||
echo "- Reason: explicit \`ci:full\`, scheduled, or manual milestone"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -70,7 +85,7 @@ jobs:
|
|||
|
||||
- name: Upload Playwright artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: playwright-artifacts
|
||||
path: |
|
||||
|
|
@ -81,16 +96,29 @@ jobs:
|
|||
|
||||
k6:
|
||||
name: k6 Load Smoke
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
contains(github.event.pull_request.labels.*.name, 'ci:full')
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
K6_PROFILE: ${{ github.event_name == 'workflow_dispatch' && inputs.load_profile || 'smoke' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Record milestone selection
|
||||
run: |
|
||||
{
|
||||
echo "### k6 milestone"
|
||||
echo
|
||||
echo "- Trigger: \`$GITHUB_EVENT_NAME\`"
|
||||
echo "- Profile: \`$K6_PROFILE\`"
|
||||
echo "- Reason: explicit \`ci:full\`, scheduled, or manual milestone"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -165,7 +193,7 @@ jobs:
|
|||
|
||||
- name: Upload k6 artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: k6-artifacts
|
||||
path: |
|
||||
|
|
|
|||
65
.github/workflows/security.yml
vendored
Normal file
65
.github/workflows/security.yml
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
name: Security Gates
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '17 9 * * 3'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
name: CodeQL
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
build-mode: none
|
||||
queries: security-extended
|
||||
- name: Analyze JavaScript and TypeScript
|
||||
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
with:
|
||||
category: '/language:javascript-typescript'
|
||||
|
||||
gitleaks:
|
||||
name: Gitleaks
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
GITLEAKS_VERSION: 8.30.1
|
||||
GITLEAKS_LINUX_X64_SHA256: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- name: Download verified gitleaks release
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
archive="$RUNNER_TEMP/gitleaks.tar.gz"
|
||||
curl --fail --silent --show-error --location \
|
||||
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
|
||||
--output "$archive"
|
||||
echo "${GITLEAKS_LINUX_X64_SHA256} ${archive}" | sha256sum --check --status
|
||||
tar -xzf "$archive" -C "$RUNNER_TEMP" gitleaks
|
||||
- name: Guard security workflow policy
|
||||
run: pnpm check:security-gates
|
||||
- name: Scan reviewed tree and test detection
|
||||
env:
|
||||
GITLEAKS_BIN: ${{ runner.temp }}/gitleaks
|
||||
run: pnpm check:gitleaks
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -44,10 +44,8 @@ tasks/archive/*.md
|
|||
tasks/backlog/*.md
|
||||
tasks/attachments/
|
||||
tasks/archive-attachments/
|
||||
storage/
|
||||
server/storage/
|
||||
!server/src/storage/
|
||||
!server/src/storage/**
|
||||
/storage/
|
||||
/server/storage/
|
||||
.veritas-kanban/*
|
||||
!.veritas-kanban/.gitkeep
|
||||
.veritas-desktop-dev/
|
||||
|
|
@ -112,7 +110,7 @@ tasks/
|
|||
!tasks/
|
||||
!tasks/examples/
|
||||
!tasks/examples/*.md
|
||||
.veritas-kanban/
|
||||
/.veritas-kanban/
|
||||
|
||||
# Local security middleware (not shared)
|
||||
server/src/middleware/external-api-key.ts
|
||||
|
|
|
|||
|
|
@ -1,22 +1,53 @@
|
|||
# Gitleaks False Positives
|
||||
# Updated: 2026-01-29 (post-history-rewrite)
|
||||
# All entries below are placeholder/example/test values, NOT real secrets.
|
||||
# CLI snapshot uses an intentionally synthetic API key in serialized output.
|
||||
cli/src/__tests__/snapshot.test.ts:generic-api-key:220
|
||||
|
||||
# Documentation example: "your-admin-key" placeholder in deployment guide
|
||||
39423f74cf3849684e8de4ebf746156a6be0ea00:docs/DEPLOYMENT.md:curl-auth-header:557
|
||||
# API documentation contains non-functional response examples.
|
||||
docs/API-REFERENCE.md:generic-api-key:991
|
||||
docs/API-WORKFLOWS.md:generic-api-key:1460
|
||||
|
||||
# Documentation example: "dev-admin-key" placeholder in security audit
|
||||
f01a0157f1d1612ab7bea646cf7265ec018e1d9a:docs/SECURITY_AUDIT_2026-01-28.md:curl-auth-header:135
|
||||
# Operator documentation uses placeholders in curl authentication examples.
|
||||
docs/DEPLOYMENT.md:curl-auth-header:926
|
||||
docs/TROUBLESHOOTING.md:curl-auth-header:230
|
||||
docs/TROUBLESHOOTING.md:curl-auth-header:258
|
||||
docs/TROUBLESHOOTING.md:curl-auth-header:261
|
||||
docs/features/prd-driven-development.md:curl-auth-header:95
|
||||
docs/features/prd-driven-development.md:curl-auth-header:775
|
||||
docs/guides/SELF_HOST.md:curl-auth-header:742
|
||||
docs/security.md:curl-auth-header:51
|
||||
docs/security.md:curl-auth-header:58
|
||||
|
||||
# Test fixture: hardcoded test JWT secret (not used in production)
|
||||
f01a0157f1d1612ab7bea646cf7265ec018e1d9a:server/src/__tests__/routes/auth.test.ts:generic-api-key:28
|
||||
f01a0157f1d1612ab7bea646cf7265ec018e1d9a:server/src/__tests__/routes/auth.test.ts:generic-api-key:29
|
||||
f01a0157f1d1612ab7bea646cf7265ec018e1d9a:server/src/__tests__/routes/auth.test.ts:generic-api-key:60
|
||||
# Demo seeding passes the operator-provided key variable to curl.
|
||||
seed-demo-data.sh:curl-auth-header:45
|
||||
|
||||
# .env.example placeholder values ("your-api-key")
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:server/.env.example:curl-auth-header:43
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:server/.env.example:curl-auth-header:46
|
||||
# Environment template documents shell-variable authentication examples.
|
||||
server/.env.example:curl-auth-header:127
|
||||
server/.env.example:curl-auth-header:130
|
||||
|
||||
# Documentation placeholder values ("your-api-key")
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:docs/security.md:curl-auth-header:40
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:docs/security.md:curl-auth-header:47
|
||||
# Compatibility test verifies redaction of a deliberately synthetic value.
|
||||
server/src/__tests__/buzz-compatibility-service.test.ts:generic-api-key:477
|
||||
|
||||
# Governance trace test verifies Stripe-shaped token redaction.
|
||||
server/src/__tests__/governance-trace-service.test.ts:stripe-access-token:22
|
||||
|
||||
# Log redaction tests require JWT- and Stripe-shaped synthetic fixtures.
|
||||
server/src/__tests__/log-redaction.test.ts:jwt:17
|
||||
server/src/__tests__/log-redaction.test.ts:stripe-access-token:30
|
||||
server/src/__tests__/log-redaction.test.ts:stripe-access-token:31
|
||||
|
||||
# Completion service test verifies JWT-shaped output redaction.
|
||||
server/src/__tests__/provider-completion-service.test.ts:jwt:314
|
||||
|
||||
# Local admission tests use synthetic idempotency keys, not credentials.
|
||||
server/src/__tests__/routes/agents-local-capability.test.ts:generic-api-key:606
|
||||
server/src/__tests__/routes/agents-local-capability.test.ts:generic-api-key:619
|
||||
|
||||
# Authentication route tests require a synthetic JWT signing value.
|
||||
server/src/__tests__/routes/auth.test.ts:generic-api-key:29
|
||||
server/src/__tests__/routes/auth.test.ts:generic-api-key:31
|
||||
server/src/__tests__/routes/auth.test.ts:generic-api-key:80
|
||||
|
||||
# Skill capability test verifies Stripe-shaped token redaction.
|
||||
server/src/__tests__/skill-capability-service.test.ts:stripe-access-token:63
|
||||
|
||||
# Multi-user UI test renders a non-secret token prefix fixture.
|
||||
web/src/__tests__/multi-user-tab.test.tsx:generic-api-key:123
|
||||
|
|
|
|||
|
|
@ -1,2 +1,5 @@
|
|||
pnpm check:security-artifacts
|
||||
pnpm check:actions-pinned
|
||||
pnpm check:tracked-ignore
|
||||
node scripts/check-delivery-cadence.mjs
|
||||
npx lint-staged
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# Pre-commit hooks for veritas-kanban
|
||||
# Install: pip install pre-commit && pre-commit install
|
||||
# Or standalone gitleaks hook (no pre-commit framework needed):
|
||||
# gitleaks protect --staged --verbose
|
||||
# Or scan the reviewed tree without the pre-commit framework:
|
||||
# gitleaks dir . --redact=100
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.21.2
|
||||
rev: 83d9cd684c87d95d656c1458ef04895a7f1cbd8e # v8.30.1
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
|
|
|
|||
240
AGENTS.md
240
AGENTS.md
|
|
@ -1,10 +1,12 @@
|
|||
# AGENTS.md — Canonical Agent Instructions for Veritas Kanban
|
||||
|
||||
> **Canonical source.** All coding harnesses — Codex, OpenClaw, Hermes, Claude, and others —
|
||||
> read this file first. Harness-specific supplements (e.g. `CLAUDE.md`) extend, never duplicate
|
||||
> or contradict, these rules.
|
||||
> **Canonical source.** Contributors and harnesses with repository-instruction discovery read
|
||||
> this file first. Every Veritas-managed run also receives an immutable task envelope; do not
|
||||
> assume a provider that disables custom instructions reads repository files implicitly.
|
||||
> Harness-specific supplements (for example `CLAUDE.md`) extend, never duplicate or contradict,
|
||||
> these rules. See `docs/AGENTS-TEMPLATE.md` for the managed-run and external-agent protocols.
|
||||
>
|
||||
> **Version:** 6.0.0
|
||||
> **Version:** 6.1.2
|
||||
> **Freshness policy:** update within two working days of any toolchain or architecture change.
|
||||
> Stale fields (package manager, Node version, provider list, test commands) are caught by
|
||||
> `pnpm check:pnpm-settings` and the smoke-test CI job.
|
||||
|
|
@ -35,7 +37,7 @@ veritas-kanban/
|
|||
├── mcp/ MCP server
|
||||
├── desktop/ Electron desktop wrapper
|
||||
├── docs/ Operator and developer documentation
|
||||
├── prompt-registry/ Prompt templates and cross-model review SOPs
|
||||
├── prompt-registry/ Prompt templates and optional review workflows
|
||||
└── .veritas-kanban/ Runtime data: agent-registry, logs, telemetry
|
||||
```
|
||||
|
||||
|
|
@ -56,9 +58,10 @@ pnpm build
|
|||
pnpm dev
|
||||
|
||||
# Tests
|
||||
pnpm test # Vitest across server, web, mcp, cli
|
||||
pnpm test:unit # Per-workspace tests sequentially
|
||||
pnpm test:e2e # Playwright end-to-end
|
||||
pnpm test # Canonical sequential workspace unit gate
|
||||
pnpm test:unit # Shared build, then server, web, CLI, and MCP
|
||||
pnpm test:coverage # Critical-path V8 coverage, HTML/JSON reports, and ratchets
|
||||
pnpm test:e2e # Playwright end-to-end, zero retries
|
||||
|
||||
# Type check (builds shared first)
|
||||
pnpm typecheck
|
||||
|
|
@ -68,7 +71,16 @@ pnpm lint
|
|||
pnpm lint:fix
|
||||
|
||||
# Smoke checks
|
||||
pnpm check:actions-pinned # Rejects mutable external GitHub Action references
|
||||
pnpm check:pnpm-settings # Validates package manager fields match this file
|
||||
pnpm check:tracked-ignore # Rejects tracked files covered by ignore rules
|
||||
pnpm check:coverage-policy # Validates coverage policy, configs, CI, and regression tests
|
||||
pnpm check:delivery-cadence # Prevents verification and review policy drift
|
||||
pnpm check:security-gates # Validates CodeQL/gitleaks workflow and exact suppressions
|
||||
pnpm check:gitleaks # Scans reviewed tree and proves new-secret detection
|
||||
pnpm check:vite-native-config # Loads web build and test configs with Vite's native loader
|
||||
pnpm check:service-filesystem-boundary # Prevents new direct filesystem imports in services
|
||||
pnpm test:ci-scope # Validates path-aware CI test selection
|
||||
pnpm smoke:cli-mcp # CLI ↔ MCP compatibility smoke test
|
||||
pnpm test:buzz:compatibility # Credential-free composed Buzz release gate
|
||||
```
|
||||
|
|
@ -78,6 +90,67 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
|
||||
---
|
||||
|
||||
## GitHub workflow
|
||||
|
||||
- Use the authenticated GitHub CLI (`gh`) as the default interface for GitHub issues, pull
|
||||
requests, releases, workflow runs, and API calls.
|
||||
- Use `git` for local repository operations and `gh` for GitHub-hosted state.
|
||||
- Do not loop through alternate connectors or permission paths while `gh` is authenticated and
|
||||
can perform the operation.
|
||||
- Fall back only when `gh` is unavailable or cannot support the required operation. Report the
|
||||
exact blocker before changing paths.
|
||||
- Source every published GitHub release body from `docs/releases/vX.Y.Z.md` and pass that file
|
||||
to `gh release create` or `gh release edit` with `--notes-file`.
|
||||
- Never hand-author or repair a release body with `--notes`, the GitHub editor, or a raw API
|
||||
body. Edit the reviewed source file first, validate it, and publish that exact file.
|
||||
- Keep each prose paragraph and list item on one logical Markdown source line. Separate blocks
|
||||
with blank lines. Do not hard-wrap release prose or add carriage returns, trailing-space hard
|
||||
breaks, literal escaped newlines, HTML `<br>` tags, or blockquotes.
|
||||
- Prefer compact, natural paragraphs over bullet-per-sentence formatting. Use lists only for
|
||||
genuinely parallel items. Keep rendered prose blocks concise so they do not become walls of
|
||||
text on GitHub's release index.
|
||||
- Run `pnpm validate:release -- --version X.Y.Z`; the post-publication `--github` form also
|
||||
requires the published GitHub body to match the reviewed file exactly.
|
||||
- After publication, inspect both the releases index and tag page. Raw Markdown validation does
|
||||
not replace a rendered-format check.
|
||||
|
||||
---
|
||||
|
||||
## Sustainable execution cadence
|
||||
|
||||
- Keep each issue and pull request to one independently shippable behavior. When implementation
|
||||
reveals a separable UI surface, secondary integration, refactor, or hardening follow-up, open
|
||||
a linked issue instead of expanding the active pull request.
|
||||
- Re-scope before continuing when an issue no longer fits one coherent review, an unexpected
|
||||
subsystem becomes necessary, or verification work is larger than the behavior being changed.
|
||||
- At the 45-minute delivery checkpoint, if the issue is not pull-request ready, stop adding scope
|
||||
and report the concrete cause. Split independent remaining work into linked issues, or continue
|
||||
only when the next step is required to preserve correctness of the current behavior.
|
||||
- During ordinary implementation, use source inspection, changed-file formatting/linting, and
|
||||
touched-package type checking. Do not run workspace unit, coverage, E2E, desktop packaging, or
|
||||
Docker contract tests between implementation PRs.
|
||||
- When a maintainer explicitly declares a focused diagnostic milestone, run the exact Vitest slice
|
||||
once with
|
||||
`pnpm --filter <package> exec vitest run <exact-test-files>`. Do not use
|
||||
`pnpm --filter <package> test -- <test-files>` or
|
||||
`pnpm --filter <package> test -- --run <test-files>`; package wrappers can ignore that file
|
||||
boundary and expand into the entire package suite.
|
||||
- Do not rerun an unchanged passing gate after documentation, comments, or formatting-only edits.
|
||||
Rerun only the checks affected by the later change.
|
||||
- Use the complete workspace suite once at an explicit integration, critical-security, or release
|
||||
milestone. Pull-request label `ci:full`, scheduled CI, and manual full dispatch are the
|
||||
authoritative broad gates. Critical coverage, unsigned desktop artifacts, and the Docker image
|
||||
contract run only at those milestones.
|
||||
- Trust `scripts/select-ci-test-scope.mjs` and the `Select Test Scope` job to record the required
|
||||
CI tier. Ordinary pull requests and `main` pushes select no workspace tests. Do not add local
|
||||
test gates merely to duplicate a future milestone.
|
||||
- Do not wait for optional desktop packaging, artifact previews, or release workflows when the
|
||||
change does not touch their product boundary. They are evidence only when declared relevant.
|
||||
- Add enough regression coverage to prove the behavior and its meaningful failure modes. Test
|
||||
count is not a quality target.
|
||||
|
||||
---
|
||||
|
||||
## Architecture rules
|
||||
|
||||
### Server (Express + TypeScript)
|
||||
|
|
@ -85,6 +158,9 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
- All routes go through centralized middleware in `server/src/middleware/`.
|
||||
- Auth: JWT + API keys. Dev bypass: `VERITAS_AUTH_LOCALHOST_BYPASS=true`.
|
||||
- Storage: always go through `storage/interfaces.ts`. Never import `fs` directly in service files.
|
||||
- Append-only durable records must complete the entire serialized write before
|
||||
sync. Never assume one `FileHandle.write()` call wrote every byte or ignore
|
||||
`bytesWritten`.
|
||||
- Error classes: `UnauthorizedError`, `ForbiddenError`, `BadRequestError`, `InternalError`.
|
||||
- Pagination: `sendPaginated(res, items, { page, limit, total })`.
|
||||
- Path traversal: always call `validatePathSegment()` on any user-supplied path component,
|
||||
|
|
@ -132,6 +208,31 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
whose built-in type and command both identify `codex` or `hermes` may infer a
|
||||
provider during migration; provider-less or profile/adapter-mismatched records
|
||||
fail closed before an attempt is created.
|
||||
- Route direct, profile, conversation, provider-handoff, child-agent, retry,
|
||||
fallback, scheduled, watcher, and workflow launches through the shared
|
||||
admission controller. A `queued` response means Veritas durably accepted
|
||||
ownership; harnesses must not submit a duplicate or create a hidden
|
||||
provider-side queue. Provider adapters require
|
||||
`provider-admission-evidence/v1` before dispatch.
|
||||
- Phase authority uses the versioned contracts in
|
||||
`shared/src/types/phase-capability.types.ts`. Compile parent, phase, agent
|
||||
profile, sandbox, tool-catalog, and launch-policy authority only through
|
||||
`phase-capability-service.ts`; never union scopes or infer missing
|
||||
dimensions. The plan artifact exception is one harness-owned exact path and
|
||||
never implies general filesystem write authority. Active phase changes go
|
||||
only through `phase-transition-service.ts` with exact attempt, sequence,
|
||||
evidence-digest, and launch-manifest compare-and-set guards. Authority
|
||||
expansion requires an exact-action approval; an emergency override requires
|
||||
`admin:manage`, expires within 24 hours, and is durably reverted. Every task
|
||||
launch, workflow step, retry or fallback, resume, follow-up, fork, compaction
|
||||
control, and provider handoff must bind the effective phase before attempt
|
||||
mutation. Descendants inherit and intersect the exact parent launch or
|
||||
transition evidence and cannot widen it. Explicit phases fail closed when any
|
||||
required dimension is not enforceable. Run tool catalogs are filtered by the
|
||||
launch phase, mediated calls re-check the active phase, and approvals bind the
|
||||
exact phase evidence and transition sequence. ACP stdio is the only current
|
||||
adapter with enforceable command and external-action mediation; other
|
||||
adapters return typed blockers for explicit phases.
|
||||
- Credential-bound tool servers persist only exact definition/scope digests and
|
||||
safe target names in `run-tool-catalog/v1`. Discovery strips their source
|
||||
environment/header values, native provider injection omits them, and
|
||||
|
|
@ -148,6 +249,26 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
high-risk environment passthrough are separate classes. Task integration
|
||||
credentials fail closed until an accepted tool or egress boundary proves
|
||||
brokered, non-bypassable delivery.
|
||||
- Atomically persist `admission-reservation/v1` before direct task attempts,
|
||||
workflow roots, executable workflow steps, pending-run state, or provider
|
||||
state. Workflow roots use the explicit `workflow-control` admission provider;
|
||||
provider-backed steps bind the resolved provider, selected host, root
|
||||
reservation, run, and step before attempt mutation. Capacity claims use the
|
||||
storage repository transaction or file lock, never process-local counters.
|
||||
Keep the invariant one-active-run-per-task policy and configured global,
|
||||
workspace, root-task, provider, and host ceilings aligned across dispatch,
|
||||
REST, and `vk`.
|
||||
Persist only a stable digest of caller-supplied idempotency values.
|
||||
Completion, interruption, cancellation, and start failure release once;
|
||||
restart recovery may reclaim only after the durable run supervisor verifies
|
||||
the original live process or session.
|
||||
- Bind every executable reservation to `execution-tree-identity/v1`. Descendants
|
||||
retain the root objective and exact parent edge across resume, follow-up,
|
||||
fork, retry, fallback, provider handoff, workflow step, and child-agent
|
||||
launches. Claim capacity and aggregate budget in the same repository lock or
|
||||
transaction. Usage events must be idempotent and attributable to one node;
|
||||
never copy cumulative parent or descendant totals into another contributor.
|
||||
Release unused reservation while retaining committed usage.
|
||||
- Persist `run-supervisor/v1` before provider dispatch. Restart recovery must
|
||||
validate the exact runtime, task-envelope, launch-manifest, worktree, host,
|
||||
lease, and process/session identity; replay only after the durable event
|
||||
|
|
@ -163,6 +284,22 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
- Tool-server environment values and credential values are never persisted.
|
||||
Credential-bound tool definitions remain fail-closed until the provider
|
||||
launch credential broker is active.
|
||||
- Run-owned commands use `run-terminal-handle/v1`, never a provider's generic
|
||||
stdin channel. The current runtime supports background pipe mode with
|
||||
exact-action approval, stable request IDs, manifest-approved executable,
|
||||
cwd, and environment posture, bounded
|
||||
cursor-addressable redacted output, bounded single/any/all waits,
|
||||
foreground detachment, process-group termination, and durable journal
|
||||
reconstruction. A dangling handle becomes `interrupted` after restart
|
||||
because inherited pipes cannot be reattached safely. PTY, interactive stdin,
|
||||
and restart reattachment fail closed until their typed controls ship.
|
||||
- Harnesses start a run-owned command with
|
||||
`POST /api/v1/run-terminals/runs/:taskId/:attemptId/execute`. Send one stable
|
||||
`requestId`, a command plus argument array, `mode: "pipe"`, start mode,
|
||||
optional worktree-relative cwd, and environment names only. A `202`
|
||||
response requires an operator decision through `run-approvals`; retry the
|
||||
identical request after approval to receive the `201` handle. Never place
|
||||
credential values in arguments or environment fields.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -292,6 +429,10 @@ src/scripts/run-harness-conformance.ts -- --suite <suite.json>
|
|||
`server/src/services/acp-stdio-adapter.ts`.
|
||||
- **Launch arguments.** Never put credential values in provider commands or arguments; use an
|
||||
allowlisted environment key or run-scoped brokered credential reference.
|
||||
- **Workspace execution trust.** Scan repository-controlled instructions,
|
||||
hooks, MCP servers, workflows, extensions, and provider configuration before
|
||||
launch. Bind the exact inventory and decision to the run launch manifest,
|
||||
then rescan before provider creation. Project policy may narrow trust only.
|
||||
- **Log redaction.** Trace logs and telemetry run through `TRACE_SECRET_PATTERNS` before storage.
|
||||
- **No credentials in PR descriptions, test fixtures, or log snippets.**
|
||||
|
||||
|
|
@ -305,6 +446,9 @@ src/scripts/run-harness-conformance.ts -- --suite <suite.json>
|
|||
- Use `vi.mock()`/`vi.fn()` to isolate external processes and HTTP calls; no live credentials
|
||||
in unit tests.
|
||||
- Credential-gated smoke tests document the tested provider version in a `@smoke` describe block.
|
||||
- Live MCP-to-HTTP integration groups require a running API and explicit
|
||||
`VK_MCP_INTEGRATION_TEST=1`; the default MCP test suite must remain
|
||||
server-independent.
|
||||
- Match actual runtime schema in test fixtures — wrong field names (`status: "success"` vs
|
||||
`success: true`) are a common source of false-passing tests.
|
||||
|
||||
|
|
@ -322,55 +466,61 @@ src/scripts/run-harness-conformance.ts -- --suite <suite.json>
|
|||
|
||||
## Conventions
|
||||
|
||||
| Artifact | Style |
|
||||
| ----------- | ---------------------------------------------------------------- |
|
||||
| TS files | `kebab-case.ts` |
|
||||
| Components | `PascalCase.tsx` |
|
||||
| Variables | `camelCase` |
|
||||
| Constants | `UPPER_SNAKE_CASE` |
|
||||
| Git commits | Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`) |
|
||||
| Branches | `feat/description-issue-number` / `fix/description-issue-number` |
|
||||
| Artifact | Style |
|
||||
| ----------- | --------------------------------------------------------- |
|
||||
| TS files | `kebab-case.ts` |
|
||||
| Components | `PascalCase.tsx` |
|
||||
| Variables | `camelCase` |
|
||||
| Constants | `UPPER_SNAKE_CASE` |
|
||||
| Git commits | Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`) |
|
||||
| Branches | `feat/description-issue-number`, `fix/...`, or `docs/...` |
|
||||
|
||||
---
|
||||
|
||||
## Code quality gates
|
||||
|
||||
1. **Cross-model review** required for non-trivial code changes. If Claude writes it, GPT
|
||||
reviews; if GPT writes it, Claude reviews. See `prompt-registry/cross-model-review.md`.
|
||||
2. **No direct `fs` imports** in service files — use the storage abstraction layer.
|
||||
3. **All provider schemas validated** — do not guess flag names; verify against versioned docs
|
||||
1. **No direct `fs` imports** in service files — use the storage abstraction layer.
|
||||
2. **All provider schemas validated** — do not guess flag names; verify against versioned docs
|
||||
or provider `--help` output.
|
||||
4. **pnpm-lock.yaml** is generated by pnpm; do not reformat or hand-edit it.
|
||||
3. **pnpm-lock.yaml** is generated by pnpm; do not reformat or hand-edit it.
|
||||
|
||||
Independent or cross-model review is optional. Run it only when the task,
|
||||
configured governance policy, issue owner, or release owner explicitly requires
|
||||
it.
|
||||
|
||||
---
|
||||
|
||||
## File locations quick-reference
|
||||
|
||||
| What | Where |
|
||||
| ---------------- | ------------------------------------- |
|
||||
| API routes | `server/src/routes/` |
|
||||
| Services | `server/src/services/` |
|
||||
| Zod schemas | `server/src/schemas/` |
|
||||
| Storage | `server/src/storage/` |
|
||||
| Server utilities | `server/src/utils/` |
|
||||
| React components | `web/src/components/` |
|
||||
| Zustand stores | `web/src/stores/` |
|
||||
| CLI commands | `cli/src/commands/` |
|
||||
| Shared types | `shared/src/` |
|
||||
| MCP server | `mcp/src/` |
|
||||
| Prompt registry | `prompt-registry/` |
|
||||
| SOPs | `docs/SOP-*.md` |
|
||||
| Agent registry | `.veritas-kanban/agent-registry.json` |
|
||||
| Agent run logs | `.veritas-kanban/logs/` |
|
||||
| Telemetry events | `.veritas-kanban/telemetry/` |
|
||||
| What | Where |
|
||||
| ----------------- | -------------------------------------------------------- |
|
||||
| API routes | `server/src/routes/` |
|
||||
| Services | `server/src/services/` |
|
||||
| Zod schemas | `server/src/schemas/` |
|
||||
| Storage | `server/src/storage/` |
|
||||
| Server utilities | `server/src/utils/` |
|
||||
| Provider adapters | `server/src/services/agent-provider-adapter-registry.ts` |
|
||||
| React components | `web/src/components/` |
|
||||
| Zustand stores | `web/src/stores/` |
|
||||
| CLI commands | `cli/src/commands/` |
|
||||
| Shared types | `shared/src/` |
|
||||
| MCP server | `mcp/src/` |
|
||||
| Prompt registry | `prompt-registry/` |
|
||||
| SOPs | `docs/SOP-*.md` |
|
||||
| Agent registry | `.veritas-kanban/agent-registry.json` |
|
||||
| Agent run logs | `.veritas-kanban/logs/` |
|
||||
| Telemetry events | `.veritas-kanban/telemetry/` |
|
||||
|
||||
---
|
||||
|
||||
## Harness-specific supplements
|
||||
## Harness instruction sources
|
||||
|
||||
| Harness | File | Purpose |
|
||||
| ----------- | ----------- | ------------------------------------------------- |
|
||||
| Claude | `CLAUDE.md` | Claude-specific lessons, cross-model review notes |
|
||||
| Codex / GPT | `AGENTS.md` | This file (canonical) |
|
||||
| Hermes | `AGENTS.md` | This file (Hermes reads AGENTS.md first) |
|
||||
| OpenClaw | `AGENTS.md` | This file |
|
||||
| Harness | Instruction source | Purpose |
|
||||
| ------------------ | ---------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| Buzz Agent | Veritas task envelope; repository files only if the runtime reads them | ACP task, worktree, tool, and completion contract |
|
||||
| Grok Build | Veritas task envelope | ACP task, worktree, tool, and completion contract |
|
||||
| GitHub Copilot CLI | Veritas task envelope | ACP task, worktree, tool, and completion contract |
|
||||
| Codex / GPT | `AGENTS.md` plus Veritas task envelope | Canonical repository rules and managed-run contract |
|
||||
| Claude Code | `AGENTS.md`, `CLAUDE.md`, and Veritas task envelope | Canonical rules plus Claude-specific lessons |
|
||||
| Hermes | `AGENTS.md` plus Veritas task envelope | Hermes reads `AGENTS.md` from the worktree |
|
||||
| OpenClaw | `AGENTS.md` plus the gateway task request | Canonical rules and callback completion contract |
|
||||
|
|
|
|||
386
CHANGELOG.md
386
CHANGELOG.md
|
|
@ -7,8 +7,387 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [6.1.2] - 2026-08-24
|
||||
|
||||
Veritas Kanban 6.1.2 completes the repository-wide reliability, security,
|
||||
storage, provider-runtime, CI, container, and supportability audit tracked in
|
||||
#1174. It is a backward-compatible patch release for 6.1.1.
|
||||
|
||||
### Added
|
||||
|
||||
- Added deterministic, milestone-scoped CI selection so ordinary pull requests
|
||||
keep test, coverage, E2E, desktop artifact, load, and Docker-contract jobs
|
||||
dormant while `ci:full`, scheduled, and manual release milestones run the
|
||||
complete gates (#1172, #1227, #1228).
|
||||
- Added risk-weighted critical-path coverage baselines and ratchets for provider
|
||||
dispatch, attempt lifecycle, authentication, storage, web API/session, CLI,
|
||||
MCP, and desktop trust boundaries (#1169, #1183).
|
||||
- Added continuous CodeQL, dependency, and secret-scanning policy enforcement,
|
||||
immutable GitHub Actions references, and repository guards that prevent those
|
||||
controls from silently regressing (#1167, #1168, #1179, #1180).
|
||||
- Triaged the initial CodeQL baseline, fixed validated request, logging,
|
||||
persisted-key, file-handling, and sandbox-read findings, and documented the
|
||||
evidence-backed disposition of non-exploitable alerts (#1231, #1232-#1235).
|
||||
- Added a production Docker runtime size contract with architecture-specific
|
||||
ceilings, non-root runtime checks, health/auth/SQLite/static-web smoke
|
||||
coverage, and a reduced build context (#1166, #1222).
|
||||
|
||||
### Changed
|
||||
|
||||
- Centralized `DATA_DIR` and `VERITAS_DATA_DIR` resolution, legacy-location
|
||||
discovery, migration, backup, integrity, and Docker-mounted runtime behavior
|
||||
behind the canonical path contract (#1162, #1184).
|
||||
- Restored the service/storage boundary across activity, progress, status
|
||||
history, scheduled deliverables, workflows, broadcasts, conflicts,
|
||||
delegation, ceremony, error analyses, permissions, lifecycle configuration,
|
||||
scheduler, reflection, chat, task, telemetry, and managed-content persistence.
|
||||
File and SQLite implementations retain their existing compatibility,
|
||||
containment, locking, and atomic-write contracts (#1163, #1190-#1220).
|
||||
- Decomposed the provider control path into explicit launch compilation, Codex
|
||||
event interpretation, runtime resolution, completion, attempt mutation, and
|
||||
adapter-registry contracts. Executable providers remain explicit and unknown
|
||||
or mismatched profiles continue to fail closed without an OpenClaw fallback
|
||||
(#1164, #1223-#1230).
|
||||
- Routed frontend JSON, blob, and stream operations through credential-aware API
|
||||
helpers, preserving cross-origin cookie authentication, base paths, and
|
||||
server error envelopes (#1165, #1218).
|
||||
- Removed four verified unused direct dependencies, regenerated the workspace
|
||||
dependency graph with pnpm 11.1.1, and reduced the server lint-warning budget
|
||||
from 600 to 458 without broad suppressions (#1170, #1173, #1217, #1221).
|
||||
- Replaced loader-fragile Vite/Vitest path handling with native ESM-compatible
|
||||
configuration and made root workspace test discovery deterministic (#1171,
|
||||
#1172, #1175, #1177, #1178, #1181).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Eliminated split runtime-state locations and service-layer persistence leaks
|
||||
that could send live data, backups, health checks, or migrations to different
|
||||
roots under custom data-directory configurations (#1162, #1163).
|
||||
- Hardened request rate limits, structured logging, persisted dynamic keys,
|
||||
bounded file reads and writes, and sandbox metadata reads identified by the
|
||||
initial CodeQL baseline (#1231, #1232-#1235).
|
||||
- Integrated coordinated validation hardening for a privately reported input
|
||||
boundary. The repository security advisory was published after supported
|
||||
6.1.2 artifacts were verified and disclosure was approved (#1236,
|
||||
[GHSA-4r99-qpvh-wrqf](https://github.com/BradGroux/veritas-kanban/security/advisories/GHSA-4r99-qpvh-wrqf)).
|
||||
- Corrected recovery-key alphabet generation and WebSocket upgrade header
|
||||
forwarding defects exposed by the final release validation (#1238, #1239).
|
||||
- Serialized complete same-task update and lifecycle operations before their
|
||||
first read, preserving archive/restore invocation order under contention
|
||||
(#1240, #1241).
|
||||
- Rejected digit-prefixed unsafe URI payloads in sanitized HTML while
|
||||
preserving safe relative links (#1242, #1243).
|
||||
|
||||
### Compatibility and operations
|
||||
|
||||
- The public REST API remains `v1`. Package, CLI, MCP, desktop, provider-profile,
|
||||
and configuration contracts remain backward compatible with 6.1.1.
|
||||
- SQLite migrations remain at 30 through 33; upgrading from 6.1.1 does not run a
|
||||
new schema migration. Runtime path normalization may move legacy files into
|
||||
the configured canonical data directory. Keep a complete stopped-writer
|
||||
backup until the upgraded runtime is accepted.
|
||||
- Rollback is restore-first: stop every writer, reinstall the prior signed
|
||||
application only when its data contracts remain compatible, and otherwise
|
||||
restore the complete pre-upgrade workspace. Never copy an older database over
|
||||
a running instance.
|
||||
|
||||
## [6.1.1] - 2026-08-22
|
||||
|
||||
Veritas Kanban 6.1.1 restores reliable Task Detail scrolling after the Mantine
|
||||
tabs migration and completes a security-audited dependency maintenance pass.
|
||||
|
||||
### Changed
|
||||
|
||||
- Clarified that independent review is owner-directed and optional rather than
|
||||
a default delivery SOP or release gate (#1156).
|
||||
- Updated supported runtime and development dependencies across the workspace,
|
||||
including Chalk 6, and refreshed transitive security override floors so both
|
||||
production and full dependency audits resolve without known vulnerabilities
|
||||
(#1149, #1155).
|
||||
- Deferred jsdom major updates in Dependabot while the current major requires a
|
||||
higher Node.js patch floor and breaks the Mantine-backed web test environment;
|
||||
jsdom 29 patch updates remain enabled (#1148).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Restored the shared overlay flex-column contract so long Task Detail content
|
||||
remains height-constrained and scrollable. Added a Chromium regression that
|
||||
verifies layout, overflow, and real wheel scrolling through the drawer
|
||||
(#1153, #1154).
|
||||
- Prevented interactive controls inside task cards from activating the card,
|
||||
restoring reliable touch status selection in WebKit after the Mantine 9.5
|
||||
update. Status-move browser coverage now waits for the visible save contract
|
||||
before closing Task Detail (#1156).
|
||||
- Made file-backed workflow operations wait for their storage directory to be
|
||||
ready, eliminating a startup race that could return `ENOENT` when the first
|
||||
workflow request arrived immediately after service construction (#1156).
|
||||
|
||||
## [6.1.0] - 2026-07-26
|
||||
|
||||
### Added
|
||||
|
||||
- Added complete workspace knowledge collections: immutable classified sources, cited and versioned derived pages, reviewed ingestion dry runs, atomic apply and reversal, cited keyword/QMD search, query promotion, and cited work-product export. Run launch evidence now restricts every agent read and mutation to the exact shared source and page resources in the persisted manifest, isolates QMD projections by manifest digest, and binds promoted or exported search evidence to the same run. Confidential and restricted previews withhold content and force redacted exports even when the collection otherwise allows full output. File and SQLite storage preserve the same workspace, digest, attribution, idempotency, contradiction, graph, and activity contracts (#867).
|
||||
- Added launch-scoped deterministic integrity linting for knowledge collections. Stable, digest-bound findings detect structural graph errors, invalid schemas and metadata, provenance gaps, changed source hashes, invalid citation locations, configurable freshness violations, orphan pages, missing canonical terms, and unanswered research questions without retaining source text in findings (#868).
|
||||
- Added explicit material-claim lifecycle controls for knowledge collections. Compare-and-set transitions are versioned, actor-attributed, evidence-linked, digest-bound, idempotent, and reversible through page history. Agents can flag claims as needs-review or disputed while only administrators can finalize supersession, retraction, or resolution, so conflicting claims remain visible instead of being silently overwritten (#868).
|
||||
- Added durable knowledge-integrity operations across file and SQLite storage. Bounded cursor-resumable lint chunks persist idempotently by run identity; optional semantic candidates flag contradictions, near-duplicates, supersession, and evidence gaps with both sides' page, claim, and source identities. Findings retain owner, status, severity, acknowledgement, due date, remediation links, occurrence history, and compare-and-set transitions, while a launch-scoped health endpoint exposes last observation, overdue work, and actionable status for scheduled workflows and operators (#868).
|
||||
- Added preview-first, attributable workspace checkpoint rewind for run-owned worktrees. Immutable content-addressed checkpoints capture Git, index, files, exclusions, ownership, conversation cursors, exact provider hunk ranges where explicit unified diffs exist, bounded retention, and direct comparisons; conflict-aware previews support digest-bound per-path `accept`, `reject`, and `leave-untouched` decisions, while recoverable storage transactions mutate only selected paths and preserve descendant state on failure. The production control route now quiesces an exact active Codex app-server turn and forks an earlier approved turn into a new live provider thread, while ambiguous cursors, external edits, unsupported providers, stale runtime evidence, and unresolved non-attribution conflicts fail closed (#872).
|
||||
- Added durable execution-tree cancellation and a provider-neutral fan-out
|
||||
circuit breaker. Operators can cancel one queued launch or an entire root
|
||||
objective through REST, `vk admission`, and Operations; root cancellation is
|
||||
recorded before descendants drain so late resume, retry, fallback,
|
||||
workflow-step, and child-agent launches fail closed. The breaker evaluates
|
||||
durable descendant count, depth, active and queued work, capacity pressure,
|
||||
and aggregate budget pressure before every expansion, persists bounded
|
||||
evidence across restart, and requires a safe operator resume before the tree
|
||||
can grow again (#1055).
|
||||
- Routed scheduled workflows, watcher continuations, conversation resume and
|
||||
follow-up, retries, fallbacks, provider handoffs, recovery, and child-agent
|
||||
starts through the same durable admission and queue contract used by direct
|
||||
tasks and workflow steps. Every source preserves its causal execution-tree
|
||||
identity, revalidates immutable launch evidence before dispatch, and rejects
|
||||
hidden adapter-owned queues or bypass launches (#1054).
|
||||
- Added one bounded durable admission queue shared by direct tasks and
|
||||
workflows. Atomic dequeue claims both a queue lease and capacity; restart
|
||||
recovery expires abandoned claims without duplicate dispatch. The scheduler
|
||||
combines explicit priority, capped age promotion, workspace fairness, and
|
||||
deterministic tie-breaking while retaining redacted conditional selection
|
||||
evidence. REST, `vk admission`, and Operations expose queue health,
|
||||
readiness, limiting scopes, age, priority, and safe cancellation with file
|
||||
and SQLite parity (#1053).
|
||||
- Added versioned execution-tree identities and aggregate budget reservations
|
||||
to direct attempts, conversation continuations, retries, fallbacks, provider
|
||||
handoffs, child agents, workflow roots, and workflow steps. Capacity and the
|
||||
strictest applicable workspace, agent, workflow, run, and root-objective
|
||||
budgets are now claimed atomically through the same file lock or SQLite
|
||||
transaction. Idempotent usage events convert reservations into attributable
|
||||
committed input/output/total tokens, cost, tool calls, runtime, idle time,
|
||||
retries, and fan-out without counting descendant totals again. Terminal
|
||||
release preserves committed usage and returns unused reservations. REST and
|
||||
`vk admission tree` expose bounded totals, remaining policy capacity,
|
||||
contributors, and the exact blocking policies (#1052).
|
||||
- Routed workflow roots and every provider-backed workflow step through the
|
||||
durable admission controller. A root now reserves `workflow-control`
|
||||
capacity before the run becomes active, while each executable step reserves
|
||||
against its resolved provider and selected host before step-attempt
|
||||
mutation or adapter dispatch. Retry and fallback attempts use new child
|
||||
reservations only after the prior reservation releases. Terminal,
|
||||
cancellation, and restart-reconciliation paths release idempotently, and
|
||||
restart recovery reclaims only the exact persisted root binding. Workflow
|
||||
run, step, and root-reservation filters are available through REST and
|
||||
`vk admission`; file and SQLite storage preserve the same inspection
|
||||
contract (#1051).
|
||||
- Added durable admission reservations for every direct agent task launch. File
|
||||
and SQLite backends atomically enforce one active run per task plus optional
|
||||
global, workspace, root-task, provider, and host ceilings for run slots,
|
||||
process slots, and operator-estimated memory. Versioned decisions distinguish
|
||||
retryable overload from terminal policy denial before attempt persistence or
|
||||
provider dispatch. Lease recovery is bound to verified live supervisors,
|
||||
terminal paths release idempotently, and file heartbeat history compacts
|
||||
atomically. Operators can inspect active and recent reservations through
|
||||
read-scoped REST and `vk admission` JSON commands (#1050).
|
||||
- Added a first-class workflow definition browser with deep-linked view, edit, and duplicate routes. Definition detail now exposes agents, ordered steps, phases, inputs, acceptance criteria, gates, loops, parallel branches, outputs, and provenance before execution. Server-owned access evidence distinguishes user-owned, shared, and built-in workflows; read-only definitions explain the restriction and offer duplication when permitted. User-owned edits save through Author with version-bound conflict protection that preserves the draft on failure. Starting a run is now a separate configuration step for optional task association and JSON context (#940).
|
||||
- Enforced active phase authority across run tool discovery, mediated
|
||||
invocation, approvals, completion evidence, REST, CLI, and the task run
|
||||
timeline. MCP `readOnlyHint` annotations now classify external reads while
|
||||
unannotated tools fail closed as mutations; disallowed tools and credentials
|
||||
stay out of launch catalogs, and hidden or stale calls are rejected against
|
||||
the current transition evidence before dispatch. Approval decisions cannot
|
||||
outlive or widen their bound phase, and completion records identify the
|
||||
effective phase plus every authority-expanding transition. Redacted support
|
||||
bundles include bounded phase identities, authority counts, source kinds,
|
||||
transition expansions, and completion bindings without exporting exact
|
||||
paths, credentials, or full digests. ACP stdio exposes the required
|
||||
pre-execution mediation; adapters without equivalent command and
|
||||
external-action controls return typed blockers for explicit phases. Legacy
|
||||
attempts remain readable without invented transition state (#1033).
|
||||
- Propagated immutable phase authority through task previews and starts,
|
||||
workflow steps, retries and fallbacks, provider changes, conversation resume,
|
||||
follow-up and fork operations, and active-run controls. Every executable
|
||||
entry now resolves the exact parent launch or current transition evidence
|
||||
before attempt mutation, persists the compiled phase digest and source
|
||||
references, intersects descendants monotonically, and returns typed blockers
|
||||
when the selected sandbox, runtime, host, or tool policy cannot enforce a
|
||||
required phase dimension. Legacy launches remain explicit and readable
|
||||
without inventing restrictions (#1036).
|
||||
- Added the append-only `phase-transition-record/v1` journal for active runs.
|
||||
Every transition is bound to the exact attempt, prior sequence, compiled
|
||||
phase evidence, and launch-manifest digest. Narrowing transitions apply
|
||||
immediately, while authority expansion uses the existing exact-action
|
||||
approval broker. Administrators can apply an auditable emergency expansion
|
||||
for at most 24 hours; expiry durably restores the prior evidence. File and
|
||||
SQLite repositories preserve restart recovery and idempotency, and new REST
|
||||
and CLI controls expose current state, history, transitions, and approval
|
||||
decisions (#1035).
|
||||
- Added the provider-neutral `phase-capability-profile/v1` foundation with
|
||||
strict schemas and built-in explore, plan, implement, verify, and publish
|
||||
profiles. A pure compiler now intersects phase requests with parent, agent,
|
||||
sandbox, tool-catalog, and launch-policy authority without ever widening a
|
||||
scope. Required unsupported or unenforceable dimensions return typed
|
||||
fail-closed blockers, legacy launches are explicit, and the optional plan
|
||||
artifact is restricted to one normalized harness-owned exact path that
|
||||
cannot grant general filesystem or indirect shell-write authority (#1034).
|
||||
- Added a provider-neutral workspace execution trust gate that scans the exact
|
||||
task worktree for repository-controlled agent instructions, provider
|
||||
configuration, MCP servers, hooks, language-server settings, workflows,
|
||||
extensions, skills, and agent definitions before launch. Stable identity
|
||||
combines canonical worktree, repository, Git common-directory, and
|
||||
credential-redacted remote evidence so sibling, nested, moved, and linked
|
||||
worktrees cannot borrow authorization. Append-only trusted, restricted,
|
||||
denied, revoked, and expiring decisions bind to an exact inventory digest;
|
||||
project policy can only narrow them. Executable configuration requires
|
||||
explicit authorization, while model-only instructions can run provisionally
|
||||
only under enforced read-only, no-network, credential-free restricted
|
||||
controls. The immutable launch manifest records redacted inventory and
|
||||
decision evidence, and a final pre-spawn rescan blocks any drift. Added
|
||||
administrator REST and CLI scan, decide, and revoke controls (#878).
|
||||
- Added run-scoped filesystem sandbox enforcement for local ACP, Claude Code,
|
||||
Codex app-server, Codex CLI, and Hermes processes. Required presets compile
|
||||
explicit read, write, deny, dotfile, protected-metadata, temporary, and cache
|
||||
rules into a version-bound `codex sandbox` wrapper before provider creation.
|
||||
Workspace and home aliases fail closed on traversal or symlink escape, and
|
||||
nested mount boundaries and pre-existing external hard-link aliases are
|
||||
denied and rechecked before spawn. Failed or byte-changed backends block
|
||||
launch. CLI package/virtual-environment roots, linked-worktree Git metadata,
|
||||
and `.git`, `.agents`, `.codex`, and `.veritas-kanban` directly beneath
|
||||
writable roots are bound read-only, while protected paths cannot themselves
|
||||
be writable roots or symlinks. Ambient Git config is replaced with a
|
||||
run-scoped identity-only environment. Immutable manifests retain only
|
||||
canonical path hashes, the exact provider-runtime digest, backend
|
||||
executable-content and conformance evidence, and durable cleanup state.
|
||||
Local native and wrapped runs receive supervisor-owned temporary and cache
|
||||
directories, while remote native enforcement must prove every active
|
||||
filesystem and lifecycle capability. Cleanup rejects symlinked ancestors
|
||||
(#862).
|
||||
- Added one durable `run-recovery/v1` state machine for production task
|
||||
attempts and workflow steps. Only classified transient transport, provider
|
||||
availability, rate-limit, timeout, and verification failures retry; policy,
|
||||
configuration, cancellation, destructive partial-side-effect, and unknown
|
||||
failures fail closed or require operator review. Recovery records preserve
|
||||
causal parents, exponential jittered backoff, selected routes, launch
|
||||
manifest digests, and cumulative budgets; compatible fallbacks are probed
|
||||
through runtime capability and sandbox gates before launch. Pending recovery
|
||||
survives restart, duplicate terminal callbacks cannot create multiple
|
||||
branches, and exact task recovery can be cancelled through REST, CLI, or MCP
|
||||
controls. Workflow recovery exposes the same exact-parent cancellation
|
||||
through REST (#861).
|
||||
|
||||
### Changed
|
||||
|
||||
- Aligned contributor verification with the sustainable delivery cadence: each change now gets one risk-proportional review, focused checks for the affected product boundary, and runtime smoke tests only when runtime behavior changes. Complete workspace build, typecheck, test, security, integration, E2E, and artifact gates remain milestone-only, while the dependency-free cadence checker now rejects the retired per-commit multi-review, per-merge broad-gate, and unconditional browser-smoke language if it returns. Cadence checker changes are verified inside the CI scope-control job and no longer trigger an unrelated workspace unit suite (#1048).
|
||||
- Standardized focused Vitest verification on direct
|
||||
`pnpm --filter <package> exec vitest run <exact-test-files>` invocation. The
|
||||
dependency-free delivery cadence checker now rejects active guidance that
|
||||
presents either of the ambiguous package `test -- <test-files>` or
|
||||
`test -- --run <test-files>` wrappers as focused verification, preventing
|
||||
an intended file slice from silently expanding into an entire package suite
|
||||
(#1044, #1058).
|
||||
- Reworked GitHub release notes to use natural page-width prose and concise
|
||||
lists instead of ragged hanging-indent blocks or unmarked stacks of bold-led
|
||||
paragraphs. The release-format gate now rejects long wrapping list items,
|
||||
nested headings, bold-led prose items, and consecutive sentence-sized blocks
|
||||
in v6.0.2 and later release bodies (#1025).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Completed append-only admission snapshot writes before sync so a short
|
||||
`FileHandle.write()` cannot leave a truncated JSONL record that makes durable
|
||||
reservation state unreadable (#1139).
|
||||
- Restored the complete `node:fs/promises` surface in filesystem test doubles
|
||||
after centralized storage added `lstat`, clearing collateral concurrent-suite
|
||||
failures without weakening production path checks (#1136).
|
||||
- Mapped the exact knowledge-collection router prefix to shared work-product
|
||||
read/write permissions, restoring fail-closed client/server permission
|
||||
coverage parity (#1141).
|
||||
- Updated the CLI native-fork regression coverage to validate the required
|
||||
generated idempotency key before comparing the stable request payload,
|
||||
clearing the release-branch related-test gate (#1143).
|
||||
- Separated live MCP-to-HTTP task and sprint integration groups from the
|
||||
server-independent unit suite so focused CI no longer attempts localhost API
|
||||
calls without a running server (#1144).
|
||||
|
||||
## [6.0.2] - 2026-07-24
|
||||
|
||||
Veritas Kanban 6.0.2 is a desktop recovery and supportability hotfix. It keeps
|
||||
Chat contained within a reversible Workbench dock, adds authoritative native
|
||||
version/build information, and makes CI verification proportional to the
|
||||
change while retaining full release gates.
|
||||
|
||||
### Changed
|
||||
|
||||
- Made CI test scope deterministic and path-aware. Documentation-only changes
|
||||
skip workspace suites, ordinary code changes run Vitest related coverage for
|
||||
affected packages, and CI, manifest, shared, storage, or desktop changes
|
||||
conservatively select the full gate. A successful reviewed full-suite head is
|
||||
reused after merge only when it is an ancestor of the merge commit, avoiding
|
||||
a duplicate full run while scheduled and explicit release gates remain
|
||||
authoritative. Dual-storage parity now invokes its exact Vitest file instead
|
||||
of expanding through the package test wrapper, and job summaries retain exact
|
||||
range and selection evidence (#1000).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Updated QMD query scoping to pass one documented `-c` flag per collection.
|
||||
Current QMD versions silently ignore the former unknown `--collections`
|
||||
argument, which could broaden a supposedly scoped search (#867).
|
||||
- Replaced the desktop-only bottom Chat surface with a bounded dock that opens on
|
||||
the right by default and can switch between Right and Bottom without remounting
|
||||
the active conversation. Both dimensions are clamped against the live viewport,
|
||||
chat scrolling cannot move the application shell, wheel input cannot resize the
|
||||
dock, and Close, Escape, Back, or Reset Layout always preserve a visible board
|
||||
and restore focus (#1004).
|
||||
- Added a native **About Veritas Kanban** panel and offline **Copy Version
|
||||
Information** action using Electron's authoritative application version. The
|
||||
same record now reports the embedded release commit, stable/beta/development
|
||||
channel, OS, and architecture through About, clipboard support text, the
|
||||
desktop bridge, and updater fallback without depending on the renderer or
|
||||
network (#1005).
|
||||
|
||||
## [6.0.1] - 2026-07-24
|
||||
|
||||
Veritas Kanban 6.0.1 is the first supported stable v6 release. It supersedes
|
||||
the quarantined 6.0.0 prerelease after closing the post-publication desktop and
|
||||
workflow stabilization backlog tracked in #924.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Kept desktop Chat in a bounded, reversible workbench panel; Escape and browser
|
||||
Back now close it, obsolete persisted panel visibility is discarded, compact
|
||||
window heights preserve the application shell, and the native Navigate menu
|
||||
can reset layout state without deleting application data (#945).
|
||||
- Normalized omitted workflow and run collections in the task Workflow action,
|
||||
preventing undefined length crashes and providing a recoverable load error
|
||||
with Retry instead of replacing the task surface with an error boundary
|
||||
(#936).
|
||||
- Made **New Profile** open a focused, validated scoring draft from both Profiles
|
||||
and Score Explorer, with cancel returning to the originating tab without
|
||||
creating an orphan profile (#943).
|
||||
- Let Archive cards grow with expanded content, clamp collapsed text by whole
|
||||
lines with accessible full text, wrap long metadata safely, and keep Restore
|
||||
reachable beside long task content (#939).
|
||||
- Established one bounded, keyboard-focusable scroll owner for task drawers and
|
||||
shared overlays, kept drawer chrome fixed, and made task description editors
|
||||
taller and vertically resizable (#935).
|
||||
- Preserved in-app route origins, scroll positions, and task return paths across
|
||||
full-page navigation, added browser Back and `Cmd+[` support, and made direct
|
||||
links fall back safely to Board (#937).
|
||||
- Reconciled Operations Digest totals with board inventory, exposed auditable
|
||||
inclusion and exclusion reasons plus metadata quality findings, bounded
|
||||
observed wall time to the selected window, and documented current-state
|
||||
versus windowed metrics (#944).
|
||||
- Removed competing profile-editor and scorer-list scroll containers so Agent
|
||||
Output Scoring uses the application page scrollbar at compact and full
|
||||
desktop sizes (#938).
|
||||
- Expanded the template editor into a responsive authoring surface with one
|
||||
bounded scroll region, fixed actions, a resizable Markdown editor, inline
|
||||
validation, and unsaved-change protection (#941).
|
||||
- Made first-run setup version-neutral and sourced desktop bridge version
|
||||
reporting from Electron's packaged application metadata (#986).
|
||||
|
||||
## [6.0.0] - 2026-07-24
|
||||
|
||||
> Quarantined prerelease. Use 6.0.1 or newer. The 6.0.0 artifacts remain
|
||||
> available only for investigation and historical release evidence.
|
||||
|
||||
Each release entry below names its tracking issue. The complete issue-to-pull
|
||||
request mapping, including multi-PR fixes and release engineering, is retained
|
||||
in the [v6 Release Candidate Evidence Packet](docs/V6-RC-EVIDENCE-PACKET.md#issue-and-pull-request-traceability).
|
||||
|
|
@ -2168,7 +2547,12 @@ Veritas Kanban is an AI-native project management board built for developers and
|
|||
|
||||
_Built by [Digital Meld](https://digitalmeld.io) — AI-driven enterprise automation._
|
||||
|
||||
[unreleased]: https://github.com/BradGroux/veritas-kanban/compare/v6.0.0...HEAD
|
||||
[unreleased]: https://github.com/BradGroux/veritas-kanban/compare/v6.1.2...HEAD
|
||||
[6.1.2]: https://github.com/BradGroux/veritas-kanban/compare/v6.1.1...v6.1.2
|
||||
[6.1.1]: https://github.com/BradGroux/veritas-kanban/compare/v6.1.0...v6.1.1
|
||||
[6.1.0]: https://github.com/BradGroux/veritas-kanban/compare/v6.0.2...v6.1.0
|
||||
[6.0.2]: https://github.com/BradGroux/veritas-kanban/compare/v6.0.1...v6.0.2
|
||||
[6.0.1]: https://github.com/BradGroux/veritas-kanban/compare/v6.0.0...v6.0.1
|
||||
[6.0.0]: https://github.com/BradGroux/veritas-kanban/compare/v5.2.5...v6.0.0
|
||||
[5.2.5]: https://github.com/BradGroux/veritas-kanban/compare/v5.2.4...v5.2.5
|
||||
[5.2.4]: https://github.com/BradGroux/veritas-kanban/compare/v5.2.3...v5.2.4
|
||||
|
|
|
|||
18
CLAUDE.md
18
CLAUDE.md
|
|
@ -1,10 +1,10 @@
|
|||
# CLAUDE.md — Claude-Specific Supplement for Veritas Kanban
|
||||
|
||||
> **Canonical instructions are in `AGENTS.md`.** Read that file first. This supplement contains
|
||||
> Claude-specific lessons, cross-model review workflow, and common mistakes caught by previous
|
||||
> Claude runs. Do not duplicate `AGENTS.md` content here.
|
||||
> Claude-specific lessons and common mistakes caught by previous Claude runs. Do not duplicate
|
||||
> `AGENTS.md` content here.
|
||||
>
|
||||
> **Last updated:** 2026-07-24 (v6.0.0 release freshness)
|
||||
> **Last updated:** 2026-08-24 (v6.1.2 release freshness)
|
||||
> **Freshness check:** Update after mistakes; review monthly.
|
||||
|
||||
---
|
||||
|
|
@ -16,14 +16,8 @@ that was previously embedded here. The fields updated from their stale v2.0 valu
|
|||
|
||||
- **pnpm:** was `9+` → now `≥ 11.0.0` (pinned `pnpm@11.1.1`)
|
||||
- **Node:** was `22+` → now `≥ 22.22.1`
|
||||
- **Providers:** `hermes-cli` added; OpenClaw gateway dispatch documented
|
||||
|
||||
---
|
||||
|
||||
## Cross-model review workflow
|
||||
|
||||
Claude writes code → GPT reviews before merge. GPT writes code → Claude reviews.
|
||||
See `prompt-registry/cross-model-review.md` for the prompt template.
|
||||
- **Providers:** managed Buzz, Grok Build, Codex, Claude Code, Copilot CLI,
|
||||
Hermes, and OpenClaw contracts are documented in `AGENTS.md`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -62,7 +56,7 @@ See `prompt-registry/cross-model-review.md` for the prompt template.
|
|||
## When to update this file
|
||||
|
||||
- After a mistake that a rule would have prevented.
|
||||
- After a cross-model review catches something systemic.
|
||||
- After any review catches a systemic pattern worth preserving.
|
||||
- Monthly freshness review.
|
||||
|
||||
---
|
||||
|
|
|
|||
186
CONTRIBUTING.md
186
CONTRIBUTING.md
|
|
@ -73,34 +73,75 @@ veritas-kanban/
|
|||
|
||||
2. Make your changes — write code, add tests, update docs.
|
||||
|
||||
3. Run type checking, linting, and focused tests before committing:
|
||||
3. Run touched-package type checking and changed-file linting before
|
||||
committing. Ordinary implementation pull requests do not run workspace
|
||||
tests:
|
||||
|
||||
```bash
|
||||
pnpm typecheck
|
||||
pnpm lint
|
||||
pnpm --filter @veritas-kanban/server exec vitest run src/path/to/changed.test.ts
|
||||
pnpm --filter @veritas-kanban/server typecheck
|
||||
pnpm exec eslint server/src/path/to/changed.ts
|
||||
```
|
||||
|
||||
Use `pnpm test` at integration or release milestones, or when the change
|
||||
affects shared foundations broadly enough that focused selection is unsafe.
|
||||
At an explicitly declared focused diagnostic milestone, use direct
|
||||
`exec vitest run` invocation for one exact-file slice. Do not use
|
||||
`pnpm --filter <package> test -- <test-files>` or
|
||||
`pnpm --filter <package> test -- --run <test-files>` as a focused command.
|
||||
Package wrappers can ignore that file boundary and expand into the entire
|
||||
package suite.
|
||||
|
||||
Build `@veritas-kanban/shared` first and type-check its known consumers when
|
||||
a shared contract changes. Use `pnpm test` at an explicit integration,
|
||||
critical-security, or release milestone, or when a maintainer explicitly
|
||||
selects the `ci:full` gate. Critical coverage, E2E, desktop packaging, and
|
||||
Docker contracts follow the same milestone boundary.
|
||||
|
||||
4. Commit using [conventional commits](#commit-conventions).
|
||||
|
||||
5. Push to your fork and open a pull request.
|
||||
|
||||
### Scope and Verification Budget
|
||||
|
||||
This cadence extends the deterministic CI selector delivered in
|
||||
[#1000](https://github.com/BradGroux/veritas-kanban/issues/1000).
|
||||
|
||||
- Keep one independently shippable behavior per issue and pull request.
|
||||
- Split separable UI work, secondary integrations, refactors, and additional
|
||||
hardening into linked follow-up issues before implementing them.
|
||||
- Re-scope when a second unexpected subsystem becomes necessary or the
|
||||
verification effort becomes larger than the changed behavior.
|
||||
- Do not rerun an unchanged passing check after documentation, comments, or
|
||||
formatting-only edits.
|
||||
- Treat `Select Test Scope` as the CI authority. Ordinary pull requests and
|
||||
`main` pushes select no workspace tests; manual focused diagnostics and full
|
||||
milestone selections are recorded in the job summary.
|
||||
- Do not wait for optional desktop artifacts, packaging previews, or release
|
||||
workflows unless the pull request changes that product boundary.
|
||||
- Test the behavior and meaningful failure modes. Do not use raw test count as
|
||||
a quality measure.
|
||||
- The dependency-free delivery cadence checker guards these rules in
|
||||
pre-commit and the early CI scope-control job without installing packages or
|
||||
running workspace tests.
|
||||
|
||||
### Branch Merge Protocol
|
||||
|
||||
**Critical:** When merging multiple feature branches, merge **one at a time**. Never batch-merge parallel branches.
|
||||
When merging multiple feature branches, merge one at a time so the next branch
|
||||
can rebase on the exact result.
|
||||
|
||||
**Process:**
|
||||
|
||||
1. Merge first branch to `main`
|
||||
2. Build all packages: `pnpm build`
|
||||
3. Run smoke tests (see [Testing Requirements](#testing-requirements))
|
||||
4. Only after smoke tests pass, merge the next branch
|
||||
5. Repeat for each branch
|
||||
2. Confirm the required GitHub checks for that pull request
|
||||
3. Rebase the next branch on the updated `main`
|
||||
4. Inspect conflict resolution and run changed-file static checks
|
||||
5. Merge the next branch
|
||||
|
||||
**Why:** Parallel branches often introduce integration issues that are hidden when batch-merging. Sequential merges with testing between each merge catch these immediately.
|
||||
The complete workspace suite, coverage, integration, E2E, desktop artifact,
|
||||
and Docker gates run once at the declared milestone. They are not repeated
|
||||
after every unrelated merge.
|
||||
|
||||
**Why:** Sequential merges keep conflicts attributable without paying the
|
||||
release-certification cost after every independent change. The declared
|
||||
milestone verifies the integrated candidate once.
|
||||
|
||||
### One Agent Per File Rule
|
||||
|
||||
|
|
@ -139,37 +180,28 @@ The `--model` flag is optional but recommended — it shows which AI model is be
|
|||
|
||||
See [SQUAD-CHAT-PROTOCOL.md](docs/SQUAD-CHAT-PROTOCOL.md) for full details.
|
||||
|
||||
### Pre-Commit Review Protocol (Mandatory)
|
||||
### Risk-Proportional Review
|
||||
|
||||
Before every commit, run these 4 reviews:
|
||||
Review the changed behavior once before committing. In that pass, cover
|
||||
correctness and any security, reliability, performance, accessibility, or
|
||||
architecture risks that actually apply to the change.
|
||||
|
||||
1. **Code Review** — Code quality, anti-patterns, architectural issues, file locking, path validation
|
||||
2. **Functionality Review** — All endpoints work, CRUD operations, settings save/load
|
||||
3. **Performance Review** — API response times, bundle size, React optimizations, memory leaks
|
||||
4. **Security Review** — Auth/authz, injection vectors, secrets exposure, CORS/CSP, rate limiting
|
||||
|
||||
All four must pass (10/10) before committing. If ANY review says unsafe:
|
||||
|
||||
1. Fix the issue
|
||||
2. Have the SAME reviewer who found it verify the fix
|
||||
3. Get human approval
|
||||
4. Then commit
|
||||
|
||||
**Never commit when a review says "unsafe." Never push without human approval.**
|
||||
|
||||
These reviews are mandatory, not optional. They catch runtime issues that static analysis and builds miss.
|
||||
Do not create separate review tasks for inapplicable categories or require
|
||||
numeric review scores. If the review finds an unsafe behavior, fix it and
|
||||
recheck the affected path before committing. Independent or cross-model review
|
||||
is optional unless a configured governance policy, issue owner, or release
|
||||
owner explicitly requires it.
|
||||
|
||||
### Pre-Merge Checklist
|
||||
|
||||
Before merging any branch, verify:
|
||||
Before merging, verify the checks selected for the changed product boundary:
|
||||
|
||||
- [ ] **Type exports:** All new types added to `shared/` are exported in `shared/src/types/index.ts`
|
||||
- [ ] **Type checks pass:** `pnpm typecheck` succeeds for all workspace packages (shared, server, web, CLI, MCP)
|
||||
- [ ] **Builds pass:** `pnpm build` succeeds for all packages (shared, server, web, CLI, MCP)
|
||||
- [ ] **No hardcoded values:** No hardcoded ports, URLs, or timeouts in application code
|
||||
- [ ] **CSP/CORS configs:** Security policies work in both `NODE_ENV=development` AND `NODE_ENV=production`
|
||||
- [ ] **Frontend hooks:** All HTTP calls use shared helpers (`apiFetch`) and all WebSocket/URL logic uses `window.location.host` (not hardcoded ports)
|
||||
- [ ] **Environment variables:** All configurable values use env vars with sensible defaults
|
||||
- [ ] **Selected CI tier:** Every required check started for the pull request is green.
|
||||
- [ ] **Implementation evidence:** The diff and applicable static checks support the changed behavior.
|
||||
- [ ] **Shared contracts, when changed:** New types are exported and known consumers type-check.
|
||||
- [ ] **Configuration, when changed:** Ports, URLs, timeouts, environment variables, CSP, and CORS behave in the affected modes.
|
||||
- [ ] **Frontend integration, when changed:** HTTP calls use shared helpers and location-sensitive behavior avoids hardcoded hosts.
|
||||
- [ ] **Milestone gate, when selected:** Complete build, typecheck, test, security, integration, E2E, or artifact checks required by `ci:full` or the release plan pass once.
|
||||
|
||||
### Environment Rules
|
||||
|
||||
|
|
@ -184,17 +216,19 @@ Before merging any branch, verify:
|
|||
|
||||
### Testing Requirements
|
||||
|
||||
**"Builds clean" is necessary but NOT sufficient.**
|
||||
Run browser or API smoke tests only at an explicit integration or release
|
||||
milestone when the change affects that product boundary. Choose the smallest
|
||||
runtime check that proves the behavior:
|
||||
|
||||
Before declaring a branch ready to merge, verify **runtime behavior:**
|
||||
- **Server or API changes:** Exercise the changed endpoint and its meaningful auth or failure path. Add a health check only when startup or routing changed.
|
||||
- **Web changes:** Open the changed route and verify its primary interaction, keyboard flow, and failure state.
|
||||
- **Realtime changes:** Verify the changed event path with the minimum number of clients needed to prove propagation.
|
||||
- **Desktop changes:** Use the relevant desktop readiness or packaging smoke check.
|
||||
- **Documentation and static tooling:** No runtime smoke is required unless deterministic CI escalates the change.
|
||||
|
||||
1. **Health check:** `curl http://localhost:3000/api/health` returns 200
|
||||
2. **Auth flow:** Log in via the UI, verify token handling works
|
||||
3. **Task CRUD:** Create, update, move, and delete a task
|
||||
4. **WebSocket connection:** Verify real-time updates work (open two browser tabs, change task in one, see update in the other)
|
||||
5. **No stray processes:** Check for leftover Vite dev servers or conflicting processes before starting: `lsof -i :3000`
|
||||
|
||||
**Static code reviews (AI or human) cannot catch runtime issues.** You must test in a running browser.
|
||||
Static review does not replace runtime evidence when runtime behavior changed,
|
||||
but unrelated browser, CRUD, WebSocket, or packaging checks add no useful
|
||||
confidence to a focused change.
|
||||
|
||||
### Common Integration Failures
|
||||
|
||||
|
|
@ -250,9 +284,11 @@ docs: update README with deployment instructions
|
|||
2. **Branch naming:** Use descriptive names like `feat/task-filters`, `fix/login-redirect`, `docs/api-reference`.
|
||||
3. **Open a PR** against `main`.
|
||||
4. **Fill out the PR template** — describe changes, link related issues, include screenshots for UI changes.
|
||||
5. **Ensure the PR CI tier passes** — all checks started for the pull request
|
||||
must be green. Full workspace and packaging evidence runs after merge, on
|
||||
explicit dispatch, or when the `ci:full` label is applied.
|
||||
5. **Ensure the selected PR CI tier passes** — all checks started for the pull
|
||||
request must be green. The scope selector records affected workspaces but
|
||||
defers their tests on ordinary pull requests. Use `ci:full` for release
|
||||
candidates, critical integration/security boundaries, or other changes that
|
||||
require an explicit complete-suite gate.
|
||||
6. **Request review** — a maintainer will review and may request changes.
|
||||
7. **Address feedback** — push additional commits as needed.
|
||||
8. **Merge** — once approved, a maintainer will merge.
|
||||
|
|
@ -275,12 +311,19 @@ Follow the existing conventions in `.eslintrc.*`, `.prettierrc`, and `tsconfig.j
|
|||
pnpm test
|
||||
```
|
||||
|
||||
This is the canonical unit gate. It builds the shared package, then runs the
|
||||
server, web, CLI, and MCP suites sequentially with at most four Vitest workers
|
||||
per project. The final line reports PASS, FAIL, or NOT RUN for every workspace.
|
||||
|
||||
- **End-to-end tests** use [Playwright](https://playwright.dev/):
|
||||
|
||||
```bash
|
||||
pnpm test:e2e
|
||||
```
|
||||
|
||||
Playwright does not retry failures. Screenshots and traces from the first
|
||||
failure are retained in `test-results/` and uploaded by Scheduled QA.
|
||||
|
||||
- **Load smoke tests** use [k6](https://k6.io/):
|
||||
|
||||
```bash
|
||||
|
|
@ -299,21 +342,44 @@ Follow the existing conventions in `.eslintrc.*`, `.prettierrc`, and `tsconfig.j
|
|||
|
||||
### CI tiers
|
||||
|
||||
| Trigger | Stable checks | Scope |
|
||||
| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| Pull request | `Lint & Type Check`, `Changed Tests`, `Build`, `Security Audit` | Static gates, full build, and only test files added or changed by the pull request |
|
||||
| Pull request with `ci:full` | Default checks plus `Workspace Unit Tests` | Complete workspace, desktop readiness regressions, and dual-storage parity |
|
||||
| Push to `main`, nightly 08:00 UTC, or manual `CI` dispatch | Static gates, `Workspace Unit Tests`, `Build`, `Security Audit` | Complete authoritative workspace suite |
|
||||
| Desktop/package/release-workflow pull request, relevant `main` push, or manual `Desktop Artifacts` dispatch | Unsigned macOS, Linux, and Windows artifact jobs | Cross-platform packaging |
|
||||
| Trigger | Stable checks | Scope |
|
||||
| ---------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Documentation-only pull request or merge | Static gates; test jobs record skip decisions | No workspace tests |
|
||||
| Ordinary code pull request or merge to `main` | `Lint & Type Check`, `Build`, `Security Audit`, scope recording | No workspace tests or coverage; affected packages remain visible |
|
||||
| Pull request with `ci:full` | Default checks plus every milestone test and artifact gate | Complete unit, coverage, desktop, Docker, and applicable integration gates |
|
||||
| Nightly 08:00 UTC or manual `CI` dispatch with `test_scope=full` | Static gates plus complete workspace and coverage gates | Authoritative recurring or operator-selected milestone |
|
||||
| Manual `CI` dispatch with `test_scope=focused` and optional `base_sha` | Static gates plus `Changed Tests` | Explicit diagnostic slice for affected workspaces; no coverage ratchet |
|
||||
| Manual `Desktop Artifacts` or `Docker Image Contract` dispatch | Selected artifact or container contract | Explicit operator milestone outside a pull request |
|
||||
|
||||
`Select Test Scope` is the decision record for each run. Its summary names the
|
||||
event, exact base/head range, changed-path count, selected tier, affected
|
||||
workspaces, and why `Changed Tests` or `Workspace Unit Tests` ran or skipped.
|
||||
Shared contracts, package manifests, lockfiles, storage implementations,
|
||||
desktop source, and known-workspace deletions are recorded as affected
|
||||
workspaces without launching tests. Build and typecheck remain
|
||||
whole-repository gates on every ordinary code pull request. The full workspace
|
||||
suite and release-grade artifact gates run at scheduled, explicit `ci:full`,
|
||||
critical integration/security, and release milestones.
|
||||
|
||||
Run the selector contract locally with:
|
||||
|
||||
```bash
|
||||
pnpm test:ci-scope
|
||||
```
|
||||
|
||||
Release validation remains the final authority: clean-clone build, full unit
|
||||
and integration suites, applicable E2E, and signed artifact verification.
|
||||
|
||||
The operational target for the default pull-request tier is under 15 minutes,
|
||||
with no desktop packaging. This is a target rather than an SLA; dependency
|
||||
installation and hosted-runner availability still vary. Behavior changes
|
||||
should include a focused test change so the pull-request selector executes
|
||||
relevant coverage.
|
||||
The operational target for the default pull-request tier is under 10 minutes,
|
||||
with no workspace tests, coverage, or desktop/container packaging. This is a
|
||||
target rather than an SLA; dependency installation and hosted-runner
|
||||
availability still vary. Behavior changes should include coverage that the
|
||||
next declared milestone can exercise.
|
||||
|
||||
Optional `Desktop Artifacts`, packaging previews, and release workflows are not
|
||||
merge blockers outside their path boundary. If one starts without providing
|
||||
evidence required by the pull request, continue based on required checks;
|
||||
maintainers may cancel the redundant run.
|
||||
|
||||
## Questions?
|
||||
|
||||
|
|
|
|||
79
Dockerfile
79
Dockerfile
|
|
@ -5,16 +5,17 @@
|
|||
# 1. deps — Install all workspace dependencies (shared cache layer)
|
||||
# 2. build-shared — Build the shared package
|
||||
# 3. build-web — Build React frontend with Vite
|
||||
# 4. build-server — Compile Express server TypeScript
|
||||
# 5. production — Minimal runtime image
|
||||
# 4. build-server — Compile the Express server TypeScript
|
||||
# 5. production-deps — Install the server-only runtime closure
|
||||
# 6. production — Minimal runtime image
|
||||
#
|
||||
# Target image size: < 200MB
|
||||
# Target image size: < 200,000,000 bytes on arm64; < 600,000,000 bytes on amd64
|
||||
# =============================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 1: Install dependencies (shared across build stages)
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM node:22-alpine AS deps
|
||||
FROM node:22-alpine3.24 AS deps
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@11.1.1 --activate
|
||||
|
||||
|
|
@ -63,45 +64,54 @@ COPY server/ ./server/
|
|||
RUN pnpm --filter @veritas-kanban/server build
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 5: Production runtime
|
||||
# Stage 5: Install the server-only production dependency closure
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM node:22-alpine AS production
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@11.1.1 --activate
|
||||
|
||||
# Security: run as non-root
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S veritas -u 1001 -G nodejs
|
||||
FROM node:22-alpine3.24 AS production-deps
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy workspace config for pnpm (include real web/package.json for lockfile integrity)
|
||||
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
|
||||
COPY shared/package.json ./shared/
|
||||
COPY server/package.json ./server/
|
||||
COPY web/package.json ./web/
|
||||
COPY cli/package.json ./cli/
|
||||
COPY mcp/package.json ./mcp/
|
||||
COPY scripts/ ./scripts/
|
||||
RUN corepack enable && \
|
||||
corepack prepare pnpm@11.1.1 --activate && \
|
||||
HUSKY=0 pnpm install --frozen-lockfile --prod --filter @veritas-kanban/server... && \
|
||||
rm -rf /root/.cache/node/corepack /root/.local/share/pnpm/store /root/.local/share/pnpm/.tools
|
||||
|
||||
# Install production-only dependencies
|
||||
# --ignore-scripts: skip husky prepare hook (not needed in container)
|
||||
# Note: web deps get installed to satisfy the lockfile, but we remove them
|
||||
# since the frontend is pre-built as static assets
|
||||
RUN pnpm install --frozen-lockfile --prod --ignore-scripts && \
|
||||
rm -rf web/node_modules && \
|
||||
pnpm store prune
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 6: Production runtime
|
||||
# ---------------------------------------------------------------------------
|
||||
# The matching Alpine base keeps Node's musl ABI while excluding npm,
|
||||
# Corepack, headers, and package-manager tooling from the runtime image.
|
||||
FROM alpine:3.24 AS production
|
||||
|
||||
# Copy built artifacts
|
||||
COPY --from=build-shared /app/shared/dist ./shared/dist
|
||||
COPY --from=build-server /app/server/dist ./server/dist
|
||||
COPY --from=build-web /app/web/dist ./web/dist
|
||||
RUN apk add --no-cache ca-certificates libstdc++ && \
|
||||
addgroup -g 1001 -S nodejs && \
|
||||
adduser -S veritas -u 1001 -G nodejs
|
||||
|
||||
# Create data directories for persistent storage and runtime config
|
||||
# Note: services resolve .veritas-kanban from both cwd/.. and cwd directly,
|
||||
# so we create it at /app/ level AND ensure server/ is writable for services
|
||||
# that use process.cwd()/.veritas-kanban when WORKDIR is /app/server
|
||||
RUN mkdir -p /app/data /app/.veritas-kanban /app/tasks && \
|
||||
chown -R veritas:nodejs /app/data /app/.veritas-kanban /app/tasks /app/server
|
||||
COPY --from=production-deps /usr/local/bin/node /usr/local/bin/node
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy only the resolved server runtime closure. The platform-specific Codex
|
||||
# binary remains available, while npm, pnpm, workspace manifests, and build
|
||||
# tooling never enter the production stage.
|
||||
COPY --from=production-deps --chown=veritas:nodejs /app/node_modules ./node_modules
|
||||
COPY --from=production-deps --chown=veritas:nodejs /app/server/node_modules ./server/node_modules
|
||||
COPY --from=production-deps --chown=veritas:nodejs /app/shared/package.json ./shared/package.json
|
||||
COPY --from=production-deps --chown=veritas:nodejs /app/server/package.json ./server/package.json
|
||||
|
||||
# Copy only built runtime artifacts. CLI, MCP, frontend dependencies, source,
|
||||
# and build tooling never enter the production stage.
|
||||
COPY --from=build-shared --chown=veritas:nodejs /app/shared/dist ./shared/dist
|
||||
COPY --from=build-server --chown=veritas:nodejs /app/server/dist ./server/dist
|
||||
COPY --from=build-web --chown=veritas:nodejs /app/web/dist ./web/dist
|
||||
|
||||
# Create the single volume-backed storage root. Runtime state is stored at
|
||||
# /app/data/.veritas-kanban and task data at /app/data/tasks.
|
||||
RUN mkdir -p /app/data && \
|
||||
chown -R veritas:nodejs /app/data /app/server
|
||||
|
||||
# Switch to non-root user
|
||||
USER veritas
|
||||
|
|
@ -117,8 +127,7 @@ EXPOSE 3001
|
|||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1
|
||||
|
||||
# Set working directory to server/ so path.resolve(cwd, '..') resolves to /app
|
||||
# (Services use process.cwd()/.. to find .veritas-kanban and tasks directories)
|
||||
# The runtime path contract is independent of cwd when DATA_DIR is set.
|
||||
WORKDIR /app/server
|
||||
|
||||
# Start server
|
||||
|
|
|
|||
149
README.md
149
README.md
|
|
@ -10,11 +10,11 @@ Start with a visual Kanban board. Add CLI, MCP, OpenClaw, Squad Chat webhooks, w
|
|||
|
||||
[](https://github.com/BradGroux/veritas-kanban/actions/workflows/ci.yml)
|
||||
[](LICENSE)
|
||||
[](CHANGELOG.md)
|
||||
[](CHANGELOG.md)
|
||||
[](https://www.typescriptlang.org/)
|
||||
[](CONTRIBUTING.md)
|
||||
|
||||

|
||||

|
||||
|
||||
> 🎬 [Watch the full demo video](https://bradgroux.github.io/veritas-kanban/demo/)
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ Want to take the easy way out? Ask your agent:
|
|||
Clone and set up veritas-kanban locally using the board-only setup path first. Install dependencies with pnpm, copy server/.env.example to server/.env, and start the dev server. Verify the UI at localhost:3000 and the API health endpoint at localhost:3001/api/health. Do not configure OpenClaw, MCP, Squad Chat webhooks, workflows, or notifications unless I explicitly ask for that layer.
|
||||
```
|
||||
|
||||
Want to do it yourself? Get up and running in under 5 minutes:
|
||||
Want to do it yourself? Choose the packaged Mac app or a local source checkout:
|
||||
|
||||
For the packaged Mac desktop app:
|
||||
|
||||
|
|
@ -92,21 +92,24 @@ When the board is working, use [Setup Paths](docs/SETUP-PATHS.md) to choose the
|
|||
|
||||
- [Setup Paths](docs/SETUP-PATHS.md) — start here for board-only, CLI, MCP, OpenClaw, and self-hosted paths without mixing optional layers into first-run setup.
|
||||
- [Getting Started Guide](docs/GETTING-STARTED.md) — zero ➝ agent-ready in 5 minutes, plus sanity checks and prompt registry tips.
|
||||
- [MCP Server Guide](docs/mcp/README.md) — optional MCP setup, 41 tools, architecture, tool catalog, security model, and read/write smoke checks.
|
||||
- [Agent Providers](docs/AGENT-PROVIDERS.md) — evidence-backed runtime manifests, Codex and Hermes execution, optional model profiles, routing, and host behavior.
|
||||
- [MCP Server Guide](docs/mcp/README.md) — optional MCP setup, 42 tools, architecture, tool catalog, security model, and read/write smoke checks.
|
||||
- [Agent Guide and `AGENTS.md` Template](docs/AGENTS-TEMPLATE.md) — shared managed-run protocol, external self-reporting template, and unmanaged MCP setup.
|
||||
- [Agent Providers](docs/AGENT-PROVIDERS.md) — evidence-backed Buzz, Grok Build, Codex, Claude Code, Copilot CLI, Hermes, OpenClaw, and optional model profiles.
|
||||
- [v6 Agent Runtime Control Plane](docs/architecture/V6-AGENT-RUNTIME-CONTROL-PLANE.md) — authority, adapter, lifecycle, approval, tool, credential, Buzz, and certification boundaries.
|
||||
- [Phase Capability Profiles](docs/architecture/PHASE-CAPABILITY-PROFILES.md) — versioned execution-phase authority contracts, deterministic intersections, exact-path plan artifacts, and current delivery boundaries.
|
||||
- [Phase Transition Journal](docs/architecture/PHASE-TRANSITION-JOURNAL.md) — durable compare-and-set transitions, approval and override controls, restart recovery, REST, and CLI operations.
|
||||
- [Knowledge Collections v1](docs/architecture/KNOWLEDGE-COLLECTIONS-V1.md) — immutable sources, cited pages, stable identity, bidirectional links, and reversible reviewed ingestion with file/SQLite parity.
|
||||
- [OpenAI Codex Integration Roadmap](docs/CODEX-INTEGRATION.md) — optional local execution, SDK sessions, cloud delegation, MCP setup, workflows, telemetry, and release QA.
|
||||
- [Veritas Cutover Operating Guide](docs/VERITAS-CUTOVER.md) — authority model, HermesAgent roster, QA evidence gate, and GitHub-backed task templates.
|
||||
- [Codex Integration SOP](docs/SOP-codex-integration.md) & [Codex Workflow Examples](docs/EXAMPLES-codex-workflows.md) — operational playbooks for using Codex as a first-class Veritas agent.
|
||||
- [API Reference](docs/API-REFERENCE.md) — Auth, endpoints, request/response examples, WebSocket, common workflows.
|
||||
- [v5 Identity and RBAC Model](docs/IDENTITY-RBAC.md) — users, workspaces, memberships, roles, agent tokens, permission matrix, migration, and UX flows.
|
||||
- [v5 Mantine Migration Plan](docs/UI-MANTINE-MIGRATION.md) — component inventory, migration order, retained custom surfaces, rollback strategy, and cleanup gates.
|
||||
- [Identity and RBAC Model](docs/IDENTITY-RBAC.md) — users, workspaces, memberships, roles, agent tokens, permission matrix, migration, and UX flows.
|
||||
- [v6 GA Checklist](docs/V6-GA-CHECKLIST.md) — release gates for harness certification, migration, runtime, desktop, and distribution evidence.
|
||||
- [v6 Visual Tour](docs/V6-VISUAL-TOUR.md) — release-safe views of provider support, Buzz setup, approvals, and run evidence.
|
||||
- [v6 Upgrade, Install, Remote, And Admin Guide](docs/V6-UPGRADE-INSTALL-ADMIN-GUIDE.md) — fresh install, v5-to-v6 upgrade, harness setup, desktop, backup, and diagnostics paths.
|
||||
- [v6 Compatibility And Release Policy](docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md) — provider support tiers, tested builds, platform combinations, update channels, and rollback limits.
|
||||
- [v6 Release Notes](docs/V6-RELEASE-NOTES.md) — harness control-plane outcomes, migrations, known limits, release artifacts, and deferred v6.x work.
|
||||
- [v5 Desktop Architecture ADR](docs/architecture/ADR-0001-v5-desktop-architecture.md) — shell decision, native/server boundaries, connection modes, lifecycle, packaging, and security model.
|
||||
- [v6 Release Notes](docs/V6-RELEASE-NOTES.md) — user-facing highlights, stabilization fixes, install/upgrade steps, behavior changes, and known limits.
|
||||
- [Desktop Architecture ADR](docs/architecture/ADR-0001-v5-desktop-architecture.md) — shell decision, native/server boundaries, connection modes, lifecycle, packaging, and security model.
|
||||
- [Post-GA Desktop Agent Workbench Spec](docs/DESKTOP-AGENT-WORKBENCH.md) — desktop workbench UX, run controls, approvals, evidence, native affordances, and safety coverage.
|
||||
- [Post-GA Native Mobile Offline ADR](docs/architecture/ADR-0003-post-ga-native-mobile-offline.md) — native mobile authority model, offline queue semantics, conflict handling, and security review.
|
||||
- [Post-GA Cloud Sync And Hosted SaaS ADR](docs/architecture/ADR-0004-post-ga-cloud-sync-hosted-saas.md) — optional hosted model, tenant isolation, lifecycle, support, cost, and migration boundaries.
|
||||
|
|
@ -151,7 +154,7 @@ When the board is working, use [Setup Paths](docs/SETUP-PATHS.md) to choose the
|
|||
|
||||
8. **Isolate environments.** Run agents in containers, VMs, or sandboxed environments when possible. Keep agent workspaces separate from sensitive data, use deny-by-default network presets for untrusted work, and broker credentials instead of exposing broad environment variables.
|
||||
|
||||
**The bottom line:** Agentic AI is transformational, but it amplifies both your capabilities and your mistakes. Plan accordingly, start small, and add autonomy gradually as you build confidence in your guardrails.
|
||||
**The bottom line:** Agents amplify both useful work and mistakes. Start locally, keep permissions narrow, and add autonomy only after the smaller setup is understood and verified.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -159,7 +162,7 @@ When the board is working, use [Setup Paths](docs/SETUP-PATHS.md) to choose the
|
|||
|
||||
### 🛡️ Agent Governance
|
||||
|
||||
**Policy Engine** — Define what agents can and can't do. Configurable tool/action policies with `allow`, `deny`, and `require-approval` guard rules. Every policy decision is logged. **Sandbox Policy Presets** — Assign reusable filesystem, network, environment, and credential rules to agents, workflow agents, or one-off runs; unsupported required controls fail closed before launch with redacted audit traces. **Decision Audit Trail** — Log agent decisions with confidence scores, supporting evidence, and stated assumptions. Record outcomes afterward to see whether assumptions held. **Behavioral Drift Detection** — Set metric baselines and thresholds; get alerted when an agent's behavior deviates. **User Feedback Loop** — Collect feedback on agent outputs with sentiment tagging and category analytics. **Output Evaluation** — Score agent outputs against weighted criteria profiles (regex, keyword, numeric range, custom expressions).
|
||||
**Policy Engine** — Define what agents can and can't do. Configurable tool/action policies with `allow`, `deny`, and `require-approval` guard rules. Every policy decision is logged. **Sandbox Policy Presets** — Assign reusable filesystem, network, environment, and credential rules to agents, workflow agents, or one-off runs; unsupported required controls fail closed before launch with redacted audit traces. **Decision Audit Trail** — Log agent decisions with confidence scores, supporting evidence, and stated assumptions. Record outcomes afterward to see whether assumptions held. **Behavioral Drift Detection** — Set metric baselines and thresholds; get alerted when an agent's behavior deviates. **User Feedback Loop** — Collect feedback on agent outputs with sentiment tagging and category analytics. **Output Evaluation** — Score agent outputs against weighted bounded criteria profiles (regex, keyword, numeric range, occurrence ratio).
|
||||
|
||||
### 🤖 Agent Orchestration
|
||||
|
||||
|
|
@ -169,11 +172,15 @@ Spawn autonomous coding agents on tasks when you choose to connect an agent runn
|
|||
|
||||

|
||||
|
||||
Desktop Board Chat and Squad Chat open in a bounded right-side Workbench dock by
|
||||
default. Switch to Bottom when vertical space is preferable; both orientations
|
||||
keep the board, header, close control, and keyboard recovery paths reachable.
|
||||
|
||||

|
||||
|
||||
### 🧭 Veritas Cutover + Hermes Support
|
||||
### 🧭 Provider And Cutover Operations
|
||||
|
||||
Veritas now documents the GitHub-backed operating model for Codex and HermesAgent work. The new cutover guide names Veritas as the source of truth, routes HermesAgent/Hermes Gateway as the control plane for agent execution, keeps Mission Control focused on display/control, and makes GitHub Issues/PRs/reviews/CI the implementation record. It also adds the active Hermes roster, required QA evidence gates, and copy/paste task templates for product specs, research/revenue intake, and approval-gated client workflows.
|
||||
The cutover guide documents a GitHub-backed operating model for Codex and HermesAgent work. Veritas remains the source of truth, HermesAgent/Hermes Gateway can provide the execution control plane, and GitHub Issues, pull requests, reviews, and CI remain the durable implementation record. Copy/paste task templates cover product specs, research intake, and approval-gated client workflows.
|
||||
|
||||
### 🧠 OpenAI Codex Integration
|
||||
|
||||
|
|
@ -203,13 +210,17 @@ Not just cards on a board. Tasks have dependency graphs with cycle detection, cr
|
|||
|
||||
Isolated worktrees per task — no branch switching, no conflicts. Built-in code review with unified diff viewer and inline comments. Approval workflows (approve, request changes, reject). Visual merge conflict resolution. Create GitHub PRs directly from the task detail panel. Bidirectional GitHub Issues sync with label mapping.
|
||||
|
||||
### 📁 Zero Infrastructure
|
||||
### 📁 Local-First Storage
|
||||
|
||||
Tasks are markdown files. Settings are JSON. Workflows are YAML. No database, no Redis, and no Docker required for local use. Clone, `pnpm install`, `pnpm dev` — done. Everything is `grep`-friendly, version-controllable, and human-readable. Back up your entire board with `git push`.
|
||||
File storage remains the zero-infrastructure default: tasks are Markdown,
|
||||
settings are JSON, and workflows are YAML. SQLite is available for governed
|
||||
multi-user and higher-integrity deployments; Redis and Docker are not required
|
||||
for local use. Clone, `pnpm install`, and `pnpm dev` to start. Back up the
|
||||
complete configured storage root, not only the Git-tracked board files.
|
||||
|
||||
### 🔌 Optional Integration Surfaces
|
||||
|
||||
- **MCP Server** — 41 tools across 9 categories via Model Context Protocol
|
||||
- **MCP Server** — 42 tools across 9 categories via Model Context Protocol
|
||||
- **CLI** — `vk begin <id>` / `vk done <id> "summary"` replaces 6 API calls with 2 commands
|
||||
- **REST API** — Full lifecycle management. If it can make HTTP calls, it can drive the board.
|
||||
|
||||
|
|
@ -243,6 +254,11 @@ Tasks are markdown files. Settings are JSON. Workflows are YAML. No database, no
|
|||
- **Team roster routing** — Workspace coordinator/member manifests route tasks by capabilities, reviewers, fallbacks, and escalation posture
|
||||
- **Workspace capability discovery** — Trusted workspace capability catalogs let Veritas package delegated work intake before handing work across workspace boundaries
|
||||
- **Agent profile packages** — Portable YAML/JSON packages that bundle role, runtime, prompt, tools, permissions, sandbox, budget, workflow, and health metadata for reusable launches
|
||||
- **Phase capability contract** — Built-in explore, plan, implement, verify, and
|
||||
publish profiles compile parent, phase, agent, sandbox, tool, and launch
|
||||
authority without widening it. The current slice defines the shared contract
|
||||
and compiler; runtime transition and enforcement work remains explicitly
|
||||
tracked.
|
||||
- **Provider-owned task envelopes** — OpenClaw, Codex CLI, Codex SDK, and Hermes render the same immutable task contract through adapter-owned transports with explicit commit policy and completion posture
|
||||
- **Decision review sessions** — Multi-participant decision reviews with independent responses, critique rounds, final synthesis packets, work-product attachment, and decision audit links
|
||||
- **Shared live run sessions** — Create workspace-scoped view, co-drive, or fork links for active task runs; viewers receive live output and events, editors send attributed messages and mobile-safe approval responses, and forks create linked tasks without mutating the parent run
|
||||
|
|
@ -311,7 +327,7 @@ Tasks are markdown files. Settings are JSON. Workflows are YAML. No database, no
|
|||
- **Activity page** — Status history with clickable task navigation, color-coded badges, and daily summary
|
||||
- **Daily standup summary** — Generate standup reports via API or CLI (`vk summary standup`)
|
||||
- **Task Templates** — Create reusable templates with defaults, subtasks, and multi-task blueprints
|
||||
- **Documentation freshness** — Steward workflow with freshness headers and automated staleness detection
|
||||
- **Documentation freshness** — Registry-backed review dates, thresholds, scores, and staleness alerts
|
||||
- **Cost prediction** — Multi-factor cost estimation for tasks
|
||||
|
||||
#### Dashboard
|
||||
|
|
@ -348,7 +364,7 @@ Tasks are markdown files. Settings are JSON. Workflows are YAML. No database, no
|
|||
#### Integration
|
||||
|
||||
- **CLI** — `vk` command for terminal workflows
|
||||
- **MCP Server** — 41 tools across 9 categories via Model Context Protocol
|
||||
- **MCP Server** — 42 tools across 9 categories via Model Context Protocol
|
||||
- **Codex MCP setup** — documented `codex mcp add veritas-kanban` setup for local and API-key-backed deployments
|
||||
- **Notifications** — Teams integration for task updates
|
||||
|
||||
|
|
@ -360,14 +376,14 @@ Tasks are markdown files. Settings are JSON. Workflows are YAML. No database, no
|
|||
|
||||
| Layer | Technology | Version |
|
||||
| ------------------- | ------------------------------------- | ------------------------------------------- |
|
||||
| **Frontend** | React, Vite, Tailwind CSS, Mantine UI | React 19, Vite 8, Tailwind 4.3, Mantine 9.3 |
|
||||
| **Frontend** | React, Vite, Tailwind CSS, Mantine UI | React 19, Vite 8, Tailwind 4.3, Mantine 9.5 |
|
||||
| **Backend** | Express, WebSocket | Express 5.2 |
|
||||
| **Language** | TypeScript (strict mode) | 6.0 |
|
||||
| **Storage** | Markdown files with YAML frontmatter | yaml + local frontmatter helper |
|
||||
| **Git** | simple-git, worktree management | — |
|
||||
| **Testing** | Playwright (E2E), Vitest (unit) | Playwright 1.61, Vitest 4.1 |
|
||||
| **Runtime** | Node.js | 22+ |
|
||||
| **Package Manager** | pnpm | 11.1.1+ |
|
||||
| **Testing** | Playwright (E2E), Vitest (unit) | Playwright 1.62, Vitest 4.1 |
|
||||
| **Runtime** | Node.js | 22.22.1+ |
|
||||
| **Package Manager** | pnpm | 11.1.1 (pinned) |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -386,7 +402,7 @@ Veritas Kanban is neither. It's the **visual command center for agentic work**
|
|||
| **YAML workflow pipelines** | ✅ Loops, gates, parallel | ⚠️ Code-defined only | ❌ |
|
||||
| **Real-time agent dashboard** | ✅ Status, model attribution | ❌ | ❌ |
|
||||
| **Agent communication** | ✅ Squad Chat with lifecycle events | ⚠️ Internal only | ❌ |
|
||||
| **MCP server** | ✅ 41 tools | ❌ | ❌ |
|
||||
| **MCP server** | ✅ 42 tools | ❌ | ❌ |
|
||||
| **CLI** | ✅ Full lifecycle | ❌ | ⚠️ Limited |
|
||||
| **Git worktrees + code review** | ✅ Built-in | ❌ | ❌ |
|
||||
| **Task persistence** | ✅ Markdown files | ❌ In-memory | ✅ Database |
|
||||
|
|
@ -470,7 +486,7 @@ veritas-kanban/ ← pnpm monorepo
|
|||
└── agent-requests/
|
||||
```
|
||||
|
||||
**Data flow:** Web ↔ REST API / WebSocket ↔ Server ↔ Markdown/YAML files on disk
|
||||
**Data flow:** Web ↔ REST API / WebSocket ↔ Server ↔ configured file or SQLite storage
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -499,7 +515,7 @@ curl -H "X-API-Version: v1" http://localhost:3001/api/tasks
|
|||
|
||||
> 📖 **Comprehensive CLI guide:** [docs/CLI-GUIDE.md](docs/CLI-GUIDE.md) — installation, every command, scripting examples, and tips.
|
||||
|
||||
Manage your entire task lifecycle with two commands.
|
||||
Handle the common start-and-complete task lifecycle with two commands.
|
||||
|
||||
```bash
|
||||
# Install globally
|
||||
|
|
@ -724,21 +740,31 @@ vk agents:pending
|
|||
# then call the completion endpoint automatically.
|
||||
```
|
||||
|
||||
### Codex + HermesAgent
|
||||
### Managed agent harnesses and external clients
|
||||
|
||||
- Follow the [Codex Integration SOP](docs/SOP-codex-integration.md) when Codex should implement, review, or delegate Veritas tasks.
|
||||
- Use the [Agent Providers guide](docs/AGENT-PROVIDERS.md) when enabling Codex,
|
||||
ACP-compatible agents, Ollama, LM Studio, provider-specific routing, or
|
||||
sandbox presets in the web app or macOS app. It also documents
|
||||
`vk acp serve --stdio` for exposing a Veritas-managed task to ACP clients.
|
||||
- Use the [Veritas Cutover Operating Guide](docs/VERITAS-CUTOVER.md) when routing work through the HermesAgent roster, enforcing QA evidence, or creating GitHub-backed task templates.
|
||||
- Configure Codex MCP access with the [MCP Server Guide](docs/mcp/README.md#codex) so Codex reads and updates Veritas through typed tools instead of one-off HTTP calls.
|
||||
- Start with the [Agent Guide and `AGENTS.md` Template](docs/AGENTS-TEMPLATE.md)
|
||||
so managed and external agents do not duplicate lifecycle callbacks or
|
||||
telemetry.
|
||||
- Use the [Agent Providers guide](docs/AGENT-PROVIDERS.md) to enable and operate
|
||||
Buzz Agent, Grok Build, Codex, Claude Code, Copilot CLI, Hermes, OpenClaw,
|
||||
ACP-compatible agents, Ollama, or LM Studio.
|
||||
- Use [Harness Compatibility](docs/HARNESS-COMPATIBILITY.md) and
|
||||
`vk doctor --json` to verify the installed runtime instead of relying on a
|
||||
provider name alone.
|
||||
- Use the [Buzz Integration guide](docs/BUZZ-INTEGRATION.md) for relay,
|
||||
community, persona/team import, ACP execution, and workflow-trigger setup.
|
||||
- Configure unmanaged client access with the
|
||||
[MCP Server Guide](docs/mcp/README.md). Managed runs receive only their
|
||||
selected run-scoped catalog and do not need a separate global VK MCP config.
|
||||
- Follow the [Codex Integration SOP](docs/SOP-codex-integration.md) or
|
||||
[Veritas Cutover Operating Guide](docs/VERITAS-CUTOVER.md) only when those
|
||||
specialized workflows apply.
|
||||
|
||||
---
|
||||
|
||||
## 🔗 MCP Server
|
||||
|
||||
Optional. The MCP server exposes 41 tools across 9 categories (tasks, agents, automation, notifications, summaries, sprints, comments, projects, and run-scoped tool control) via [Model Context Protocol](https://modelcontextprotocol.io/). Skip this for board-only use.
|
||||
Optional. The MCP server exposes 42 tools across 9 categories (tasks, agents, automation, notifications, summaries, sprints, comments, projects, and run-scoped tool control) via [Model Context Protocol](https://modelcontextprotocol.io/). Skip this for board-only use.
|
||||
|
||||
**→ [Full MCP documentation](docs/mcp/README.md)** — architecture, quickstart, tool catalog with examples, security model, read/write smoke checks, and troubleshooting.
|
||||
|
||||
|
|
@ -772,7 +798,7 @@ Verify discovery with `openclaw mcp list`. See [Troubleshooting](docs/TROUBLESHO
|
|||
**Troubleshooting MCP connection issues:**
|
||||
|
||||
- **Always restart the MCP client after MCP config changes** — MCP servers are discovered at startup
|
||||
- **Verify tools are available:** Run `openclaw mcp list` to confirm 41 Veritas Kanban tools appear
|
||||
- **Verify tools are available:** Run `openclaw mcp list` to confirm 42 Veritas Kanban tools appear
|
||||
- **When reporting issues, provide:**
|
||||
- OpenClaw version (`openclaw --version`)
|
||||
- VK version and health (`curl http://localhost:3001/api/health`)
|
||||
|
|
@ -814,7 +840,7 @@ pnpm build # Production build
|
|||
pnpm typecheck # TypeScript strict check
|
||||
pnpm lint # ESLint
|
||||
pnpm lint:budget # ESLint with current warning budget
|
||||
pnpm test # Unit tests (Vitest)
|
||||
pnpm test # Canonical unit gate (server, web, CLI, MCP)
|
||||
pnpm test:e2e # E2E tests (Playwright)
|
||||
pnpm test:load:smoke # k6 API smoke test
|
||||
pnpm validate:release # Release readiness checks
|
||||
|
|
@ -824,27 +850,27 @@ pnpm validate:release # Release readiness checks
|
|||
|
||||
## 📚 Documentation
|
||||
|
||||
| Document | Description |
|
||||
| ---------------------------------------------- | ------------------------------------------------ |
|
||||
| [Features](docs/FEATURES.md) | Complete feature reference |
|
||||
| [v6 Visual Tour](docs/V6-VISUAL-TOUR.md) | Provider, Buzz, approval, and run evidence views |
|
||||
| [API Reference](docs/API-REFERENCE.md) | Auth, endpoints, WebSocket docs |
|
||||
| [CLI Guide](docs/CLI-GUIDE.md) | Comprehensive CLI usage guide |
|
||||
| [Self-Hosting Guide](docs/guides/SELF_HOST.md) | Production deployment, reverse proxy, Docker |
|
||||
| [Deployment](docs/DEPLOYMENT.md) | Docker, bare metal, env config |
|
||||
| [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues & solutions |
|
||||
| [Contributing](CONTRIBUTING.md) | How to contribute, PR guidelines |
|
||||
| [Security Policy](SECURITY.md) | Vulnerability reporting |
|
||||
| [Code of Conduct](CODE_OF_CONDUCT.md) | Community guidelines |
|
||||
| [Changelog](CHANGELOG.md) | Release history |
|
||||
| [Sprint Docs](docs/) | Sprint planning & audit reports |
|
||||
| Document | Description |
|
||||
| ---------------------------------------------- | --------------------------------------------------- |
|
||||
| [Features](docs/FEATURES.md) | Complete feature reference |
|
||||
| [v6 Visual Tour](docs/V6-VISUAL-TOUR.md) | Provider, Buzz, approval, and run evidence views |
|
||||
| [API Reference](docs/API-REFERENCE.md) | Auth, endpoints, WebSocket docs |
|
||||
| [CLI Guide](docs/CLI-GUIDE.md) | Comprehensive CLI usage guide |
|
||||
| [Self-Hosting Guide](docs/guides/SELF_HOST.md) | Production deployment, reverse proxy, Docker |
|
||||
| [Deployment](docs/DEPLOYMENT.md) | Docker, bare metal, env config |
|
||||
| [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues and solutions |
|
||||
| [Contributing](CONTRIBUTING.md) | How to contribute and pull request guidelines |
|
||||
| [Security Policy](SECURITY.md) | Vulnerability reporting |
|
||||
| [Code of Conduct](CODE_OF_CONDUCT.md) | Community guidelines |
|
||||
| [Changelog](CHANGELOG.md) | Release history |
|
||||
| [Documentation Index](docs/) | Operator, developer, architecture, and release docs |
|
||||
|
||||
---
|
||||
|
||||
## 📸 v5 Visuals
|
||||
## 📸 Visuals
|
||||
|
||||
<details>
|
||||
<summary><strong>Click to expand v5 screenshots and GIFs</strong></summary>
|
||||
<summary><strong>Click to expand screenshots and GIFs</strong></summary>
|
||||
|
||||
These captures use release-safe dummy content against the current app surfaces. See the [v6 Visual Tour](docs/V6-VISUAL-TOUR.md) for the current release views and retained v5 shell captures.
|
||||
|
||||
|
|
@ -878,19 +904,22 @@ These captures use release-safe dummy content against the current app surfaces.
|
|||
|
||||
## 🗺️ Roadmap
|
||||
|
||||
Current planning lives in GitHub, not in a stale README checklist:
|
||||
Current work and priorities live in GitHub, not in a version-specific README checklist:
|
||||
|
||||
- [Open issues](https://github.com/BradGroux/veritas-kanban/issues)
|
||||
- [v5.0 roadmap issues](https://github.com/BradGroux/veritas-kanban/issues?q=is%3Aissue%20state%3Aopen%20label%3Arelease%3Av5.0)
|
||||
- [v5.0 SQLite schema and migration strategy](docs/SQLITE-SCHEMA.md)
|
||||
- [v5.0 SQLite migration recovery drill](docs/MIGRATION-RECOVERY.md)
|
||||
- [v5.0 desktop architecture decision](docs/architecture/ADR-0001-v5-desktop-architecture.md)
|
||||
- [post-GA desktop agent workbench spec](docs/DESKTOP-AGENT-WORKBENCH.md)
|
||||
- [Release history](CHANGELOG.md)
|
||||
- [GitHub releases](https://github.com/BradGroux/veritas-kanban/releases)
|
||||
|
||||
Longer-lived product and architecture direction is recorded separately:
|
||||
|
||||
- [v6 agent runtime control plane](docs/architecture/V6-AGENT-RUNTIME-CONTROL-PLANE.md)
|
||||
- [phase capability profiles](docs/architecture/PHASE-CAPABILITY-PROFILES.md)
|
||||
- [tool control plane v1](docs/architecture/TOOL-CONTROL-PLANE-V1.md)
|
||||
- [post-GA desktop agent workbench](docs/DESKTOP-AGENT-WORKBENCH.md)
|
||||
- [post-GA native mobile offline decision](docs/architecture/ADR-0003-post-ga-native-mobile-offline.md)
|
||||
- [post-GA cloud sync and hosted SaaS decision](docs/architecture/ADR-0004-post-ga-cloud-sync-hosted-saas.md)
|
||||
- [Release history](CHANGELOG.md)
|
||||
|
||||
Use issues for current work and the changelog for shipped work.
|
||||
Use issues for current work, architecture records for durable direction, and the changelog and releases for shipped work.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
44
SECURITY.md
44
SECURITY.md
|
|
@ -53,6 +53,50 @@ GitHub secret scanning and push protection should remain enabled for the
|
|||
repository. The tracked-file guard complements those services because generic
|
||||
password and recovery-key hashes may not match provider-specific signatures.
|
||||
|
||||
## CI Supply Chain Integrity
|
||||
|
||||
Every external GitHub Action and reusable workflow reference must use a full
|
||||
40-character commit SHA followed by a readable release comment. Local actions
|
||||
under `./.github/actions/` are reviewed with the repository and do not need a
|
||||
remote revision. Docker actions must use a complete SHA-256 image digest.
|
||||
|
||||
The same policy is enforced locally and in CI:
|
||||
|
||||
```bash
|
||||
pnpm check:actions-pinned
|
||||
```
|
||||
|
||||
Dependabot retains the `github-actions` ecosystem entry so reviewed updates can
|
||||
advance both the immutable commit and its release comment.
|
||||
|
||||
## Continuous Security Gates
|
||||
|
||||
The `Security Gates` workflow runs CodeQL and gitleaks for pull requests, main
|
||||
branch updates, and a weekly schedule. CodeQL uses the extended JavaScript and
|
||||
TypeScript security query suite. Repository merge protection blocks CodeQL
|
||||
errors and high-or-critical security alerts. Gitleaks scans the current tree,
|
||||
accepts only the exact reviewed fingerprints in `.gitleaksignore`, and proves
|
||||
that a newly introduced synthetic secret is still rejected.
|
||||
|
||||
Brad Groux owns Dependabot and GitHub security alert triage. New dependency,
|
||||
code-scanning, or secret-scanning alerts must be reviewed privately within two
|
||||
working days. Confirm exploitability and affected releases before opening a
|
||||
public issue. Track confirmed vulnerabilities in a private GitHub security
|
||||
advisory, prioritize critical and high findings for the next safe patch, and
|
||||
record false positives at the narrowest available fingerprint or path. Do not
|
||||
disable a detector class to clear a gate.
|
||||
|
||||
Dependabot vulnerability alerts and security updates, GitHub secret scanning,
|
||||
and push protection must remain enabled. Security-update pull requests use the
|
||||
existing `BradGroux` reviewer assignment in `.github/dependabot.yml`.
|
||||
|
||||
Run the repository controls locally with:
|
||||
|
||||
```bash
|
||||
pnpm check:security-gates
|
||||
pnpm check:gitleaks
|
||||
```
|
||||
|
||||
## Scope
|
||||
|
||||
This policy applies to:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@veritas-kanban/cli",
|
||||
"version": "6.0.0",
|
||||
"version": "6.1.2",
|
||||
"description": "CLI for Veritas Kanban task management",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
@ -9,17 +9,18 @@
|
|||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsx src/index.ts",
|
||||
"test": "vitest run --maxWorkers=4",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@veritas-kanban/shared": "workspace:*",
|
||||
"commander": "^15.0.0",
|
||||
"chalk": "^5.3.0"
|
||||
"chalk": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.1.1",
|
||||
"@types/node": "^26.2.0",
|
||||
"typescript": "^6.0.3",
|
||||
"tsx": "^4.23.1"
|
||||
"tsx": "^4.23.12"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Brad Groux <brad@digitalmeld.io>",
|
||||
|
|
|
|||
438
cli/src/__tests__/admission.test.ts
Normal file
438
cli/src/__tests__/admission.test.ts
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
import { Command } from 'commander';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const api = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('../utils/api.js', () => ({ api }));
|
||||
|
||||
import { registerAdmissionCommands } from '../commands/admission.js';
|
||||
|
||||
describe('vk admission commands', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
process.exitCode = 0;
|
||||
});
|
||||
|
||||
it('lists reservations as JSON with all operator filters preserved', async () => {
|
||||
api.mockResolvedValue({
|
||||
generatedAt: '2026-07-25T10:00:00.000Z',
|
||||
reservations: [{ id: 'admission_1', state: 'active' }],
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'list',
|
||||
'--workspace',
|
||||
'workspace-a',
|
||||
'--workflow-run',
|
||||
'run_1234567890_abcdef',
|
||||
'--workflow-step',
|
||||
'execute',
|
||||
'--root-reservation',
|
||||
'admission_root',
|
||||
'--root-objective',
|
||||
'objective-a',
|
||||
'--node',
|
||||
'node-child',
|
||||
'--parent-node',
|
||||
'node-root',
|
||||
'--state',
|
||||
'active',
|
||||
'released',
|
||||
'--limit',
|
||||
'25',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(
|
||||
'/api/admission?workspaceId=workspace-a&workflowRunId=run_1234567890_abcdef&workflowStepId=execute&rootReservationId=admission_root&rootObjectiveId=objective-a&nodeId=node-child&parentNodeId=node-root&state=active&state=released&limit=25'
|
||||
);
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toEqual({
|
||||
generatedAt: '2026-07-25T10:00:00.000Z',
|
||||
reservations: [{ id: 'admission_1', state: 'active' }],
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('inspects one reservation as JSON', async () => {
|
||||
api.mockResolvedValue({ id: 'admission_1', state: 'released' });
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync(['node', 'vk', 'admission', 'get', 'admission_1', '--json']);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/admission/admission_1');
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toEqual({
|
||||
id: 'admission_1',
|
||||
state: 'released',
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('inspects an aggregate execution tree as JSON', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'execution-tree-budget-summary/v1',
|
||||
rootObjectiveId: 'objective-a',
|
||||
contributors: [],
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'tree',
|
||||
'objective-a',
|
||||
'--limit',
|
||||
'25',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/admission/tree/objective-a?limit=25');
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
rootObjectiveId: 'objective-a',
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('shows durable execution-tree control in human output', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'execution-tree-budget-summary/v1',
|
||||
rootObjectiveId: 'objective-a',
|
||||
control: {
|
||||
schemaVersion: 'execution-tree-control/v1',
|
||||
rootObjectiveId: 'objective-a',
|
||||
state: 'cancelled',
|
||||
trigger: 'operator',
|
||||
reason: 'Operator stopped runaway expansion.',
|
||||
idempotencyKey: 'sha256:cancelled',
|
||||
recordedAt: '2026-07-25T12:00:00.000Z',
|
||||
},
|
||||
committed: {
|
||||
totalTokens: 0,
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
toolCalls: 0,
|
||||
runtimeSeconds: 0,
|
||||
idleRuntimeSeconds: 0,
|
||||
costUsd: 0,
|
||||
retries: 0,
|
||||
fanOut: 0,
|
||||
},
|
||||
reserved: {
|
||||
totalTokens: 0,
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
toolCalls: 0,
|
||||
runtimeSeconds: 0,
|
||||
idleRuntimeSeconds: 0,
|
||||
costUsd: 0,
|
||||
retries: 0,
|
||||
fanOut: 0,
|
||||
},
|
||||
policies: [],
|
||||
contributors: [],
|
||||
contributorCount: 0,
|
||||
truncated: false,
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync(['node', 'vk', 'admission', 'tree', 'objective-a']);
|
||||
|
||||
expect(output.mock.calls.map(([line]) => String(line)).join('\n')).toContain(
|
||||
'control=cancelled trigger=operator'
|
||||
);
|
||||
expect(output.mock.calls.map(([line]) => String(line)).join('\n')).toContain(
|
||||
'Operator stopped runaway expansion.'
|
||||
);
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('cancels one queued launch with a stable idempotency identity', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'execution-tree-cancellation/v1',
|
||||
scope: 'queued-launch',
|
||||
queueEntry: { id: 'admission_queue_1', state: 'terminal' },
|
||||
reservationReleased: true,
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'queue',
|
||||
'cancel',
|
||||
'admission_queue_1',
|
||||
'--reason',
|
||||
'Operator cancelled the queued launch.',
|
||||
'--idempotency-key',
|
||||
'cancel-queue-entry-123',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/admission/queue/admission_queue_1/cancel', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
reason: 'Operator cancelled the queued launch.',
|
||||
idempotencyKey: 'cancel-queue-entry-123',
|
||||
}),
|
||||
});
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
scope: 'queued-launch',
|
||||
queueEntry: { state: 'terminal' },
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('cancels an execution tree and reports remaining verified runs', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'execution-tree-cancellation/v1',
|
||||
scope: 'execution-tree',
|
||||
rootObjectiveId: 'objective-a',
|
||||
queueEntriesCancelled: 2,
|
||||
interruptedAttempts: 1,
|
||||
runningAttempts: [],
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'cancel-tree',
|
||||
'objective-a',
|
||||
'--reason',
|
||||
'Operator cancelled runaway expansion.',
|
||||
'--idempotency-key',
|
||||
'cancel-execution-tree-123',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/admission/tree/objective-a/cancel', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
reason: 'Operator cancelled runaway expansion.',
|
||||
idempotencyKey: 'cancel-execution-tree-123',
|
||||
}),
|
||||
});
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
scope: 'execution-tree',
|
||||
queueEntriesCancelled: 2,
|
||||
interruptedAttempts: 1,
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('resumes an eligible execution tree with a stable idempotency identity', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'execution-tree-control/v1',
|
||||
rootObjectiveId: 'objective-a',
|
||||
state: 'resumed',
|
||||
resumedAt: '2026-07-25T12:00:00.000Z',
|
||||
resumeReason: 'Operator confirmed pressure cleared.',
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'resume-tree',
|
||||
'objective-a',
|
||||
'--reason',
|
||||
'Operator confirmed pressure cleared.',
|
||||
'--idempotency-key',
|
||||
'resume-execution-tree-123',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/admission/tree/objective-a/resume', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
reason: 'Operator confirmed pressure cleared.',
|
||||
idempotencyKey: 'resume-execution-tree-123',
|
||||
}),
|
||||
});
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
state: 'resumed',
|
||||
rootObjectiveId: 'objective-a',
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('lists the admission queue as JSON with all operator filters preserved', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'admission-queue-list/v1',
|
||||
generatedAt: '2026-07-25T12:00:00.000Z',
|
||||
conditional: true,
|
||||
depth: {
|
||||
global: { current: 2, limit: 1_000 },
|
||||
workspaces: [],
|
||||
},
|
||||
pagination: { page: 2, limit: 25, total: 26, hasMore: false },
|
||||
entries: [{ id: 'admission_queue_1', state: 'queued', position: 26 }],
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'queue',
|
||||
'list',
|
||||
'--workspace',
|
||||
'workspace-a',
|
||||
'--root-objective',
|
||||
'objective-a',
|
||||
'--node',
|
||||
'node-a',
|
||||
'--source',
|
||||
'workflow',
|
||||
'--state',
|
||||
'queued',
|
||||
'requeued',
|
||||
'--priority',
|
||||
'3',
|
||||
'--limiting-scope',
|
||||
'provider',
|
||||
'--min-age',
|
||||
'60000',
|
||||
'--max-age',
|
||||
'3600000',
|
||||
'--page',
|
||||
'2',
|
||||
'--limit',
|
||||
'25',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(
|
||||
'/api/admission/queue?workspaceId=workspace-a&rootObjectiveId=objective-a&nodeId=node-a&source=workflow&state=queued&state=requeued&priority=3&limitingScope=provider&minAgeMs=60000&maxAgeMs=3600000&page=2&limit=25'
|
||||
);
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
schemaVersion: 'admission-queue-list/v1',
|
||||
conditional: true,
|
||||
entries: [{ id: 'admission_queue_1', position: 26 }],
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('inspects one admission queue entry as JSON', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'admission-queue-inspection/v1',
|
||||
generatedAt: '2026-07-25T12:00:00.000Z',
|
||||
conditional: true,
|
||||
depth: {
|
||||
global: { current: 1, limit: 1_000 },
|
||||
workspaces: [],
|
||||
},
|
||||
entry: {
|
||||
schemaVersion: 'admission-queue-inspection/v1',
|
||||
id: 'admission_queue_1',
|
||||
state: 'leased',
|
||||
readiness: 'reserved',
|
||||
},
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'admission',
|
||||
'queue',
|
||||
'get',
|
||||
'admission_queue_1',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/admission/queue/admission_queue_1');
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
schemaVersion: 'admission-queue-inspection/v1',
|
||||
entry: { id: 'admission_queue_1', readiness: 'reserved' },
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('prints compact conditional queue output without an exact start promise', async () => {
|
||||
api.mockResolvedValue({
|
||||
schemaVersion: 'admission-queue-list/v1',
|
||||
generatedAt: '2026-07-25T12:00:00.000Z',
|
||||
conditional: true,
|
||||
depth: {
|
||||
global: { current: 1, limit: 1_000 },
|
||||
workspaces: [],
|
||||
},
|
||||
pagination: {
|
||||
page: 1,
|
||||
limit: 1,
|
||||
total: 1,
|
||||
hasMore: false,
|
||||
snapshotTruncated: false,
|
||||
},
|
||||
entries: [
|
||||
{
|
||||
schemaVersion: 'admission-queue-inspection/v1',
|
||||
id: 'admission_queue_1',
|
||||
state: 'queued',
|
||||
position: 1,
|
||||
rawPriority: 1,
|
||||
effectivePriority: 2,
|
||||
agePromotion: 1,
|
||||
ageMs: 60_000,
|
||||
readiness: 'conditional',
|
||||
lease: { posture: 'none' },
|
||||
limitingPolicies: [],
|
||||
conditionalStartFactors: ['capacity-recheck'],
|
||||
launch: {
|
||||
source: 'direct',
|
||||
target: 'direct',
|
||||
taskKey: `sha256:${'a'.repeat(64)}`,
|
||||
rootTaskKey: `sha256:${'b'.repeat(64)}`,
|
||||
workspaceKey: `sha256:${'c'.repeat(64)}`,
|
||||
provider: 'codex-cli',
|
||||
hostKey: `sha256:${'d'.repeat(64)}`,
|
||||
},
|
||||
retry: {
|
||||
count: 0,
|
||||
maximum: 3,
|
||||
availableAt: '2026-07-25T12:00:00.000Z',
|
||||
},
|
||||
createdAt: '2026-07-25T11:59:00.000Z',
|
||||
updatedAt: '2026-07-25T11:59:00.000Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerAdmissionCommands(program);
|
||||
|
||||
await program.parseAsync(['node', 'vk', 'admission', 'queue', 'list', '--limit', '1']);
|
||||
|
||||
const rendered = output.mock.calls.map(([line]) => String(line)).join('\n');
|
||||
expect(rendered).toContain('priority=1->2 readiness=conditional');
|
||||
expect(rendered).toContain('Conditional snapshot at 2026-07-25T12:00:00.000Z');
|
||||
expect(rendered).not.toMatch(/\bETA\b|starts? at|start time/i);
|
||||
output.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { Command } from 'commander';
|
||||
|
||||
const { mockApi, mockFindTask } = vi.hoisted(() => ({
|
||||
|
|
@ -11,6 +14,23 @@ vi.mock('../utils/find.js', () => ({ findTask: mockFindTask }));
|
|||
|
||||
import { registerAgentCommands } from '../commands/agents.js';
|
||||
|
||||
const temporaryRoots: string[] = [];
|
||||
|
||||
function expectLaunchBody(expected: Record<string, unknown>): void {
|
||||
const [url, request] = mockApi.mock.calls.at(-1) as [string, { method: string; body: string }];
|
||||
const { idempotencyKey, ...body } = JSON.parse(request.body) as Record<string, unknown>;
|
||||
expect(url).toBe('/api/agents/task_1/start');
|
||||
expect(request.method).toBe('POST');
|
||||
expect(idempotencyKey).toMatch(/^vk-cli:task_1:[0-9a-f-]{36}$/);
|
||||
expect(body).toEqual(expected);
|
||||
}
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
temporaryRoots.splice(0).map((root) => fs.rm(root, { recursive: true, force: true }))
|
||||
);
|
||||
});
|
||||
|
||||
describe('vk agent runtime capability controls', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
|
@ -38,6 +58,10 @@ describe('vk agent runtime capability controls', () => {
|
|||
'task_1',
|
||||
'--agent',
|
||||
'codex',
|
||||
'--phase',
|
||||
'implement',
|
||||
'--parent-attempt',
|
||||
'attempt_parent',
|
||||
'--require-capability',
|
||||
'tool.mcp',
|
||||
'output.structured',
|
||||
|
|
@ -46,12 +70,43 @@ describe('vk agent runtime capability controls', () => {
|
|||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/start', {
|
||||
expectLaunchBody({
|
||||
agent: 'codex',
|
||||
phase: 'implement',
|
||||
requiredRuntimeCapabilities: ['tool.mcp', 'output.structured'],
|
||||
parentAttemptId: 'attempt_parent',
|
||||
});
|
||||
});
|
||||
|
||||
it('previews one explicit phase against exact parent launch evidence', async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
'launch-preview',
|
||||
'task_1',
|
||||
'--agent',
|
||||
'codex',
|
||||
'--phase',
|
||||
'plan',
|
||||
'--parent-attempt',
|
||||
'attempt_parent',
|
||||
'--json',
|
||||
],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/launch-preview', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
agent: 'codex',
|
||||
profileId: undefined,
|
||||
requiredRuntimeCapabilities: ['tool.mcp', 'output.structured'],
|
||||
phase: 'plan',
|
||||
requiredRuntimeCapabilities: undefined,
|
||||
commitPolicy: undefined,
|
||||
parentAttemptId: 'attempt_parent',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
|
@ -66,14 +121,9 @@ describe('vk agent runtime capability controls', () => {
|
|||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/start', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
agent: 'codex',
|
||||
profileId: undefined,
|
||||
requiredRuntimeCapabilities: undefined,
|
||||
commitPolicy: 'forbidden',
|
||||
}),
|
||||
expectLaunchBody({
|
||||
agent: 'codex',
|
||||
commitPolicy: 'forbidden',
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -119,6 +169,181 @@ describe('vk agent runtime capability controls', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('binds recovery cancellation to the exact persisted parent attempt', async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
['agent:cancel-recovery', 'task_1', '--attempt', 'attempt_parent', '--json'],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/recovery/cancel', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ attemptId: 'attempt_parent' }),
|
||||
});
|
||||
});
|
||||
|
||||
it('reads durable phase state for one exact attempt', async () => {
|
||||
mockApi.mockResolvedValueOnce({ current: null, history: [] });
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
['agent:phase', 'task_1', '--attempt', 'attempt_1', '--limit', '25', '--json'],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/phase?attemptId=attempt_1&limit=25');
|
||||
});
|
||||
|
||||
it('binds the first phase transition to exact evidence and manifest provenance', async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'vk-phase-cli-'));
|
||||
temporaryRoots.push(root);
|
||||
const fromPath = path.join(root, 'from.json');
|
||||
const targetPath = path.join(root, 'target.json');
|
||||
const fromEvidence = { digest: `sha256:${'1'.repeat(64)}` };
|
||||
const targetEvidence = { digest: `sha256:${'2'.repeat(64)}` };
|
||||
await fs.writeFile(fromPath, JSON.stringify(fromEvidence));
|
||||
await fs.writeFile(targetPath, JSON.stringify(targetEvidence));
|
||||
mockApi.mockResolvedValueOnce({ current: null, history: [] }).mockResolvedValueOnce({
|
||||
status: 'applied',
|
||||
current: null,
|
||||
targetEvidenceDigest: targetEvidence.digest,
|
||||
});
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
'agent:transition-phase',
|
||||
'task_1',
|
||||
'--attempt',
|
||||
'attempt_1',
|
||||
'--operation',
|
||||
'phase-op-1',
|
||||
'--from-evidence',
|
||||
fromPath,
|
||||
'--target-evidence',
|
||||
targetPath,
|
||||
'--manifest',
|
||||
`sha256:${'3'.repeat(64)}`,
|
||||
'--reason',
|
||||
'Approved plan is ready.',
|
||||
'--json',
|
||||
],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenNthCalledWith(2, '/api/agents/task_1/phase/transitions', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
attemptId: 'attempt_1',
|
||||
operationId: 'phase-op-1',
|
||||
expectedSequence: 0,
|
||||
expectedPhaseEvidenceDigest: fromEvidence.digest,
|
||||
expectedManifestDigest: `sha256:${'3'.repeat(64)}`,
|
||||
reason: 'Approved plan is ready.',
|
||||
fromEvidence,
|
||||
targetEvidence,
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects partially numeric phase approval lifetimes before transition', async () => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'vk-phase-cli-'));
|
||||
temporaryRoots.push(root);
|
||||
const fromPath = path.join(root, 'from.json');
|
||||
const targetPath = path.join(root, 'target.json');
|
||||
await fs.writeFile(fromPath, JSON.stringify({ digest: `sha256:${'1'.repeat(64)}` }));
|
||||
await fs.writeFile(targetPath, JSON.stringify({ digest: `sha256:${'2'.repeat(64)}` }));
|
||||
mockApi.mockResolvedValueOnce({ current: null, history: [] });
|
||||
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {
|
||||
throw new Error('process.exit called');
|
||||
}) as typeof process.exit);
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
try {
|
||||
await expect(
|
||||
program.parseAsync(
|
||||
[
|
||||
'agent:transition-phase',
|
||||
'task_1',
|
||||
'--attempt',
|
||||
'attempt_1',
|
||||
'--operation',
|
||||
'phase-op-1',
|
||||
'--from-evidence',
|
||||
fromPath,
|
||||
'--target-evidence',
|
||||
targetPath,
|
||||
'--manifest',
|
||||
`sha256:${'3'.repeat(64)}`,
|
||||
'--reason',
|
||||
'Approved plan is ready.',
|
||||
'--approval-ttl-ms',
|
||||
'1000x',
|
||||
],
|
||||
{ from: 'user' }
|
||||
)
|
||||
).rejects.toThrow('process.exit called');
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('--approval-ttl-ms must be an integer')
|
||||
);
|
||||
expect(mockApi).toHaveBeenCalledTimes(1);
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
} finally {
|
||||
errorSpy.mockRestore();
|
||||
exitSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('decides an exact phase approval with revision and action-hash guards', async () => {
|
||||
const approval = {
|
||||
id: 'runapproval_000000000001',
|
||||
revision: 4,
|
||||
actionHash: 'a'.repeat(64),
|
||||
status: 'pending',
|
||||
};
|
||||
mockApi.mockResolvedValueOnce(approval).mockResolvedValueOnce({
|
||||
...approval,
|
||||
revision: 5,
|
||||
status: 'approved',
|
||||
});
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
'agent:decide-phase-approval',
|
||||
approval.id,
|
||||
'--decision',
|
||||
'approve',
|
||||
'--note',
|
||||
'Expansion reviewed.',
|
||||
'--json',
|
||||
],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenNthCalledWith(2, `/api/run-approvals/${approval.id}/decision`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
decision: 'approved',
|
||||
expectedRevision: 4,
|
||||
expectedActionHash: approval.actionHash,
|
||||
note: 'Expansion reviewed.',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('starts a native history fork from an explicit source attempt and turn', async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
|
|
@ -134,6 +359,8 @@ describe('vk agent runtime capability controls', () => {
|
|||
'Explore the alternate fix',
|
||||
'--fork-turn',
|
||||
'turn_7',
|
||||
'--phase',
|
||||
'explore',
|
||||
'--require-capability',
|
||||
'tool.mcp',
|
||||
'--json',
|
||||
|
|
@ -141,14 +368,20 @@ describe('vk agent runtime capability controls', () => {
|
|||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/conversation/fork', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
sourceAttemptId: 'attempt_parent',
|
||||
message: 'Explore the alternate fix',
|
||||
forkTurnId: 'turn_7',
|
||||
requiredRuntimeCapabilities: ['tool.mcp'],
|
||||
}),
|
||||
const [url, request] = mockApi.mock.calls.at(-1) as [string, { method: string; body: string }];
|
||||
const { idempotencyKey, ...body } = JSON.parse(request.body) as Record<string, unknown>;
|
||||
|
||||
expect(url).toBe('/api/agents/task_1/conversation/fork');
|
||||
expect(request.method).toBe('POST');
|
||||
expect(idempotencyKey).toMatch(
|
||||
/^vk-cli:task_1:conversation:fork:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
|
||||
);
|
||||
expect(body).toEqual({
|
||||
sourceAttemptId: 'attempt_parent',
|
||||
message: 'Explore the alternate fix',
|
||||
forkTurnId: 'turn_7',
|
||||
phase: 'explore',
|
||||
requiredRuntimeCapabilities: ['tool.mcp'],
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -198,4 +431,83 @@ describe('vk agent runtime capability controls', () => {
|
|||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it('scans the exact task workspace execution inventory', async () => {
|
||||
mockApi.mockResolvedValueOnce({
|
||||
inventory: {
|
||||
identity: { digest: `sha256:${'1'.repeat(64)}` },
|
||||
digest: `sha256:${'2'.repeat(64)}`,
|
||||
projectPolicy: { maximumTrust: 'restricted' },
|
||||
entries: [],
|
||||
},
|
||||
});
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(['workspace-trust', 'scan', 'task_1', '--json'], {
|
||||
from: 'user',
|
||||
});
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/workspace-trust');
|
||||
});
|
||||
|
||||
it('records and revokes exact-inventory workspace decisions', async () => {
|
||||
mockApi.mockResolvedValue({
|
||||
id: 'workspace-decision-1',
|
||||
mode: 'trusted',
|
||||
});
|
||||
const digest = `sha256:${'3'.repeat(64)}`;
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
'workspace-trust',
|
||||
'decide',
|
||||
'task_1',
|
||||
'--mode',
|
||||
'trusted',
|
||||
'--inventory',
|
||||
digest,
|
||||
'--reason',
|
||||
'Reviewed exact inventory',
|
||||
'--json',
|
||||
],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/workspace-trust/decisions', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
mode: 'trusted',
|
||||
inventoryDigest: digest,
|
||||
reason: 'Reviewed exact inventory',
|
||||
expiresAt: undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
'workspace-trust',
|
||||
'revoke',
|
||||
'task_1',
|
||||
'--inventory',
|
||||
digest,
|
||||
'--reason',
|
||||
'Authorization withdrawn',
|
||||
'--json',
|
||||
],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/workspace-trust/revoke', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
inventoryDigest: digest,
|
||||
reason: 'Authorization withdrawn',
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
214
cli/src/__tests__/goals.test.ts
Normal file
214
cli/src/__tests__/goals.test.ts
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
import { Command } from 'commander';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const api = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock('../utils/api.js', () => ({ api }));
|
||||
|
||||
import { registerGoalCommands } from '../commands/goals.js';
|
||||
|
||||
const GOAL_ID = 'goal_0123456789abcdef';
|
||||
|
||||
describe('vk goals commands', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
process.exitCode = 0;
|
||||
});
|
||||
|
||||
it('lists goals as JSON with bounded filters', async () => {
|
||||
api.mockResolvedValue({
|
||||
generatedAt: '2026-07-26T02:00:00.000Z',
|
||||
goals: [{ id: GOAL_ID, state: 'blocked' }],
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerGoalCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'goals',
|
||||
'list',
|
||||
'--state',
|
||||
'active',
|
||||
'blocked',
|
||||
'--root-task',
|
||||
'task-865',
|
||||
'--limit',
|
||||
'25',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(
|
||||
'/api/goals?state=active&state=blocked&rootTaskId=task-865&limit=25'
|
||||
);
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
goals: [{ id: GOAL_ID, state: 'blocked' }],
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('creates an evidence-gated task goal', async () => {
|
||||
api.mockResolvedValue({ id: GOAL_ID, state: 'active', revision: 1 });
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerGoalCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'goals',
|
||||
'create',
|
||||
'--objective',
|
||||
'Deliver durable controls.',
|
||||
'--acceptance',
|
||||
'REST passes',
|
||||
'CLI passes',
|
||||
'--requirement',
|
||||
'focused-tests|test|Focused tests pass.',
|
||||
'--root-task',
|
||||
'task-865',
|
||||
'--mode',
|
||||
'automatic',
|
||||
'--max-turns',
|
||||
'20',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith('/api/goals', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
objective: 'Deliver durable controls.',
|
||||
constraints: [],
|
||||
acceptanceCriteria: ['REST passes', 'CLI passes'],
|
||||
root: { kind: 'task', taskId: 'task-865' },
|
||||
continuation: { mode: 'automatic', maxTurns: 20 },
|
||||
completionRequirements: [
|
||||
{
|
||||
id: 'focused-tests',
|
||||
verificationKind: 'test',
|
||||
description: 'Focused tests pass.',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('transitions with exact revision and structured completion evidence', async () => {
|
||||
api.mockResolvedValue({ id: GOAL_ID, state: 'complete', revision: 3 });
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerGoalCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'goals',
|
||||
'transition',
|
||||
GOAL_ID,
|
||||
'--revision',
|
||||
'2',
|
||||
'--state',
|
||||
'complete',
|
||||
'--reason',
|
||||
'All verification passed.',
|
||||
'--evidence-json',
|
||||
'[{"requirementId":"focused-tests","evidenceId":"ci-1082","summary":"Passed."}]',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(`/api/goals/${GOAL_ID}/transition`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
expectedRevision: 2,
|
||||
state: 'complete',
|
||||
reason: 'All verification passed.',
|
||||
blocker: undefined,
|
||||
completionEvidence: [
|
||||
{
|
||||
requirementId: 'focused-tests',
|
||||
evidenceId: 'ci-1082',
|
||||
summary: 'Passed.',
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('links a run to the continuation chain', async () => {
|
||||
api.mockResolvedValue({ id: GOAL_ID, state: 'active', revision: 4 });
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerGoalCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'goals',
|
||||
'link-run',
|
||||
GOAL_ID,
|
||||
'--revision',
|
||||
'3',
|
||||
'--task',
|
||||
'task-865',
|
||||
'--attempt',
|
||||
'attempt-3',
|
||||
'--conversation',
|
||||
'conversation-3',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(`/api/goals/${GOAL_ID}/runs`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
expectedRevision: 3,
|
||||
taskId: 'task-865',
|
||||
attemptId: 'attempt-3',
|
||||
conversationId: 'conversation-3',
|
||||
}),
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
|
||||
it('approves and dispatches a bounded conversation rollover', async () => {
|
||||
api.mockResolvedValue({
|
||||
action: 'dispatched',
|
||||
goal: { id: GOAL_ID, revision: 8 },
|
||||
continuation: {
|
||||
id: 'continuation-rollover',
|
||||
kind: 'rollover',
|
||||
state: 'dispatched',
|
||||
resultAttemptId: 'attempt-8',
|
||||
},
|
||||
});
|
||||
const output = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
const program = new Command().exitOverride();
|
||||
registerGoalCommands(program);
|
||||
|
||||
await program.parseAsync([
|
||||
'node',
|
||||
'vk',
|
||||
'goals',
|
||||
'rollover',
|
||||
GOAL_ID,
|
||||
'--revision',
|
||||
'7',
|
||||
'--json',
|
||||
]);
|
||||
|
||||
expect(api).toHaveBeenCalledWith(`/api/goals/${GOAL_ID}/rollover`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
expectedRevision: 7,
|
||||
}),
|
||||
});
|
||||
expect(JSON.parse(String(output.mock.calls[0][0]))).toMatchObject({
|
||||
action: 'dispatched',
|
||||
continuation: { kind: 'rollover', resultAttemptId: 'attempt-8' },
|
||||
});
|
||||
output.mockRestore();
|
||||
});
|
||||
});
|
||||
409
cli/src/commands/admission.ts
Normal file
409
cli/src/commands/admission.ts
Normal file
|
|
@ -0,0 +1,409 @@
|
|||
import { Command } from 'commander';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import chalk from 'chalk';
|
||||
import type {
|
||||
AdmissionExecutionTreeCancellationResult,
|
||||
AdmissionLaunchSource,
|
||||
AdmissionQueueGetResponse,
|
||||
AdmissionQueueInspectionEntry,
|
||||
AdmissionQueueListResponse,
|
||||
AdmissionQueueState,
|
||||
AdmissionQueuedCancellationResult,
|
||||
AdmissionReservation,
|
||||
AdmissionReservationState,
|
||||
AdmissionScope,
|
||||
ExecutionTreeBudgetSummary,
|
||||
ExecutionTreeControl,
|
||||
} from '@veritas-kanban/shared';
|
||||
import { api } from '../utils/api.js';
|
||||
|
||||
interface AdmissionListResponse {
|
||||
generatedAt: string;
|
||||
reservations: AdmissionReservation[];
|
||||
}
|
||||
|
||||
export function registerAdmissionCommands(program: Command): void {
|
||||
const admission = program
|
||||
.command('admission')
|
||||
.description('Inspect durable execution admission reservations');
|
||||
|
||||
const queue = admission.command('queue').description('Inspect the durable admission queue');
|
||||
|
||||
queue
|
||||
.command('list')
|
||||
.description('List queued, leased, dispatched, or terminal admission entries')
|
||||
.option('--workspace <id>', 'Filter by workspace')
|
||||
.option('--root-objective <id>', 'Filter by execution-tree root objective')
|
||||
.option('--node <id>', 'Filter by execution-tree node')
|
||||
.option('--source <sources...>', 'Filter by launch source')
|
||||
.option('--state <states...>', 'Filter by queue state')
|
||||
.option('--priority <level>', 'Filter by raw numeric priority')
|
||||
.option('--limiting-scope <scopes...>', 'Filter by limiting scope')
|
||||
.option('--min-age <milliseconds>', 'Minimum queue age in milliseconds')
|
||||
.option('--max-age <milliseconds>', 'Maximum queue age in milliseconds')
|
||||
.option('--page <number>', 'Result page', '1')
|
||||
.option('--limit <count>', 'Maximum entries per page', '100')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
const query = new URLSearchParams();
|
||||
if (options.workspace) query.set('workspaceId', options.workspace);
|
||||
if (options.rootObjective) query.set('rootObjectiveId', options.rootObjective);
|
||||
if (options.node) query.set('nodeId', options.node);
|
||||
for (const source of (options.source ?? []) as AdmissionLaunchSource[]) {
|
||||
query.append('source', source);
|
||||
}
|
||||
for (const state of (options.state ?? []) as AdmissionQueueState[]) {
|
||||
query.append('state', state);
|
||||
}
|
||||
if (options.priority) query.set('priority', options.priority);
|
||||
for (const scope of (options.limitingScope ?? []) as AdmissionScope[]) {
|
||||
query.append('limitingScope', scope);
|
||||
}
|
||||
if (options.minAge) query.set('minAgeMs', options.minAge);
|
||||
if (options.maxAge) query.set('maxAgeMs', options.maxAge);
|
||||
query.set('page', options.page);
|
||||
query.set('limit', options.limit);
|
||||
const result = await api<AdmissionQueueListResponse>(
|
||||
`/api/admission/queue?${query.toString()}`
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
if (result.entries.length === 0) {
|
||||
console.log(chalk.dim('No admission queue entries matched.'));
|
||||
return;
|
||||
}
|
||||
for (const entry of result.entries) printQueueEntry(entry);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
`Conditional snapshot at ${result.generatedAt}; ${result.depth.global.current}/${result.depth.global.limit} global queue slots used.`
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
queue
|
||||
.command('cancel <id>')
|
||||
.description('Cancel one queued launch before provider dispatch')
|
||||
.requiredOption('--reason <text>', 'Operator reason for cancellation')
|
||||
.option('--idempotency-key <key>', 'Stable identity for safe retries')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const result = await api<AdmissionQueuedCancellationResult>(
|
||||
`/api/admission/queue/${encodeURIComponent(id)}/cancel`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
reason: options.reason,
|
||||
idempotencyKey: options.idempotencyKey ?? `vk-cli:queue-cancel:${id}:${randomUUID()}`,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green(`✓ Cancelled queued launch ${result.queueEntry.id}`));
|
||||
console.log(
|
||||
chalk.dim(
|
||||
`State: ${result.queueEntry.state}; reservation released: ${result.reservationReleased}`
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
queue
|
||||
.command('get <id>')
|
||||
.description('Inspect one admission queue entry')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const result = await api<AdmissionQueueGetResponse>(
|
||||
`/api/admission/queue/${encodeURIComponent(id)}`
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
printQueueEntry(result.entry, true);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
`Conditional snapshot at ${result.generatedAt}; capacity, policy, arrivals, and leases may change position.`
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
admission
|
||||
.command('list')
|
||||
.description('List active or recently terminal admission reservations')
|
||||
.option('--workspace <id>', 'Filter by workspace')
|
||||
.option('--task <id>', 'Filter by task')
|
||||
.option('--root-task <id>', 'Filter by root task')
|
||||
.option('--provider <provider>', 'Filter by provider')
|
||||
.option('--host <id>', 'Filter by launch host')
|
||||
.option('--workflow-run <id>', 'Filter by workflow run')
|
||||
.option('--workflow-step <id>', 'Filter by workflow step')
|
||||
.option('--root-reservation <id>', 'Filter by workflow root reservation')
|
||||
.option('--root-objective <id>', 'Filter by execution-tree root objective')
|
||||
.option('--node <id>', 'Filter by execution-tree node')
|
||||
.option('--parent-node <id>', 'Filter by execution-tree parent node')
|
||||
.option('--state <states...>', 'Filter by state (active, released, expired)')
|
||||
.option('--limit <count>', 'Maximum records', '100')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
const query = new URLSearchParams();
|
||||
if (options.workspace) query.set('workspaceId', options.workspace);
|
||||
if (options.task) query.set('taskId', options.task);
|
||||
if (options.rootTask) query.set('rootTaskId', options.rootTask);
|
||||
if (options.provider) query.set('provider', options.provider);
|
||||
if (options.host) query.set('hostId', options.host);
|
||||
if (options.workflowRun) query.set('workflowRunId', options.workflowRun);
|
||||
if (options.workflowStep) query.set('workflowStepId', options.workflowStep);
|
||||
if (options.rootReservation) query.set('rootReservationId', options.rootReservation);
|
||||
if (options.rootObjective) query.set('rootObjectiveId', options.rootObjective);
|
||||
if (options.node) query.set('nodeId', options.node);
|
||||
if (options.parentNode) query.set('parentNodeId', options.parentNode);
|
||||
for (const state of (options.state ?? []) as AdmissionReservationState[]) {
|
||||
query.append('state', state);
|
||||
}
|
||||
query.set('limit', options.limit);
|
||||
const result = await api<AdmissionListResponse>(`/api/admission?${query.toString()}`);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
if (result.reservations.length === 0) {
|
||||
console.log(chalk.dim('No admission reservations matched.'));
|
||||
return;
|
||||
}
|
||||
for (const reservation of result.reservations) printReservation(reservation);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
admission
|
||||
.command('tree <root-objective-id>')
|
||||
.description('Inspect aggregate usage and reservations for one execution tree')
|
||||
.option('--limit <count>', 'Maximum contributors', '100')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (rootObjectiveId, options) => {
|
||||
try {
|
||||
const query = new URLSearchParams({ limit: options.limit });
|
||||
const result = await api<ExecutionTreeBudgetSummary>(
|
||||
`/api/admission/tree/${encodeURIComponent(rootObjectiveId)}?${query.toString()}`
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
printExecutionTreeSummary(result);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
admission
|
||||
.command('cancel-tree <root-objective-id>')
|
||||
.description('Cancel queued and verified running work for one execution tree')
|
||||
.requiredOption('--reason <text>', 'Operator reason for cancellation')
|
||||
.option('--idempotency-key <key>', 'Stable identity for safe retries')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (rootObjectiveId, options) => {
|
||||
try {
|
||||
const result = await api<AdmissionExecutionTreeCancellationResult>(
|
||||
`/api/admission/tree/${encodeURIComponent(rootObjectiveId)}/cancel`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
reason: options.reason,
|
||||
idempotencyKey:
|
||||
options.idempotencyKey ?? `vk-cli:tree-cancel:${rootObjectiveId}:${randomUUID()}`,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green(`✓ Cancelled execution tree ${result.rootObjectiveId}`));
|
||||
console.log(
|
||||
chalk.dim(
|
||||
`Queued: ${result.queueEntriesCancelled}; interrupted: ${result.interruptedAttempts}; remaining verified runs: ${result.runningAttempts.length}`
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
admission
|
||||
.command('resume-tree <root-objective-id>')
|
||||
.description('Resume an eligible execution tree after its fan-out breaker pauses')
|
||||
.requiredOption('--reason <text>', 'Operator reason for resuming expansion')
|
||||
.option('--idempotency-key <key>', 'Stable identity for safe retries')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (rootObjectiveId, options) => {
|
||||
try {
|
||||
const result = await api<ExecutionTreeControl>(
|
||||
`/api/admission/tree/${encodeURIComponent(rootObjectiveId)}/resume`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
reason: options.reason,
|
||||
idempotencyKey:
|
||||
options.idempotencyKey ?? `vk-cli:tree-resume:${rootObjectiveId}:${randomUUID()}`,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green(`✓ Resumed execution tree ${result.rootObjectiveId}`));
|
||||
console.log(chalk.dim(`Recorded: ${result.resumedAt}; reason: ${result.resumeReason}`));
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
admission
|
||||
.command('get <id>')
|
||||
.description('Inspect one admission reservation')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const result = await api<AdmissionReservation>(`/api/admission/${encodeURIComponent(id)}`);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
printReservation(result, true);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function printQueueEntry(entry: AdmissionQueueInspectionEntry, verbose = false): void {
|
||||
console.log(
|
||||
`${entry.position ?? '-'} ${entry.state} ${chalk.bold(entry.id)} priority=${entry.rawPriority}->${entry.effectivePriority} readiness=${entry.readiness}`
|
||||
);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` source=${entry.launch.source} target=${entry.launch.target} age=${entry.ageMs}ms lease=${entry.lease.posture}`
|
||||
)
|
||||
);
|
||||
if (verbose) {
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` retries=${entry.retry.count}/${entry.retry.maximum} available=${entry.retry.availableAt}`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
chalk.dim(` conditional=${entry.conditionalStartFactors.join(',') || 'capacity-recheck'}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function printReservation(reservation: AdmissionReservation, verbose = false): void {
|
||||
const state =
|
||||
reservation.state === 'active'
|
||||
? chalk.green(reservation.state)
|
||||
: reservation.state === 'released'
|
||||
? chalk.blue(reservation.state)
|
||||
: chalk.yellow(reservation.state);
|
||||
console.log(
|
||||
`${state} ${chalk.bold(reservation.id)} task=${reservation.request.taskId} provider=${reservation.request.provider}`
|
||||
);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` workspace=${reservation.request.workspaceId} root=${reservation.request.rootTaskId} host=${reservation.request.hostId}`
|
||||
)
|
||||
);
|
||||
if (reservation.request.workflowRunId) {
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` workflow=${reservation.request.workflowRunId} step=${reservation.request.workflowStepId ?? 'root'} root-reservation=${reservation.request.rootReservationId ?? reservation.id}`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (reservation.request.executionTree) {
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` objective=${reservation.request.executionTree.rootObjectiveId} node=${reservation.request.executionTree.nodeId} parent=${reservation.request.executionTree.parentNodeId ?? 'root'} edge=${reservation.request.executionTree.edge}`
|
||||
)
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` capacity runs=${reservation.request.requested.runSlots} processes=${reservation.request.requested.processSlots} memory=${reservation.request.requested.estimatedMemoryMb}MB`
|
||||
)
|
||||
);
|
||||
if (verbose || reservation.state === 'active') {
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` attempt=${reservation.attemptId ?? 'unbound'} lease=${reservation.lease.expiresAt} revision=${reservation.revision}`
|
||||
)
|
||||
);
|
||||
}
|
||||
if (reservation.release) {
|
||||
console.log(
|
||||
chalk.dim(` released=${reservation.release.reason} at ${reservation.release.releasedAt}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function printExecutionTreeSummary(summary: ExecutionTreeBudgetSummary): void {
|
||||
console.log(chalk.bold(`Execution tree ${summary.rootObjectiveId}`));
|
||||
if (summary.control) {
|
||||
const color = summary.control.state === 'resumed' ? chalk.green : chalk.red;
|
||||
console.log(
|
||||
color(
|
||||
` control=${summary.control.state} trigger=${summary.control.trigger} recorded=${summary.control.recordedAt}`
|
||||
)
|
||||
);
|
||||
console.log(chalk.dim(` reason=${summary.control.reason}`));
|
||||
if (summary.control.resumedAt) {
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` resumed=${summary.control.resumedAt} resume-reason=${summary.control.resumeReason}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
` committed tokens=${summary.committed.totalTokens} cost=$${summary.committed.costUsd.toFixed(4)} tools=${summary.committed.toolCalls} runtime=${summary.committed.runtimeSeconds}s retries=${summary.committed.retries} fan-out=${summary.committed.fanOut}`
|
||||
);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` reserved tokens=${summary.reserved.totalTokens} cost=$${summary.reserved.costUsd.toFixed(4)} tools=${summary.reserved.toolCalls} runtime=${summary.reserved.runtimeSeconds}s retries=${summary.reserved.retries} fan-out=${summary.reserved.fanOut}`
|
||||
)
|
||||
);
|
||||
for (const status of summary.policies) {
|
||||
console.log(
|
||||
`${status.blocksNextLaunch ? chalk.red('blocked') : chalk.green('available')} ${status.policy.name} (${status.policy.scope}:${status.policy.scopeId})`
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` contributors=${summary.contributorCount}${summary.truncated ? ` (showing ${summary.contributors.length})` : ''}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function printError(error: unknown): void {
|
||||
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { Command } from 'commander';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import chalk from 'chalk';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
|
@ -11,7 +12,16 @@ import type {
|
|||
AgentProfileValidationResult,
|
||||
ConversationLifecycleRecord,
|
||||
ConversationLifecycleResult,
|
||||
PhaseCapabilityEvidence,
|
||||
PhaseTransitionRecord,
|
||||
PhaseTransitionResult,
|
||||
RunApprovalRequest,
|
||||
RunRecoveryRecord,
|
||||
RunLaunchManifestPreview,
|
||||
RunPhaseAuthoritySnapshot,
|
||||
WorkspaceExecutionTrustDecision,
|
||||
WorkspaceExecutionTrustDecisionMode,
|
||||
WorkspaceExecutionTrustScanResult,
|
||||
} from '@veritas-kanban/shared';
|
||||
|
||||
type ConversationTurnAction = 'resume' | 'follow-up' | 'fork';
|
||||
|
|
@ -22,6 +32,7 @@ interface ConversationTurnOptions {
|
|||
message: string;
|
||||
forkTurn?: string;
|
||||
profile?: string;
|
||||
phase?: string;
|
||||
requireCapability?: string[];
|
||||
commitPolicy?: string;
|
||||
json?: boolean;
|
||||
|
|
@ -32,6 +43,20 @@ interface ConversationControlOptions {
|
|||
json?: boolean;
|
||||
}
|
||||
|
||||
interface PhaseTransitionOptions {
|
||||
attempt: string;
|
||||
operation: string;
|
||||
targetEvidence: string;
|
||||
fromEvidence?: string;
|
||||
manifest?: string;
|
||||
reason: string;
|
||||
approvalId?: string;
|
||||
approvalTtlMs?: string;
|
||||
overrideUntil?: string;
|
||||
overrideReason?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
function inferProfileFormat(filePath: string): AgentProfilePackageFormat {
|
||||
const extension = path.extname(filePath).toLowerCase();
|
||||
return extension === '.json' ? 'json' : 'yaml';
|
||||
|
|
@ -43,6 +68,10 @@ async function resolveTaskId(id: string): Promise<string> {
|
|||
return task.id;
|
||||
}
|
||||
|
||||
function readPhaseEvidence(filePath: string): PhaseCapabilityEvidence {
|
||||
return JSON.parse(readFileSync(path.resolve(filePath), 'utf8')) as PhaseCapabilityEvidence;
|
||||
}
|
||||
|
||||
function printConversationResult(
|
||||
action: string,
|
||||
result: {
|
||||
|
|
@ -65,6 +94,17 @@ function printConversationResult(
|
|||
if (result.note) console.log(chalk.dim(result.note));
|
||||
}
|
||||
|
||||
function phaseIdentityLabel(record: PhaseTransitionRecord): string {
|
||||
return phaseEvidenceIdentityLabel(record.effectiveEvidence);
|
||||
}
|
||||
|
||||
function phaseEvidenceIdentityLabel(evidence: PhaseCapabilityEvidence): string {
|
||||
const identity = evidence.identity;
|
||||
return identity.mode === 'legacy'
|
||||
? 'legacy'
|
||||
: `${identity.phase} (${identity.profileId}@${identity.profileVersion})`;
|
||||
}
|
||||
|
||||
function registerConversationTurnCommand(
|
||||
program: Command,
|
||||
action: ConversationTurnAction,
|
||||
|
|
@ -76,6 +116,7 @@ function registerConversationTurnCommand(
|
|||
.requiredOption('--source-attempt <attemptId>', 'Terminal attempt with durable conversation')
|
||||
.requiredOption('-m, --message <text>', 'Prompt for the new turn')
|
||||
.option('-p, --profile <profileId>', 'Agent profile package to launch')
|
||||
.option('--phase <phase>', 'Execution phase (explore, plan, implement, verify, publish)')
|
||||
.option(
|
||||
'--require-capability <capabilities...>',
|
||||
'Require provider runtime capabilities before launch'
|
||||
|
|
@ -103,8 +144,10 @@ function registerConversationTurnCommand(
|
|||
message: options.message,
|
||||
...(action === 'fork' && options.forkTurn ? { forkTurnId: options.forkTurn } : {}),
|
||||
profileId: options.profile,
|
||||
phase: options.phase,
|
||||
requiredRuntimeCapabilities: options.requireCapability,
|
||||
commitPolicy: options.commitPolicy,
|
||||
idempotencyKey: `vk-cli:${taskId}:conversation:${action}:${randomUUID()}`,
|
||||
}),
|
||||
});
|
||||
printConversationResult(action, result, options.json);
|
||||
|
|
@ -154,6 +197,7 @@ export function registerAgentCommands(program: Command): void {
|
|||
'claude-code'
|
||||
)
|
||||
.option('-p, --profile <profileId>', 'Agent profile package to launch')
|
||||
.option('--phase <phase>', 'Execution phase (explore, plan, implement, verify, publish)')
|
||||
.option(
|
||||
'--require-capability <capabilities...>',
|
||||
'Require provider runtime capabilities before launch'
|
||||
|
|
@ -188,9 +232,11 @@ export function registerAgentCommands(program: Command): void {
|
|||
body: JSON.stringify({
|
||||
agent: options.profile ? undefined : options.agent,
|
||||
profileId: options.profile,
|
||||
phase: options.phase,
|
||||
requiredRuntimeCapabilities: options.requireCapability,
|
||||
commitPolicy: options.commitPolicy,
|
||||
parentAttemptId: options.parentAttempt,
|
||||
idempotencyKey: `vk-cli:${task.id}:${randomUUID()}`,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -212,6 +258,7 @@ export function registerAgentCommands(program: Command): void {
|
|||
.description('Preview the immutable effective launch manifest without starting an agent')
|
||||
.option('-a, --agent <agent>', 'Agent to use', 'codex')
|
||||
.option('-p, --profile <profileId>', 'Agent profile package to preview')
|
||||
.option('--phase <phase>', 'Execution phase (explore, plan, implement, verify, publish)')
|
||||
.option(
|
||||
'--require-capability <capabilities...>',
|
||||
'Require provider runtime capabilities before launch'
|
||||
|
|
@ -233,6 +280,7 @@ export function registerAgentCommands(program: Command): void {
|
|||
body: JSON.stringify({
|
||||
agent: options.profile ? undefined : options.agent,
|
||||
profileId: options.profile,
|
||||
phase: options.phase,
|
||||
requiredRuntimeCapabilities: options.requireCapability,
|
||||
commitPolicy: options.commitPolicy,
|
||||
parentAttemptId: options.parentAttempt,
|
||||
|
|
@ -247,6 +295,18 @@ export function registerAgentCommands(program: Command): void {
|
|||
console.log(` Digest: ${preview.manifest.digest}`);
|
||||
console.log(` Provider: ${preview.manifest.providerRuntime.provider}`);
|
||||
console.log(` Model: ${preview.manifest.runtime.model ?? 'provider default'}`);
|
||||
console.log(
|
||||
` Phase: ${
|
||||
preview.manifest.phase?.evidence.identity.mode === 'profile'
|
||||
? preview.manifest.phase.evidence.identity.phase
|
||||
: 'legacy'
|
||||
}`
|
||||
);
|
||||
if (preview.manifest.phase) {
|
||||
console.log(` Phase evidence: ${preview.manifest.phase.evidence.digest}`);
|
||||
}
|
||||
console.log(` Workspace trust: ${preview.manifest.workspaceTrust.status}`);
|
||||
console.log(chalk.dim(` ${preview.manifest.workspaceTrust.source}`));
|
||||
console.log(
|
||||
` Enforceable: ${preview.manifest.enforcement.enforceable ? chalk.green('yes') : chalk.red('no')}`
|
||||
);
|
||||
|
|
@ -264,6 +324,119 @@ export function registerAgentCommands(program: Command): void {
|
|||
}
|
||||
});
|
||||
|
||||
const workspaceTrust = program
|
||||
.command('workspace-trust')
|
||||
.description('Inspect and manage repository execution trust');
|
||||
|
||||
workspaceTrust
|
||||
.command('scan <id>')
|
||||
.description('Scan repository-controlled instructions and executable configuration')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<WorkspaceExecutionTrustScanResult>(
|
||||
`/api/agents/${taskId}/workspace-trust`
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.bold('Workspace execution trust'));
|
||||
console.log(` Identity: ${result.inventory.identity.digest}`);
|
||||
console.log(` Inventory: ${result.inventory.digest}`);
|
||||
console.log(` Project maximum: ${result.inventory.projectPolicy.maximumTrust}`);
|
||||
console.log(` Current decision: ${result.currentDecision?.mode ?? chalk.yellow('none')}`);
|
||||
if (result.inventory.entries.length === 0) {
|
||||
console.log(chalk.dim(' No recognized repository-controlled components found.'));
|
||||
return;
|
||||
}
|
||||
for (const entry of result.inventory.entries) {
|
||||
console.log(
|
||||
` ${entry.posture === 'executable' ? chalk.red('!') : chalk.yellow('•')} ${entry.relativePath}`
|
||||
);
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` ${entry.kind}; ${entry.posture}; ${entry.requestedCapabilities.join(', ')}`
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
workspaceTrust
|
||||
.command('decide <id>')
|
||||
.description('Record trust, restricted, or denied for an exact scanned inventory')
|
||||
.requiredOption('--mode <mode>', 'Decision mode: trusted, restricted, or denied')
|
||||
.requiredOption('--inventory <digest>', 'Exact inventory digest from workspace-trust scan')
|
||||
.requiredOption('--reason <text>', 'Reason for the decision')
|
||||
.option('--expires-at <timestamp>', 'Optional ISO-8601 expiry')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const mode = options.mode as WorkspaceExecutionTrustDecisionMode;
|
||||
if (!['trusted', 'restricted', 'denied'].includes(mode)) {
|
||||
throw new Error('Mode must be trusted, restricted, or denied.');
|
||||
}
|
||||
const taskId = await resolveTaskId(id);
|
||||
const decision = await api<WorkspaceExecutionTrustDecision>(
|
||||
`/api/agents/${taskId}/workspace-trust/decisions`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
mode,
|
||||
inventoryDigest: options.inventory,
|
||||
reason: options.reason,
|
||||
expiresAt: options.expiresAt,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(decision, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green(`✓ Workspace decision recorded: ${decision.mode}`));
|
||||
console.log(chalk.dim(`Decision ID: ${decision.id}`));
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
workspaceTrust
|
||||
.command('revoke <id>')
|
||||
.description('Revoke the current workspace execution trust decision')
|
||||
.requiredOption('--inventory <digest>', 'Exact current inventory digest')
|
||||
.requiredOption('--reason <text>', 'Reason for revocation')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const decision = await api<WorkspaceExecutionTrustDecision>(
|
||||
`/api/agents/${taskId}/workspace-trust/revoke`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
inventoryDigest: options.inventory,
|
||||
reason: options.reason,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(decision, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green('✓ Workspace execution trust decision revoked'));
|
||||
console.log(chalk.dim(`Decision ID: ${decision.id}`));
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
const profiles = program
|
||||
.command('profiles')
|
||||
.description('Manage reusable agent profile packages');
|
||||
|
|
@ -419,6 +592,238 @@ export function registerAgentCommands(program: Command): void {
|
|||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('agent:recovery <id>')
|
||||
.description('Show the latest durable retry or fallback decision for a task')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<{ recovery: RunRecoveryRecord | null }>(
|
||||
`/api/agents/${taskId}/recovery`
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else if (!result.recovery) {
|
||||
console.log(chalk.dim('No recovery decision is recorded for this task'));
|
||||
} else {
|
||||
console.log(chalk.yellow(`Recovery: ${result.recovery.state}`));
|
||||
console.log(` Action: ${result.recovery.action}`);
|
||||
console.log(` Attempt: ${result.recovery.parentRunId}`);
|
||||
console.log(` Sequence: ${result.recovery.sequence}`);
|
||||
console.log(` Reason: ${result.recovery.reason}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('agent:phase <id>')
|
||||
.description('Show the active durable phase and transition history for an exact run')
|
||||
.requiredOption('--attempt <attemptId>', 'Exact attempt ID')
|
||||
.option('--limit <count>', 'Maximum transition records', '100')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(
|
||||
async (
|
||||
id: string,
|
||||
options: { attempt: string; limit: string; json?: boolean }
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<{
|
||||
phase: RunPhaseAuthoritySnapshot | null;
|
||||
current: PhaseTransitionRecord | null;
|
||||
history: PhaseTransitionRecord[];
|
||||
}>(
|
||||
`/api/agents/${taskId}/phase?attemptId=${encodeURIComponent(options.attempt)}&limit=${encodeURIComponent(options.limit)}`
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else if (!result.phase) {
|
||||
console.log(chalk.dim('This legacy run has no phase authority evidence'));
|
||||
} else {
|
||||
console.log(
|
||||
chalk.cyan(`Phase: ${phaseEvidenceIdentityLabel(result.phase.effectiveEvidence)}`)
|
||||
);
|
||||
console.log(chalk.dim(`Sequence: ${result.phase.transitionSequence}`));
|
||||
console.log(chalk.dim(`Evidence: ${result.phase.effectiveEvidence.digest}`));
|
||||
console.log(chalk.dim(`Manifest: ${result.phase.manifestDigest}`));
|
||||
if (result.current?.emergencyOverride) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Emergency override expires: ${result.current.emergencyOverride.expiresAt}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
program
|
||||
.command('agent:transition-phase <id>')
|
||||
.description('Apply or request approval for one compare-and-set phase transition')
|
||||
.requiredOption('--attempt <attemptId>', 'Exact active attempt ID')
|
||||
.requiredOption('--operation <id>', 'Stable idempotency key for this transition')
|
||||
.requiredOption('--target-evidence <file>', 'Compiled target phase evidence JSON')
|
||||
.requiredOption('--reason <text>', 'Operator reason')
|
||||
.option('--from-evidence <file>', 'Initial phase evidence JSON for the first transition')
|
||||
.option('--manifest <digest>', 'Launch manifest digest for the first transition')
|
||||
.option('--approval-id <id>', 'Exact approval returned by the prior request')
|
||||
.option('--approval-ttl-ms <milliseconds>', 'Approval request lifetime')
|
||||
.option('--override-until <timestamp>', 'Emergency override expiry, at most 24 hours')
|
||||
.option('--override-reason <text>', 'Emergency override justification')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id: string, options: PhaseTransitionOptions): Promise<void> => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const state = await api<{
|
||||
phase: RunPhaseAuthoritySnapshot | null;
|
||||
current: PhaseTransitionRecord | null;
|
||||
history: PhaseTransitionRecord[];
|
||||
}>(`/api/agents/${taskId}/phase?attemptId=${encodeURIComponent(options.attempt)}`);
|
||||
const fromEvidence = options.fromEvidence
|
||||
? readPhaseEvidence(options.fromEvidence)
|
||||
: undefined;
|
||||
const priorEvidence =
|
||||
state.phase?.effectiveEvidence ?? state.current?.effectiveEvidence ?? fromEvidence;
|
||||
if (!priorEvidence) {
|
||||
throw new Error('The first transition requires --from-evidence');
|
||||
}
|
||||
const manifestDigest =
|
||||
state.phase?.manifestDigest ?? state.current?.manifestDigest ?? options.manifest;
|
||||
if (!manifestDigest) {
|
||||
throw new Error('The first transition requires --manifest');
|
||||
}
|
||||
if (
|
||||
(options.overrideUntil && !options.overrideReason) ||
|
||||
(!options.overrideUntil && options.overrideReason)
|
||||
) {
|
||||
throw new Error('--override-until and --override-reason must be used together');
|
||||
}
|
||||
const approvalTtlMs =
|
||||
options.approvalTtlMs && /^\d+$/.test(options.approvalTtlMs)
|
||||
? Number(options.approvalTtlMs)
|
||||
: undefined;
|
||||
if (options.approvalTtlMs && !Number.isSafeInteger(approvalTtlMs)) {
|
||||
throw new Error('--approval-ttl-ms must be an integer');
|
||||
}
|
||||
const result = await api<PhaseTransitionResult>(`/api/agents/${taskId}/phase/transitions`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
attemptId: options.attempt,
|
||||
operationId: options.operation,
|
||||
expectedSequence: state.current?.sequence ?? 0,
|
||||
expectedPhaseEvidenceDigest: priorEvidence.digest,
|
||||
expectedManifestDigest: manifestDigest,
|
||||
reason: options.reason,
|
||||
...(state.current ? {} : { fromEvidence: priorEvidence }),
|
||||
targetEvidence: readPhaseEvidence(options.targetEvidence),
|
||||
approvalId: options.approvalId,
|
||||
approvalTtlMs,
|
||||
...(options.overrideUntil && options.overrideReason
|
||||
? {
|
||||
emergencyOverride: {
|
||||
expiresAt: options.overrideUntil,
|
||||
justification: options.overrideReason,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
});
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else if (result.status === 'approval-required' && result.approval) {
|
||||
console.log(chalk.yellow('Phase expansion requires approval'));
|
||||
console.log(` Approval: ${result.approval.id}`);
|
||||
console.log(` Revision: ${result.approval.revision}`);
|
||||
console.log(` Action hash: ${result.approval.actionHash}`);
|
||||
console.log(
|
||||
chalk.dim('Approve it, then retry this command with the same --operation value.')
|
||||
);
|
||||
} else if (result.record) {
|
||||
console.log(chalk.green(`✓ Phase transitioned to ${phaseIdentityLabel(result.record)}`));
|
||||
console.log(chalk.dim(`Sequence: ${result.record.sequence}`));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('agent:decide-phase-approval <approvalId>')
|
||||
.description('Approve or reject an exact pending phase transition')
|
||||
.requiredOption('--decision <decision>', 'approve or reject')
|
||||
.option('--note <text>', 'Decision note')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(
|
||||
async (
|
||||
approvalId: string,
|
||||
options: { decision: string; note?: string; json?: boolean }
|
||||
): Promise<void> => {
|
||||
try {
|
||||
if (!['approve', 'reject'].includes(options.decision)) {
|
||||
throw new Error('--decision must be approve or reject');
|
||||
}
|
||||
const approval = await api<RunApprovalRequest>(
|
||||
`/api/run-approvals/${encodeURIComponent(approvalId)}`
|
||||
);
|
||||
const decided = await api<RunApprovalRequest>(
|
||||
`/api/run-approvals/${encodeURIComponent(approval.id)}/decision`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
decision: options.decision === 'approve' ? 'approved' : 'rejected',
|
||||
expectedRevision: approval.revision,
|
||||
expectedActionHash: approval.actionHash,
|
||||
note: options.note,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(decided, null, 2));
|
||||
} else {
|
||||
console.log(chalk.green(`✓ Phase transition approval ${decided.status}`));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
program
|
||||
.command('agent:cancel-recovery <id>')
|
||||
.description('Cancel the exact pending retry or fallback for a task')
|
||||
.requiredOption('--attempt <attemptId>', 'Parent attempt that owns the pending recovery')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options: { attempt: string; json?: boolean }) => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<{ cancelled: boolean; recovery: RunRecoveryRecord }>(
|
||||
`/api/agents/${taskId}/recovery/cancel`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ attemptId: options.attempt }),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
console.log(chalk.yellow('✓ Automatic recovery cancelled'));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
registerConversationTurnCommand(
|
||||
program,
|
||||
'resume',
|
||||
|
|
|
|||
312
cli/src/commands/goals.ts
Normal file
312
cli/src/commands/goals.ts
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import type {
|
||||
DurableGoalBlocker,
|
||||
DurableGoalCompletionEvidence,
|
||||
DurableGoalCompletionRequirement,
|
||||
DurableGoalContinuationMode,
|
||||
DurableGoalRecord,
|
||||
DurableGoalState,
|
||||
} from '@veritas-kanban/shared';
|
||||
import { DURABLE_GOAL_STATES } from '@veritas-kanban/shared';
|
||||
import { api } from '../utils/api.js';
|
||||
|
||||
interface GoalListResponse {
|
||||
generatedAt: string;
|
||||
goals: DurableGoalRecord[];
|
||||
}
|
||||
|
||||
interface GoalRolloverResponse {
|
||||
action: string;
|
||||
goal?: DurableGoalRecord;
|
||||
continuation?: {
|
||||
id: string;
|
||||
kind: string;
|
||||
state: string;
|
||||
resultAttemptId?: string;
|
||||
queueId?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type GoalBlockerInput = Omit<DurableGoalBlocker, 'id' | 'recordedAt'> & { id?: string };
|
||||
|
||||
const VERIFICATION_KINDS = new Set(['test', 'build', 'artifact', 'operator', 'external', 'other']);
|
||||
|
||||
export function registerGoalCommands(program: Command): void {
|
||||
const goals = program
|
||||
.command('goals')
|
||||
.description('Create, inspect, and control durable objectives');
|
||||
|
||||
goals
|
||||
.command('list')
|
||||
.description('List durable goals in the current workspace')
|
||||
.option('--state <states...>', 'Filter by goal state')
|
||||
.option('--root-task <id>', 'Filter by root task')
|
||||
.option('--root-workflow <id>', 'Filter by root workflow')
|
||||
.option('--limit <count>', 'Maximum goals', '100')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
const query = new URLSearchParams();
|
||||
for (const state of (options.state ?? []) as DurableGoalState[]) {
|
||||
query.append('state', state);
|
||||
}
|
||||
if (options.rootTask) query.set('rootTaskId', options.rootTask);
|
||||
if (options.rootWorkflow) query.set('rootWorkflowId', options.rootWorkflow);
|
||||
query.set('limit', options.limit);
|
||||
const result = await api<GoalListResponse>(`/api/goals?${query.toString()}`);
|
||||
if (options.json) return printJson(result);
|
||||
if (result.goals.length === 0) {
|
||||
console.log(chalk.dim('No durable goals matched.'));
|
||||
return;
|
||||
}
|
||||
for (const goal of result.goals) printGoal(goal);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
goals
|
||||
.command('get <id>')
|
||||
.description('Inspect one durable goal')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const goal = await api<DurableGoalRecord>(`/api/goals/${encodeURIComponent(id)}`);
|
||||
if (options.json) return printJson(goal);
|
||||
printGoal(goal, true);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
goals
|
||||
.command('create')
|
||||
.description('Create an evidence-gated durable goal')
|
||||
.requiredOption('--objective <text>', 'Goal objective')
|
||||
.requiredOption('--acceptance <criteria...>', 'Acceptance criteria')
|
||||
.requiredOption(
|
||||
'--requirement <requirements...>',
|
||||
'Completion requirement as id|kind|description'
|
||||
)
|
||||
.option('--constraint <constraints...>', 'Goal constraints')
|
||||
.option('--root-task <id>', 'Root task identity')
|
||||
.option('--root-workflow <id>', 'Root workflow identity')
|
||||
.option('--task <id>', 'Optional task associated with a root workflow')
|
||||
.option('--mode <mode>', 'Continuation mode: manual or automatic', 'manual')
|
||||
.option('--max-turns <count>', 'Maximum continuation turns')
|
||||
.option('--max-rollovers <count>', 'Maximum conversation rollovers')
|
||||
.option('--compact-after-tokens <count>', 'Compaction threshold')
|
||||
.option('--require-rollover-approval', 'Require approval before rollover')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options) => {
|
||||
try {
|
||||
if (Boolean(options.rootTask) === Boolean(options.rootWorkflow)) {
|
||||
throw new Error('Specify exactly one of --root-task or --root-workflow.');
|
||||
}
|
||||
if (!['manual', 'automatic'].includes(options.mode)) {
|
||||
throw new Error('--mode must be manual or automatic.');
|
||||
}
|
||||
const completionRequirements = (options.requirement as string[]).map(
|
||||
parseCompletionRequirement
|
||||
);
|
||||
const root = options.rootTask
|
||||
? { kind: 'task' as const, taskId: options.rootTask }
|
||||
: {
|
||||
kind: 'workflow' as const,
|
||||
workflowId: options.rootWorkflow,
|
||||
...(options.task ? { taskId: options.task } : {}),
|
||||
};
|
||||
const goal = await api<DurableGoalRecord>('/api/goals', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
objective: options.objective,
|
||||
constraints: options.constraint ?? [],
|
||||
acceptanceCriteria: options.acceptance,
|
||||
root,
|
||||
continuation: {
|
||||
mode: options.mode as DurableGoalContinuationMode,
|
||||
...(options.maxTurns ? { maxTurns: parsePositiveInteger(options.maxTurns) } : {}),
|
||||
...(options.maxRollovers
|
||||
? { maxRollovers: parseNonnegativeInteger(options.maxRollovers) }
|
||||
: {}),
|
||||
...(options.compactAfterTokens
|
||||
? { compactAfterTokens: parsePositiveInteger(options.compactAfterTokens) }
|
||||
: {}),
|
||||
...(options.requireRolloverApproval ? { requireApprovalForRollover: true } : {}),
|
||||
},
|
||||
completionRequirements,
|
||||
}),
|
||||
});
|
||||
if (options.json) return printJson(goal);
|
||||
console.log(chalk.green(`✓ Created durable goal ${goal.id}`));
|
||||
printGoal(goal);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
goals
|
||||
.command('transition <id>')
|
||||
.description('Apply one compare-and-set goal state transition')
|
||||
.requiredOption('--revision <number>', 'Expected goal revision')
|
||||
.requiredOption('--state <state>', `New state: ${DURABLE_GOAL_STATES.join(', ')}`)
|
||||
.requiredOption('--reason <text>', 'Operator reason')
|
||||
.option('--blocker-json <json>', 'Actionable blocker JSON for blocked state')
|
||||
.option('--evidence-json <json>', 'Completion evidence JSON array')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
if (!DURABLE_GOAL_STATES.includes(options.state as DurableGoalState)) {
|
||||
throw new Error(`Unknown goal state: ${options.state}`);
|
||||
}
|
||||
const blocker = options.blockerJson
|
||||
? parseJson<GoalBlockerInput>(options.blockerJson, '--blocker-json')
|
||||
: undefined;
|
||||
const completionEvidence = options.evidenceJson
|
||||
? parseJson<
|
||||
Array<Pick<DurableGoalCompletionEvidence, 'requirementId' | 'evidenceId' | 'summary'>>
|
||||
>(options.evidenceJson, '--evidence-json')
|
||||
: undefined;
|
||||
const goal = await api<DurableGoalRecord>(
|
||||
`/api/goals/${encodeURIComponent(id)}/transition`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
expectedRevision: parsePositiveInteger(options.revision),
|
||||
state: options.state,
|
||||
reason: options.reason,
|
||||
blocker,
|
||||
completionEvidence,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) return printJson(goal);
|
||||
console.log(chalk.green(`✓ Goal ${goal.id} is ${goal.state} at revision ${goal.revision}`));
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
goals
|
||||
.command('link-run <id>')
|
||||
.description('Link one run or continuation to a durable goal')
|
||||
.requiredOption('--revision <number>', 'Expected goal revision')
|
||||
.requiredOption('--task <id>', 'Run task identity')
|
||||
.option('--attempt <id>', 'Attempt identity')
|
||||
.option('--workflow-run <id>', 'Workflow run identity')
|
||||
.option('--conversation <id>', 'Conversation identity')
|
||||
.option('--parent-attempt <id>', 'Causal parent attempt')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const goal = await api<DurableGoalRecord>(`/api/goals/${encodeURIComponent(id)}/runs`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
expectedRevision: parsePositiveInteger(options.revision),
|
||||
taskId: options.task,
|
||||
attemptId: options.attempt,
|
||||
workflowRunId: options.workflowRun,
|
||||
conversationId: options.conversation,
|
||||
parentAttemptId: options.parentAttempt,
|
||||
}),
|
||||
});
|
||||
if (options.json) return printJson(goal);
|
||||
console.log(chalk.green(`✓ Linked run to goal ${goal.id} at revision ${goal.revision}`));
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
|
||||
goals
|
||||
.command('rollover <id>')
|
||||
.description('Approve and dispatch one bounded fresh-conversation rollover')
|
||||
.requiredOption('--revision <number>', 'Expected goal revision')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const result = await api<GoalRolloverResponse>(
|
||||
`/api/goals/${encodeURIComponent(id)}/rollover`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
expectedRevision: parsePositiveInteger(options.revision),
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) return printJson(result);
|
||||
const attempt = result.continuation?.resultAttemptId;
|
||||
console.log(
|
||||
chalk.green(`✓ Goal ${id} rollover ${result.action}${attempt ? ` as ${attempt}` : ''}`)
|
||||
);
|
||||
} catch (error) {
|
||||
printError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function parseCompletionRequirement(value: string): DurableGoalCompletionRequirement {
|
||||
const [id, verificationKind, ...descriptionParts] = value.split('|');
|
||||
const description = descriptionParts.join('|').trim();
|
||||
if (!id?.trim() || !verificationKind?.trim() || !description) {
|
||||
throw new Error(`Invalid requirement "${value}"; expected id|kind|description.`);
|
||||
}
|
||||
if (!VERIFICATION_KINDS.has(verificationKind)) {
|
||||
throw new Error(`Invalid verification kind "${verificationKind}".`);
|
||||
}
|
||||
return {
|
||||
id: id.trim(),
|
||||
verificationKind: verificationKind as DurableGoalCompletionRequirement['verificationKind'],
|
||||
description,
|
||||
required: true,
|
||||
};
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value: string): number {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
if (!Number.isInteger(parsed) || parsed <= 0) throw new Error(`Expected a positive integer.`);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function parseNonnegativeInteger(value: string): number {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`Expected a nonnegative integer.`);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function parseJson<T>(value: string, option: string): T {
|
||||
try {
|
||||
return JSON.parse(value) as T;
|
||||
} catch {
|
||||
throw new Error(`${option} must contain valid JSON.`);
|
||||
}
|
||||
}
|
||||
|
||||
function printGoal(goal: DurableGoalRecord, verbose = false): void {
|
||||
const state =
|
||||
goal.state === 'active'
|
||||
? chalk.green(goal.state)
|
||||
: ['complete'].includes(goal.state)
|
||||
? chalk.blue(goal.state)
|
||||
: ['cancelled', 'failed'].includes(goal.state)
|
||||
? chalk.red(goal.state)
|
||||
: chalk.yellow(goal.state);
|
||||
console.log(`${state} ${chalk.bold(goal.id)} revision=${goal.revision}`);
|
||||
console.log(` ${goal.objective}`);
|
||||
if (verbose) {
|
||||
console.log(
|
||||
chalk.dim(
|
||||
` root=${goal.root.kind === 'task' ? goal.root.taskId : goal.root.workflowId} runs=${goal.continuationChain.length} blockers=${goal.blockers.length} evidence=${goal.completionEvidence.length}/${goal.completionRequirements.length}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function printJson(value: unknown): void {
|
||||
console.log(JSON.stringify(value, null, 2));
|
||||
}
|
||||
|
||||
function printError(error: unknown): void {
|
||||
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
|
|
@ -25,6 +25,8 @@ import { registerQueueMonitorCommands } from './commands/queue-monitors.js';
|
|||
import { registerSqliteCommands } from './commands/sqlite.js';
|
||||
import { registerToolServerCommands } from './commands/tool-servers.js';
|
||||
import { registerAcpCommands } from './commands/acp.js';
|
||||
import { registerAdmissionCommands } from './commands/admission.js';
|
||||
import { registerGoalCommands } from './commands/goals.js';
|
||||
|
||||
const program = new Command();
|
||||
const packageJson = JSON.parse(
|
||||
|
|
@ -61,5 +63,7 @@ registerQueueMonitorCommands(program);
|
|||
registerSqliteCommands(program);
|
||||
registerToolServerCommands(program);
|
||||
registerAcpCommands(program);
|
||||
registerAdmissionCommands(program);
|
||||
registerGoalCommands(program);
|
||||
|
||||
program.parse();
|
||||
|
|
|
|||
|
|
@ -5,5 +5,21 @@ export default defineConfig({
|
|||
include: ['src/**/*.test.ts'],
|
||||
exclude: ['**/node_modules/**', '**/dist/**'],
|
||||
globals: true,
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: [
|
||||
'src/**/*.test.ts',
|
||||
'src/**/*.d.ts',
|
||||
'src/__tests__/**',
|
||||
'src/**/__fixtures__/**',
|
||||
'src/**/fixtures/**',
|
||||
'src/**/generated/**',
|
||||
'src/**/*.generated.*',
|
||||
'src/**/types.ts',
|
||||
'src/types/**/*.ts',
|
||||
],
|
||||
all: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ const electronRuntimeExternal = ['electron', /^electron\/.+/];
|
|||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
define: {
|
||||
__VERITAS_BUILD_SHA__: JSON.stringify(
|
||||
process.env.VERITAS_BUILD_SHA ?? process.env.GITHUB_SHA ?? ''
|
||||
),
|
||||
__VERITAS_RELEASE_CHANNEL__: JSON.stringify(process.env.VERITAS_UPDATE_CHANNEL ?? ''),
|
||||
},
|
||||
build: {
|
||||
// Vite 8 builds with Rolldown. Electron Vite 5 still places its built-in
|
||||
// runtime externals under rollupOptions, which Rolldown does not consume.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@veritas-kanban/desktop",
|
||||
"version": "6.0.0",
|
||||
"version": "6.1.2",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/BradGroux/veritas-kanban",
|
||||
"description": "Veritas Kanban native desktop shell",
|
||||
|
|
@ -33,13 +33,13 @@
|
|||
"electron-updater": "^6.8.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.1.1",
|
||||
"electron": "^43.1.0",
|
||||
"@types/node": "^26.2.0",
|
||||
"electron": "^43.4.1",
|
||||
"electron-builder": "^26.15.3",
|
||||
"electron-vite": "^5.0.0",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.1.4",
|
||||
"vitest": "^4.1.10"
|
||||
"vite": "^8.2.1",
|
||||
"vitest": "^4.1.11"
|
||||
},
|
||||
"build": {
|
||||
"appId": "io.digitalmeld.veritas-kanban",
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ function shell(): Shell {
|
|||
}
|
||||
|
||||
function handlers(): DesktopBridgeHandlerMap {
|
||||
return createDesktopBridgeHandlers(runtime(), shell(), false);
|
||||
return createDesktopBridgeHandlers(runtime(), shell(), false, '6.0.1');
|
||||
}
|
||||
|
||||
describe('desktop bridge contracts', () => {
|
||||
|
|
@ -124,7 +124,7 @@ describe('desktop bridge contracts', () => {
|
|||
}),
|
||||
} as unknown as IpcMain;
|
||||
|
||||
registerDesktopBridge(ipcMain, runtime(), shell(), false);
|
||||
registerDesktopBridge(ipcMain, runtime(), shell(), false, '6.0.1');
|
||||
|
||||
expect([...registered.keys()].sort()).toEqual(
|
||||
DESKTOP_BRIDGE_METHOD_NAMES.map((method) => DESKTOP_BRIDGE_METHODS[method].channel).sort()
|
||||
|
|
@ -132,6 +132,19 @@ describe('desktop bridge contracts', () => {
|
|||
expect(registered.size).toBe(DESKTOP_BRIDGE_METHOD_NAMES.length);
|
||||
});
|
||||
|
||||
it('reports the Electron application version through the desktop bridge', () => {
|
||||
const bridgeHandlers = createDesktopBridgeHandlers(runtime(), shell(), true, '6.0.1');
|
||||
|
||||
expect(bridgeHandlers.getAppInfo(undefined)).toMatchObject({
|
||||
name: 'Veritas Kanban',
|
||||
version: '6.0.1',
|
||||
channel: 'stable',
|
||||
arch: process.arch,
|
||||
osVersion: expect.any(String),
|
||||
packaged: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the preload API method list aligned to invoke and event contracts', () => {
|
||||
expect(DESKTOP_PRELOAD_API_METHODS).toEqual([
|
||||
...DESKTOP_BRIDGE_METHOD_NAMES,
|
||||
|
|
@ -172,7 +185,8 @@ describe('desktop bridge contracts', () => {
|
|||
const bridgeHandlers: DesktopBridgeHandlerMap = createDesktopBridgeHandlers(
|
||||
runtime(),
|
||||
fakeShell,
|
||||
false
|
||||
false,
|
||||
'6.0.1'
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
|
@ -193,7 +207,7 @@ describe('desktop bridge contracts', () => {
|
|||
|
||||
it('validates restart confirmation before restarting the local server', async () => {
|
||||
const fakeRuntime = runtime();
|
||||
const bridgeHandlers = createDesktopBridgeHandlers(fakeRuntime, shell(), false);
|
||||
const bridgeHandlers = createDesktopBridgeHandlers(fakeRuntime, shell(), false, '6.0.1');
|
||||
|
||||
expect(() => bridgeHandlers.restartLocalServer({ confirmation: 'restart' } as never)).toThrow(
|
||||
'explicit restart confirmation'
|
||||
|
|
|
|||
|
|
@ -93,6 +93,10 @@ describe('desktop command registry', () => {
|
|||
expect(DESKTOP_COMMAND_REGISTRY['new-task'].accelerator).toBe('CommandOrControl+N');
|
||||
expect(DESKTOP_COMMAND_REGISTRY['open-command-center'].accelerator).toBe('CommandOrControl+K');
|
||||
expect(DESKTOP_COMMAND_REGISTRY['open-onboarding'].label).toBe('Setup & Diagnostics');
|
||||
expect(DESKTOP_COMMAND_REGISTRY['reset-layout']).toMatchObject({
|
||||
label: 'Reset Window Layout',
|
||||
nativeAction: 'renderer',
|
||||
});
|
||||
});
|
||||
|
||||
it('routes renderer commands through the menu command event path', async () => {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ function updateStatus(state: DesktopUpdateStatus['state']): DesktopUpdateStatus
|
|||
describe('desktop native menu', () => {
|
||||
it('exposes common actions with keyboard shortcuts', () => {
|
||||
const dispatch = vi.fn();
|
||||
const template = createDesktopMenuTemplate({ status: status(), dispatch });
|
||||
const copyVersionInfo = vi.fn();
|
||||
const template = createDesktopMenuTemplate({ status: status(), dispatch, copyVersionInfo });
|
||||
const labels = template.flatMap((item) =>
|
||||
Array.isArray(item.submenu) ? item.submenu.map((child) => child.label) : []
|
||||
);
|
||||
|
|
@ -54,6 +55,15 @@ describe('desktop native menu', () => {
|
|||
expect(labels).toContain('Search');
|
||||
expect(labels).toContain('Settings');
|
||||
expect(labels).toContain('Restart Local Server');
|
||||
expect(labels).toContain('Reset Window Layout');
|
||||
|
||||
const appMenu = template.find((item) => item.label === 'Veritas Kanban');
|
||||
const appItems = Array.isArray(appMenu?.submenu) ? appMenu.submenu : [];
|
||||
expect(appItems[0]).toMatchObject({ role: 'about', label: 'About Veritas Kanban' });
|
||||
expect(appItems[1]).toMatchObject({ type: 'separator' });
|
||||
const copyVersion = appItems.find((item) => item.label === 'Copy Version Information');
|
||||
copyVersion?.click?.(undefined as never, undefined as never, undefined as never);
|
||||
expect(copyVersionInfo).toHaveBeenCalledOnce();
|
||||
|
||||
const fileMenu = template.find((item) => item.label === 'File');
|
||||
const newTask = Array.isArray(fileMenu?.submenu)
|
||||
|
|
@ -66,7 +76,11 @@ describe('desktop native menu', () => {
|
|||
});
|
||||
|
||||
it('exposes the native edit menu so macOS text fields receive standard shortcuts', () => {
|
||||
const template = createDesktopMenuTemplate({ status: status(), dispatch: vi.fn() });
|
||||
const template = createDesktopMenuTemplate({
|
||||
status: status(),
|
||||
dispatch: vi.fn(),
|
||||
copyVersionInfo: vi.fn(),
|
||||
});
|
||||
|
||||
expect(template.some((item) => item.role === 'editMenu')).toBe(true);
|
||||
});
|
||||
|
|
@ -75,6 +89,7 @@ describe('desktop native menu', () => {
|
|||
const desktopMenu = createDesktopMenuTemplate({
|
||||
status: status('failed'),
|
||||
dispatch: vi.fn(),
|
||||
copyVersionInfo: vi.fn(),
|
||||
}).find((item) => item.label === 'Desktop');
|
||||
const externalTest = Array.isArray(desktopMenu?.submenu)
|
||||
? desktopMenu.submenu.find((item) => item.label === 'Test External Delivery')
|
||||
|
|
@ -88,6 +103,7 @@ describe('desktop native menu', () => {
|
|||
status: status(),
|
||||
updateStatus: updateStatus('available'),
|
||||
dispatch: vi.fn(),
|
||||
copyVersionInfo: vi.fn(),
|
||||
}).find((item) => item.label === 'Veritas Kanban');
|
||||
const downloadUpdate = Array.isArray(appMenu?.submenu)
|
||||
? appMenu.submenu.find((item) => item.label === 'Download Update')
|
||||
|
|
|
|||
80
desktop/src/main/__tests__/version-info.test.ts
Normal file
80
desktop/src/main/__tests__/version-info.test.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
createDesktopAboutPanelOptions,
|
||||
createDesktopAppInfo,
|
||||
formatDesktopVersionInfo,
|
||||
normalizeBuildIdentity,
|
||||
} from '../version-info.js';
|
||||
|
||||
describe('desktop version information', () => {
|
||||
it('formats authoritative packaged version, build, channel, and platform details', () => {
|
||||
const info = createDesktopAppInfo('6.0.2', true, {
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
osVersion: '15.5',
|
||||
buildIdentity: 'abc1234',
|
||||
});
|
||||
|
||||
expect(info).toMatchObject({
|
||||
version: '6.0.2',
|
||||
buildIdentity: 'abc1234',
|
||||
channel: 'stable',
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
osVersion: '15.5',
|
||||
packaged: true,
|
||||
});
|
||||
expect(formatDesktopVersionInfo(info)).toBe(
|
||||
['Veritas Kanban 6.0.2', 'Build: abc1234', 'Channel: stable', 'macOS 15.5 · arm64'].join('\n')
|
||||
);
|
||||
});
|
||||
|
||||
it('labels prerelease and development builds without network access', () => {
|
||||
expect(
|
||||
createDesktopAppInfo('6.0.2-beta.1', true, {
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
osVersion: '15.5',
|
||||
buildIdentity: null,
|
||||
}).channel
|
||||
).toBe('beta');
|
||||
|
||||
const development = createDesktopAppInfo('6.0.2', false, {
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
osVersion: '15.5',
|
||||
buildIdentity: null,
|
||||
});
|
||||
expect(development.channel).toBe('dev');
|
||||
expect(formatDesktopVersionInfo(development)).toContain('Build: development');
|
||||
});
|
||||
|
||||
it('rejects path-like or unbounded build metadata from support output', () => {
|
||||
expect(normalizeBuildIdentity('/Users/example/private/build')).toBeNull();
|
||||
expect(normalizeBuildIdentity('a'.repeat(65))).toBeNull();
|
||||
|
||||
const info = createDesktopAppInfo('6.0.2', true, {
|
||||
buildIdentity: '/Users/example/private/build',
|
||||
osVersion: '15.5',
|
||||
});
|
||||
expect(info.buildIdentity).toBeNull();
|
||||
expect(formatDesktopVersionInfo(info)).not.toContain('/Users/');
|
||||
});
|
||||
|
||||
it('builds an offline native About panel from the same app information', () => {
|
||||
const info = createDesktopAppInfo('6.0.2', true, {
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
osVersion: '15.5',
|
||||
buildIdentity: 'abc1234',
|
||||
});
|
||||
|
||||
expect(createDesktopAboutPanelOptions(info)).toMatchObject({
|
||||
applicationName: 'Veritas Kanban',
|
||||
applicationVersion: '6.0.2',
|
||||
version: 'Build abc1234',
|
||||
credits: expect.stringContaining('Channel: stable'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -2,11 +2,10 @@ import type { IpcMain, Shell } from 'electron';
|
|||
import { lookup } from 'node:dns/promises';
|
||||
import { blockedRemoteConnectionDestinationReason } from '@veritas-kanban/shared';
|
||||
|
||||
import { DESKTOP_APP_ID, DESKTOP_APP_NAME } from './app-metadata.js';
|
||||
import type { DesktopCommandDispatcher } from './commands.js';
|
||||
import type { DesktopAppInfo } from './types.js';
|
||||
import type { DesktopRuntime } from './runtime.js';
|
||||
import type { DesktopUpdateService } from './updates.js';
|
||||
import { createDesktopAppInfo } from './version-info.js';
|
||||
import {
|
||||
createDesktopSetupDiagnostics,
|
||||
createDesktopSupportSnapshot,
|
||||
|
|
@ -204,17 +203,12 @@ export function createDesktopBridgeHandlers(
|
|||
runtime: DesktopRuntime,
|
||||
shell: Shell,
|
||||
packaged: boolean,
|
||||
appVersion: string,
|
||||
commandDispatcher?: DesktopCommandDispatcher,
|
||||
updateService?: DesktopUpdateService,
|
||||
windowControls?: DesktopWindowControls
|
||||
): DesktopBridgeHandlerMap {
|
||||
const appInfo = (): DesktopAppInfo => ({
|
||||
name: DESKTOP_APP_NAME,
|
||||
appId: DESKTOP_APP_ID,
|
||||
version: process.env.npm_package_version || '0.0.0',
|
||||
platform: process.platform,
|
||||
packaged,
|
||||
});
|
||||
const appInfo = () => createDesktopAppInfo(appVersion, packaged);
|
||||
|
||||
return {
|
||||
getAppInfo: appInfo,
|
||||
|
|
@ -243,7 +237,7 @@ export function createDesktopBridgeHandlers(
|
|||
updateService?.snapshot() ?? {
|
||||
state: 'unsupported',
|
||||
currentVersion: appInfo().version,
|
||||
channel: packaged ? 'stable' : 'dev',
|
||||
channel: appInfo().channel,
|
||||
checkedAt: new Date().toISOString(),
|
||||
detail: 'Updater service is not initialized.',
|
||||
},
|
||||
|
|
@ -304,6 +298,7 @@ export function registerDesktopBridge(
|
|||
runtime: DesktopRuntime,
|
||||
shell: Shell,
|
||||
packaged: boolean,
|
||||
appVersion: string,
|
||||
commandDispatcher?: DesktopCommandDispatcher,
|
||||
updateService?: DesktopUpdateService,
|
||||
windowControls?: DesktopWindowControls
|
||||
|
|
@ -312,6 +307,7 @@ export function registerDesktopBridge(
|
|||
runtime,
|
||||
shell,
|
||||
packaged,
|
||||
appVersion,
|
||||
commandDispatcher,
|
||||
updateService,
|
||||
windowControls
|
||||
|
|
@ -321,8 +317,7 @@ export function registerDesktopBridge(
|
|||
const definition = DESKTOP_BRIDGE_METHODS[method];
|
||||
const handler = handlers[method] as (request: unknown) => MaybePromise<unknown>;
|
||||
const validator = DESKTOP_BRIDGE_METHOD_VALIDATORS[method] as
|
||||
| ((payload: unknown) => unknown)
|
||||
| undefined;
|
||||
((payload: unknown) => unknown) | undefined;
|
||||
|
||||
ipcMain.handle(definition.channel, async (_event, request: unknown) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ function commandLabel(name: DesktopCommandName): string {
|
|||
return 'Settings';
|
||||
case 'open-command-center':
|
||||
return 'Command Center';
|
||||
case 'reset-layout':
|
||||
return 'Reset Window Layout';
|
||||
case 'import-data':
|
||||
return 'Import';
|
||||
case 'export-data':
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ import {
|
|||
ElectronAutoUpdaterAdapter,
|
||||
resolveDesktopUpdateChannel,
|
||||
} from './updates.js';
|
||||
import {
|
||||
createDesktopAboutPanelOptions,
|
||||
createDesktopAppInfo,
|
||||
formatDesktopVersionInfo,
|
||||
} from './version-info.js';
|
||||
import {
|
||||
DESKTOP_BRIDGE_EVENTS,
|
||||
redactDesktopBridgeValue,
|
||||
|
|
@ -174,9 +179,11 @@ function refreshDesktopMenu(): void {
|
|||
return;
|
||||
}
|
||||
|
||||
const appInfo = createDesktopAppInfo(app.getVersion(), launchPackaged);
|
||||
configureDesktopMenu({
|
||||
status: runtime.snapshot(),
|
||||
updateStatus: updateService?.snapshot(),
|
||||
copyVersionInfo: () => clipboard.writeText(formatDesktopVersionInfo(appInfo)),
|
||||
dispatch: (command) => {
|
||||
if (commandDispatcher) {
|
||||
dispatchDesktopMenuCommand(commandDispatcher, command);
|
||||
|
|
@ -186,10 +193,11 @@ function refreshDesktopMenu(): void {
|
|||
}
|
||||
|
||||
function updateServiceFallback(packaged: boolean): DesktopUpdateStatus {
|
||||
const appInfo = createDesktopAppInfo(app.getVersion(), packaged);
|
||||
return {
|
||||
state: 'unsupported',
|
||||
currentVersion: app.getVersion(),
|
||||
channel: packaged ? 'stable' : 'dev',
|
||||
currentVersion: appInfo.version,
|
||||
channel: appInfo.channel,
|
||||
checkedAt: new Date().toISOString(),
|
||||
detail: 'Updater service is not initialized.',
|
||||
};
|
||||
|
|
@ -200,6 +208,8 @@ async function boot(): Promise<void> {
|
|||
app.setAppUserModelId(DESKTOP_APP_ID);
|
||||
|
||||
const packaged = launchPackaged;
|
||||
const appInfo = createDesktopAppInfo(app.getVersion(), packaged);
|
||||
app.setAboutPanelOptions(createDesktopAboutPanelOptions(appInfo));
|
||||
const repoRoot = launchRepoRoot;
|
||||
const profile = launchProfile;
|
||||
const workspace = launchWorkspace;
|
||||
|
|
@ -309,20 +319,29 @@ async function boot(): Promise<void> {
|
|||
},
|
||||
});
|
||||
|
||||
registerDesktopBridge(ipcMain, runtime, shell, packaged, commandDispatcher, updateService, {
|
||||
toggleMaximize: () => {
|
||||
const window = activeMainWindow();
|
||||
if (!window) {
|
||||
return { maximized: false };
|
||||
}
|
||||
if (window.isMaximized()) {
|
||||
window.unmaximize();
|
||||
} else {
|
||||
window.maximize();
|
||||
}
|
||||
return { maximized: window.isMaximized() };
|
||||
},
|
||||
});
|
||||
registerDesktopBridge(
|
||||
ipcMain,
|
||||
runtime,
|
||||
shell,
|
||||
packaged,
|
||||
app.getVersion(),
|
||||
commandDispatcher,
|
||||
updateService,
|
||||
{
|
||||
toggleMaximize: () => {
|
||||
const window = activeMainWindow();
|
||||
if (!window) {
|
||||
return { maximized: false };
|
||||
}
|
||||
if (window.isMaximized()) {
|
||||
window.unmaximize();
|
||||
} else {
|
||||
window.maximize();
|
||||
}
|
||||
return { maximized: window.isMaximized() };
|
||||
},
|
||||
}
|
||||
);
|
||||
refreshDesktopMenu();
|
||||
runtime.on('status', (status) => {
|
||||
activeMainWindow()?.webContents.send(DESKTOP_BRIDGE_EVENTS.serverStatus.channel, status);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import type {
|
|||
|
||||
export interface ConfigureDesktopMenuOptions {
|
||||
dispatch(command: DesktopCommandName): void;
|
||||
copyVersionInfo(): void;
|
||||
status: DesktopStatusSnapshot;
|
||||
updateStatus?: DesktopUpdateStatus;
|
||||
}
|
||||
|
|
@ -38,6 +39,13 @@ export function createDesktopMenuTemplate(
|
|||
{
|
||||
label: 'Veritas Kanban',
|
||||
submenu: [
|
||||
{ role: 'about', label: 'About Veritas Kanban' },
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Copy Version Information',
|
||||
click: () => options.copyVersionInfo(),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
command('open-onboarding'),
|
||||
command('open-settings'),
|
||||
command('communication-health'),
|
||||
|
|
@ -61,7 +69,13 @@ export function createDesktopMenuTemplate(
|
|||
{ role: 'editMenu' },
|
||||
{
|
||||
label: 'Navigate',
|
||||
submenu: [command('open-command-center'), command('open-search'), command('open-settings')],
|
||||
submenu: [
|
||||
command('open-command-center'),
|
||||
command('open-search'),
|
||||
command('open-settings'),
|
||||
{ type: 'separator' },
|
||||
command('reset-layout'),
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Desktop',
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ export interface DesktopAppInfo {
|
|||
name: string;
|
||||
appId: string;
|
||||
version: string;
|
||||
buildIdentity: string | null;
|
||||
channel: 'dev' | 'beta' | 'stable';
|
||||
platform: NodeJS.Platform;
|
||||
arch: string;
|
||||
osVersion: string;
|
||||
packaged: boolean;
|
||||
}
|
||||
|
|
|
|||
102
desktop/src/main/version-info.ts
Normal file
102
desktop/src/main/version-info.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import os from 'node:os';
|
||||
|
||||
import { DESKTOP_APP_ID, DESKTOP_APP_NAME } from './app-metadata.js';
|
||||
import type { DesktopAppInfo } from './types.js';
|
||||
import { resolveDesktopUpdateChannel } from './updates.js';
|
||||
|
||||
declare const __VERITAS_BUILD_SHA__: string | undefined;
|
||||
declare const __VERITAS_RELEASE_CHANNEL__: string | undefined;
|
||||
|
||||
export interface DesktopAppInfoOverrides {
|
||||
platform?: NodeJS.Platform;
|
||||
arch?: string;
|
||||
osVersion?: string;
|
||||
requestedChannel?: string;
|
||||
buildIdentity?: string | null;
|
||||
}
|
||||
|
||||
function embeddedBuildIdentity(): string | null {
|
||||
const value = typeof __VERITAS_BUILD_SHA__ === 'string' ? __VERITAS_BUILD_SHA__ : undefined;
|
||||
return normalizeBuildIdentity(value);
|
||||
}
|
||||
|
||||
function embeddedReleaseChannel(): string | undefined {
|
||||
return typeof __VERITAS_RELEASE_CHANNEL__ === 'string' && __VERITAS_RELEASE_CHANNEL__.trim()
|
||||
? __VERITAS_RELEASE_CHANNEL__
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export function normalizeBuildIdentity(value: string | undefined | null): string | null {
|
||||
const normalized = value?.trim();
|
||||
if (!normalized || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(normalized)) {
|
||||
return null;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function resolveSystemVersion(platform: NodeJS.Platform): string {
|
||||
if (platform === 'darwin') {
|
||||
const electronProcess = process as NodeJS.Process & { getSystemVersion?: () => string };
|
||||
const systemVersion = electronProcess.getSystemVersion?.();
|
||||
if (systemVersion?.trim()) return systemVersion.trim();
|
||||
}
|
||||
return os.release();
|
||||
}
|
||||
|
||||
export function createDesktopAppInfo(
|
||||
version: string,
|
||||
packaged: boolean,
|
||||
overrides: DesktopAppInfoOverrides = {}
|
||||
): DesktopAppInfo {
|
||||
const platform = overrides.platform ?? process.platform;
|
||||
const buildIdentity =
|
||||
overrides.buildIdentity === undefined
|
||||
? embeddedBuildIdentity()
|
||||
: normalizeBuildIdentity(overrides.buildIdentity);
|
||||
return {
|
||||
name: DESKTOP_APP_NAME,
|
||||
appId: DESKTOP_APP_ID,
|
||||
version,
|
||||
buildIdentity,
|
||||
channel: resolveDesktopUpdateChannel(
|
||||
overrides.requestedChannel ?? embeddedReleaseChannel() ?? process.env.VERITAS_UPDATE_CHANNEL,
|
||||
version,
|
||||
packaged
|
||||
),
|
||||
platform,
|
||||
arch: overrides.arch ?? process.arch,
|
||||
osVersion: overrides.osVersion ?? resolveSystemVersion(platform),
|
||||
packaged,
|
||||
};
|
||||
}
|
||||
|
||||
function platformLabel(platform: NodeJS.Platform): string {
|
||||
if (platform === 'darwin') return 'macOS';
|
||||
if (platform === 'win32') return 'Windows';
|
||||
if (platform === 'linux') return 'Linux';
|
||||
return platform;
|
||||
}
|
||||
|
||||
export function formatDesktopVersionInfo(info: DesktopAppInfo): string {
|
||||
const lines = [`${info.name} ${info.version}`];
|
||||
if (info.buildIdentity) {
|
||||
lines.push(`Build: ${info.buildIdentity}`);
|
||||
} else if (!info.packaged) {
|
||||
lines.push('Build: development');
|
||||
}
|
||||
lines.push(
|
||||
`Channel: ${info.channel}`,
|
||||
`${platformLabel(info.platform)} ${info.osVersion} · ${info.arch}`
|
||||
);
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export function createDesktopAboutPanelOptions(info: DesktopAppInfo) {
|
||||
const supportLines = formatDesktopVersionInfo(info).split('\n').slice(1);
|
||||
return {
|
||||
applicationName: info.name,
|
||||
applicationVersion: info.version,
|
||||
version: info.buildIdentity ? `Build ${info.buildIdentity}` : `Channel ${info.channel}`,
|
||||
credits: supportLines.join('\n'),
|
||||
};
|
||||
}
|
||||
|
|
@ -90,6 +90,7 @@ export const DESKTOP_COMMAND_NAMES = [
|
|||
'open-search',
|
||||
'open-settings',
|
||||
'open-command-center',
|
||||
'reset-layout',
|
||||
'import-data',
|
||||
'export-data',
|
||||
'create-backup',
|
||||
|
|
|
|||
|
|
@ -5,5 +5,21 @@ export default defineConfig({
|
|||
environment: 'node',
|
||||
include: ['src/**/*.test.ts'],
|
||||
exclude: ['dist/**', 'out/**', 'node_modules/**'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: [
|
||||
'src/**/*.test.ts',
|
||||
'src/**/*.d.ts',
|
||||
'src/**/__tests__/**',
|
||||
'src/**/__fixtures__/**',
|
||||
'src/**/fixtures/**',
|
||||
'src/**/generated/**',
|
||||
'src/**/*.generated.*',
|
||||
'src/**/types.ts',
|
||||
'src/types/**/*.ts',
|
||||
],
|
||||
all: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ services:
|
|||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: veritas-kanban-demo
|
||||
# IMPORTANT: Must match Dockerfile WORKDIR (/app/server) for correct path resolution
|
||||
# Kept aligned with the image entrypoint; persistent paths resolve from DATA_DIR.
|
||||
working_dir: /app/server
|
||||
ports:
|
||||
# Demo instance port (do NOT use production 3001)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ Readiness runs bounded `claude --version`, `claude auth status`, and
|
|||
`claude agents --json` probes without a shell. The auth status probe is useful
|
||||
diagnostic evidence, but only the explicit bare-mode environment satisfies
|
||||
launch authentication. The runtime profile requires an exact
|
||||
`2.1.218 (Claude Code)` version match and probe revision 14; version or build
|
||||
`2.1.218 (Claude Code)` version match and probe revision 16; version or build
|
||||
drift invalidates conformance evidence.
|
||||
|
||||
Claude's stream is consumed as bounded JSONL. Partial text/thinking, tool
|
||||
|
|
@ -192,7 +192,7 @@ starts it without a shell in the assigned task worktree.
|
|||
Before changing attempt state, Veritas starts a bounded probe process and sends
|
||||
ACP `initialize` with protocol version `1`. The returned agent name, version,
|
||||
capabilities, and a deterministic capability digest become
|
||||
`provider-runtime-manifest/v1` evidence at probe revision 14. Launch starts a
|
||||
`provider-runtime-manifest/v1` evidence at probe revision 16. Launch starts a
|
||||
fresh process and rejects capability drift before opening or prompting a
|
||||
session.
|
||||
|
||||
|
|
@ -538,6 +538,8 @@ close. Every accepted action has auth-derived attribution and a causal
|
|||
gate; any recorded-only fallback returns `delivered: false`. Generic process
|
||||
stdin is not treated as provider delivery.
|
||||
|
||||
Workspace checkpoint rewind is deliberately narrower than ordinary conversation fork. `POST /api/agents/:taskId/workspace/checkpoints/rewind` is preview-first, requires exact critical approval, and is available only for an active Codex app-server attempt whose target checkpoint names an earlier exact turn in the same thread. Operators may resolve an attribution conflict per path with `accept`, `reject`, or `leave-untouched`; the selected paths and canonical decisions are digest-bound through approval, transaction, and recovery. Veritas interrupts the current turn, commits only the approved workspace paths, forks the approved provider history, and records the new live cursor plus its checkpoint anchor. Other adapters, ambiguous or item-level cursors, unresolved non-attribution conflicts, and changed current-state evidence fail closed.
|
||||
|
||||
Agents and supervisors can register the same validated manifest with
|
||||
`POST /api/agents/register` and refresh it through the heartbeat endpoint. Host
|
||||
provider, model, `tool.*`, and sandbox posture is derived only from those
|
||||
|
|
@ -579,6 +581,8 @@ to 128 MiB. Payloads are recursively redacted, strings and collections are
|
|||
bounded, and payloads larger than 32 KiB are replaced by explicit dropped
|
||||
metadata before persistence.
|
||||
|
||||
Launch previews expose current redacted provider and selected-host dependency circuit posture. `run.started`, launch-failure, and terminal journal records retain the corresponding `run-dependency-circuit-evidence/v1` snapshot; run telemetry carries compact state counts; and normalized completion results add verified harness evidence for both circuit identities. These records contain opaque dependency IDs and policy/state metrics, never endpoint URLs, request bodies, response bodies, or credentials.
|
||||
|
||||
The TypeScript contract is in `shared/src/types/run-event.types.ts`; the
|
||||
portable schema is `shared/schemas/run-event-envelope.v1.schema.json`.
|
||||
Consumers replay with
|
||||
|
|
@ -634,6 +638,11 @@ CLI, Codex SDK, Codex app-server, Claude Code, and Hermes renderers all include
|
|||
the envelope digest, runtime identity, objective and bounded context, a bounded
|
||||
workspace-baseline summary, explicit commit policy, allowed side effects,
|
||||
expected outputs, verification gates, and completion evidence contract.
|
||||
When reviewed reflection lessons are relevant, every renderer also receives
|
||||
the same bounded **Accepted Memory** section. Each entry includes its stable
|
||||
reflection ID and source task, run, and event attribution. Harnesses should
|
||||
treat these as human-reviewed supporting guidance and preserve the identifiers
|
||||
when reporting whether a lesson helped or regressed the run.
|
||||
Profile instructions and saved task checkpoints are rendered as separate,
|
||||
attributed sections and are capped at 20,000 characters each. The persisted
|
||||
task envelope retains the complete baseline fingerprints used for later
|
||||
|
|
@ -672,6 +681,14 @@ Callback and remote-session terminal sources are accepted only for OpenClaw.
|
|||
CLI process and SDK stream providers reject callback transport even when an
|
||||
attempt ID and manifest digest are known.
|
||||
|
||||
Terminal task mutation crosses one `AttemptLifecycleCoordinator` seam. The
|
||||
coordinator validates the persisted runtime, envelope, and optional launch
|
||||
manifest bindings; enforces active-attempt ownership; retries bounded task
|
||||
revision conflicts; updates current and historical attempt state together;
|
||||
and treats only the exact persisted idempotency key as a safe duplicate.
|
||||
Provider adapters and restart recovery prepare evidence but cannot implement a
|
||||
parallel terminal persistence path.
|
||||
|
||||
Provider summaries, evidence, artifacts, and verification claims are bounded,
|
||||
redacted, and stored as unverified provider evidence. Veritas independently
|
||||
captures Git HEAD, post-launch files and commits, task verification state,
|
||||
|
|
@ -724,9 +741,9 @@ its task contract, and records the selected provider/model/transport, redacted
|
|||
command and arguments, instruction fingerprints and precedence, environment
|
||||
key names and broker references, profile tools/MCP/permissions/health checks,
|
||||
sandbox/network posture, readiness and any hashed operator override, budget,
|
||||
routing/fallback, workspace trust, and the origin of each effective value.
|
||||
Prompt content, override text, and credential values are never stored in this
|
||||
manifest.
|
||||
routing/fallback, workspace trust, compiled phase evidence and source
|
||||
references, and the origin of each effective value. Prompt content, override
|
||||
text, and credential values are never stored in this manifest.
|
||||
|
||||
`POST /api/agents/:taskId/launch-preview` returns the same compiled contract
|
||||
without creating an attempt or dispatching a process. The CLI equivalent is
|
||||
|
|
@ -736,6 +753,31 @@ cannot enforce is returned as a concrete blocker, and `start` rejects it before
|
|||
pending or task attempt state changes. Declaring `tool.calls` support is not
|
||||
treated as proof that an adapter can enforce a named allowlist.
|
||||
|
||||
Phase authority is resolved at the same pre-mutation boundary for task and
|
||||
workflow launches, retries, fallbacks, conversation continuations, compaction
|
||||
controls, and provider handoffs. A descendant intersects the exact parent
|
||||
launch or current transition evidence, so changing providers or omitting an
|
||||
explicit phase cannot widen authority. Explicit phase launches fail closed with
|
||||
typed blockers when the runtime, sandbox, host, or tool policy cannot prove a
|
||||
required dimension. Tool-command and external-action enforcement support is
|
||||
delivered separately in #1033; prompt text is never accepted as enforcement.
|
||||
|
||||
Repository-controlled instructions and executable configuration pass through
|
||||
the provider-neutral workspace execution trust gate before Veritas reads
|
||||
repository instructions or creates an attempt. The gate fingerprints the exact
|
||||
task worktree, inventories recognized instructions, hooks, MCP servers,
|
||||
provider overrides, language-server settings, workflows, extensions, skills,
|
||||
and agent definitions, and evaluates them against an actor-attributed operator
|
||||
decision. Executable configuration requires explicit authorization. Model-only
|
||||
instructions may run provisionally only in enforced restricted mode.
|
||||
|
||||
The immutable launch manifest records redacted identity and inventory evidence,
|
||||
the effective decision, project maximum, requested capabilities, and
|
||||
restriction checks. Veritas rescans immediately before provider creation; an
|
||||
identity, inventory, or decision change aborts the launch. Repository policy
|
||||
can only narrow operator trust. See
|
||||
[Workspace Execution Trust](architecture/WORKSPACE-EXECUTION-TRUST.md).
|
||||
|
||||
Codex app-server and Claude Code inject a positive MCP catalog through their
|
||||
native run-scoped configuration. Other task adapters reject non-empty MCP
|
||||
selections. All adapters continue to reject named-tool restrictions they
|
||||
|
|
@ -782,13 +824,74 @@ Presets can be assigned to:
|
|||
- A workflow agent, as the guardrail for that workflow role.
|
||||
- A one-off agent start request, by passing `sandboxPresetId`.
|
||||
|
||||
The launch path dry-runs the selected preset before starting Codex CLI, Codex
|
||||
SDK, Claude Code, or OpenClaw-backed work. Required controls fail closed when
|
||||
the provider cannot support them. Advisory controls continue with warnings and
|
||||
a governance trace. Settings also includes a dry-run panel that shows effective
|
||||
The launch path dry-runs the selected preset before starting any executable
|
||||
provider adapter. Required controls fail closed when the provider or execution
|
||||
host cannot support them. Advisory controls continue with warnings and a
|
||||
governance trace. Settings also includes a dry-run panel that shows effective
|
||||
sandbox mode, network access, environment allowlist, unsupported controls, and
|
||||
the trace ID.
|
||||
|
||||
Selective network policies for local task and workflow providers start an
|
||||
authenticated, run-scoped loopback gateway before dispatch. Durable launch and
|
||||
gateway evidence records only the injected proxy key names and policy digest;
|
||||
tokens and proxy URLs are never persisted. HTTP, HTTPS CONNECT, and plaintext
|
||||
WebSocket transports use the evaluated DNS address. `ALL_PROXY` exposes a
|
||||
separate authenticated `socks5h` listener with the same host, address, approval,
|
||||
and audit policy; encrypted SOCKS tunnels fail closed when method or path
|
||||
inspection would be required. Both listeners stop during terminal cleanup.
|
||||
OpenClaw is provider-managed and cannot receive this local boundary, so a
|
||||
required fine-grained policy blocks its launch.
|
||||
|
||||
Operators that require a corporate or audited proxy can set
|
||||
`VERITAS_EGRESS_UPSTREAM_PROXY` to an HTTP proxy origin. The gateway evaluates
|
||||
the destination first, pins the resolved address, and then opens an upstream
|
||||
CONNECT tunnel to that address. Proxy credentials remain memory-only; durable
|
||||
evidence records only `upstreamMode: http-connect`.
|
||||
|
||||
When a preset enables scoped approvals, an otherwise eligible block creates a
|
||||
durable, exact-action network approval and holds the request at the gateway.
|
||||
Only an approved request proceeds. Explicit host denies and protected address
|
||||
classes are never approval-eligible. Gateway shutdown aborts pending waits.
|
||||
Every final decision also emits a `network.egress` telemetry record containing
|
||||
the hashed host key, run key, policy digest, protocol, port, decision, reason,
|
||||
and approval ID when present. URLs, query strings, headers, bodies, proxy
|
||||
credentials, and raw paths are excluded from telemetry.
|
||||
|
||||
Local ACP, Claude Code, Codex app-server, Codex CLI, and Hermes processes use a
|
||||
version-bound `codex sandbox` wrapper when its credential-free conformance
|
||||
probe passes. The wrapper enforces the compiled read, write, deny, dotfile, and
|
||||
protected-metadata rules before the provider starts and applies to descendant
|
||||
processes. Codex SDK and remote OpenClaw runs cannot use that outer process
|
||||
wrapper. They satisfy required filesystem policies only when the exact
|
||||
provider runtime manifest proves every active filesystem capability, including
|
||||
descendant inheritance, run-scoped temporary storage, and cleanup. Coarse
|
||||
provider modes such as `workspace-write` remain advisory.
|
||||
|
||||
Workspace and home aliases cannot escape their canonical base. Nested mounts
|
||||
below an allowed root are denied unless explicitly granted or denied, and the
|
||||
mount topology is rechecked before activation. Provider-native local
|
||||
enforcement blocks if Veritas finds an ambiguous nested mount that it cannot
|
||||
add to the native policy. The bounded workspace tree is also scanned before
|
||||
compilation and immediately before activation for pre-existing hard links that
|
||||
alias inaccessible external inodes.
|
||||
|
||||
The immutable launch manifest records the backend, versioned capability
|
||||
contract, executable-content digest, provider runtime manifest digest, policy
|
||||
hash, and only hashes of canonical paths. `.git`, `.agents`, `.codex`, and
|
||||
`.veritas-kanban` remain explicitly read-only directly beneath writable roots,
|
||||
and cannot themselves be selected as writable policy roots. Local wrapper and
|
||||
provider-native runs use supervisor-owned temporary and cache directories;
|
||||
remote native backends must prove their own run-scoped storage and cleanup
|
||||
contract. See
|
||||
[Run-scoped filesystem sandbox backends](architecture/FILESYSTEM-SANDBOX-BACKENDS.md)
|
||||
for the enforcement and failure contract.
|
||||
|
||||
There is no per-run override for a required filesystem boundary. The
|
||||
`overrideReason` launch field applies only to task readiness. Intentionally
|
||||
relaxing filesystem enforcement requires an authorized advisory preset, and
|
||||
the effective decision remains recorded in policy and launch governance
|
||||
evidence.
|
||||
|
||||
Credential references and environment-style `name=value` values are redacted from dry-run output and governance traces. Prefer brokered credential presets for workflows that need scoped secrets instead of exposing broad environment passthrough.
|
||||
|
||||
Credential definitions are admin-managed at `/api/credential-broker`. Records
|
||||
|
|
@ -863,6 +966,190 @@ Use **Settings -> Agents** and **Settings -> Data & Storage -> Budget Tracking**
|
|||
|
||||
Soft thresholds create `budget-policy` governance traces and visible warnings. Hard thresholds can pause for review, require approval, downgrade to a configured model, or cancel the run. Completion packets include the final budget decision, usage, threshold events, related trace IDs, and operator override notes when present.
|
||||
|
||||
## Execution Admission
|
||||
|
||||
Every direct task launch, workflow root, and provider-backed workflow step
|
||||
receives a durable `admission-reservation/v1` record before Veritas persists
|
||||
an executable attempt or calls a provider adapter. Workflow roots use the
|
||||
explicit `workflow-control` admission provider and reserve no provider process
|
||||
or memory estimate. Each executable child step uses its resolved provider,
|
||||
selected host, workflow run, step, and root reservation. Reservations account
|
||||
for run slots, process slots, and operator-estimated memory across optional
|
||||
global, workspace, root-task, provider, and host ceilings. Estimated memory is
|
||||
a configured planning value, not a runtime memory prediction. An invariant
|
||||
one-run-per-admission-task policy also prevents concurrent duplicate launches
|
||||
across server processes.
|
||||
|
||||
Configure ceilings through `PATCH /api/settings/features`:
|
||||
|
||||
```json
|
||||
{
|
||||
"admission": {
|
||||
"global": {
|
||||
"concurrentRuns": 8,
|
||||
"processSlots": 8,
|
||||
"estimatedMemoryMb": 16384
|
||||
},
|
||||
"providers": {
|
||||
"codex-cli": {
|
||||
"concurrentRuns": 4
|
||||
},
|
||||
"workflow-control": {
|
||||
"concurrentRuns": 4
|
||||
}
|
||||
},
|
||||
"hosts": {
|
||||
"local-process": {
|
||||
"processSlots": 6
|
||||
}
|
||||
},
|
||||
"queue": {
|
||||
"enabled": true,
|
||||
"globalLimit": 1000,
|
||||
"workspaceLimit": 100,
|
||||
"leaseMs": 30000,
|
||||
"retryBackoffMs": 5000,
|
||||
"maxRetries": 3,
|
||||
"scheduler": {
|
||||
"priorityLevels": 4,
|
||||
"defaultPriority": 1,
|
||||
"agingIntervalMs": 60000,
|
||||
"maxAgePromotion": 3,
|
||||
"workspaceBurstLimit": 4,
|
||||
"evaluationLimit": 32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An individual request larger than a ceiling receives a terminal policy denial.
|
||||
Queueable temporary exhaustion persists one bounded
|
||||
`admission-queue-entry/v1` record. Direct, profile, conversation,
|
||||
provider-handoff, child-agent, retry, and fallback starts return
|
||||
`status: "queued"`, `queueId`, the reserved `attemptId`, `retryAfterMs`, and
|
||||
redacted limiting scopes. Harnesses must treat that response as accepted work,
|
||||
not as a failed attempt, and must not submit a duplicate start. Workflow roots
|
||||
and provider-backed workflow steps instead persist `state: "waiting"` in their
|
||||
admission binding while the run and step remain `pending`. Scheduled workflows
|
||||
retain `source: "scheduled"`; queue-monitor continuations retain
|
||||
`source: "watcher"`.
|
||||
|
||||
The queue uses the versioned `admission-queue-scheduler/v1` policy for
|
||||
agent-launch, legacy direct, workflow-root, and workflow-step targets. It ranks
|
||||
the bounded eligible queue snapshot by task priority, configured age
|
||||
promotion, workspace turns, enqueue sequence, and queue identity. After
|
||||
`workspaceBurstLimit` consecutive selections, compatible work from another
|
||||
workspace receives the next turn. Age promotion is capped at the highest
|
||||
configured priority, and `maxAgePromotion` must let the lowest priority
|
||||
eventually reach that level. Provider and host limits only determine capacity
|
||||
readiness; they cannot override durable ordering or create a hidden provider
|
||||
queue.
|
||||
|
||||
A worker tries ranked entries until it atomically claims both a queue lease and
|
||||
admission capacity. A capacity-blocked entry stays queued while another ready
|
||||
entry may run. The leased entry persists redacted
|
||||
`admission-queue-selection/v1` evidence with raw and effective priority, age
|
||||
promotion, workspace turn, readiness, limiting scopes for skipped entries, and
|
||||
conditional start factors. The evidence never promises an exact start time.
|
||||
The worker then reruns provider, sandbox, budget, workspace trust, and
|
||||
launch-manifest checks before any attempt is persisted. Durable supervisor
|
||||
ownership changes the entry to `dispatched`; after that point only run recovery
|
||||
may restart or finish the work. Abandoned pre-dispatch leases requeue with
|
||||
bounded backoff. Terminal drift, retry exhaustion, and queue overflow fail
|
||||
closed. Queue records retain task, workspace, agent, execution tree, workflow
|
||||
version and revision, retry or fallback sequence, provider, host, immutable
|
||||
runtime and phase digests, and limiting-scope evidence. Workflow targets never
|
||||
retain prompts, workflow context, tool arguments, or credentials. Agent-launch
|
||||
targets retain only bounded provider-neutral inputs required for exact replay,
|
||||
which may include an operator turn or override reason. Queue inspection,
|
||||
telemetry, Operations, and support bundles omit those private inputs. No target
|
||||
retains credentials, process handles, leases, or raw idempotency keys.
|
||||
|
||||
Every built-in and custom provider adapter receives
|
||||
`provider-admission-evidence/v1` immediately before dispatch. The evidence binds
|
||||
the shared reservation, launch source, queue-dispatch outcome, and exact
|
||||
execution-tree identity to the durable attempt. Missing or inconsistent
|
||||
evidence fails before the adapter is called, so an adapter cannot widen
|
||||
capacity or substitute an external hidden queue.
|
||||
|
||||
### Provider adapter lifecycle ownership
|
||||
|
||||
`server/src/services/agent-provider-adapter-registry.ts` is the executable
|
||||
provider-selection authority. Its `resolve(provider, surface)` interface owns
|
||||
the exact adapter identity, task-envelope renderer, runtime probe, run-event
|
||||
mapper, start dispatch, and stop behavior for every executable provider.
|
||||
Unknown or non-executable providers fail before this seam; there is no implicit
|
||||
OpenClaw fallback.
|
||||
|
||||
`ClawdbotAgentService` remains the shared run orchestrator. It supplies
|
||||
admission, supervisor, sandbox, budget, journal, and completion effects to the
|
||||
registry host without duplicating provider selection. Full attempt mutations
|
||||
cross `AttemptLifecycleCoordinator`, which verifies active-attempt ownership,
|
||||
optimistic revisions, history maintenance, and terminal completion binding.
|
||||
Provider adapters never write attempt state directly.
|
||||
|
||||
Active leases renew while the verified run is live; completion, interruption,
|
||||
cancellation, or launch failure releases the reservation idempotently.
|
||||
Workflow retry and fallback attempts release the prior step reservation before
|
||||
queueing or acquiring the replacement. After restart, task runs use their
|
||||
durable supervisor and workflow runs use the exact persisted root and step
|
||||
bindings before reclaiming a reservation. Roots and steps interrupted after
|
||||
durable queue dispatch but before provider execution resume exactly once.
|
||||
Unverified provider work that was already running remains blocked for operator
|
||||
reconciliation. Caller-supplied idempotency values are represented by a stable
|
||||
SHA-256 identity in durable records; the original value is not stored.
|
||||
|
||||
Every reservation also carries a versioned execution-tree identity. Resume,
|
||||
follow-up, fork, retry, fallback, provider handoff, workflow-step, and
|
||||
child-agent launches preserve the same root objective and exact parent edge.
|
||||
The capacity claim and strictest applicable workspace, agent, workflow, run,
|
||||
and root-objective budget claim share one atomic repository operation.
|
||||
Idempotent node usage events convert reserved tokens, cost, tool calls,
|
||||
runtime, idle time, retries, and fan-out into committed attribution. Release
|
||||
returns only unused reservation; committed usage remains in the tree.
|
||||
|
||||
Inspect reservations with `vk admission list`, `vk admission get <id>`, or the
|
||||
matching read-only REST endpoints. Use `--workflow-run`, `--workflow-step`,
|
||||
`--root-reservation`, or `--root-objective` to follow a tree. Use
|
||||
`vk admission tree <root-objective-id>` for aggregate totals, remaining policy
|
||||
capacity, blocking policies, bounded contributors, and durable cancellation
|
||||
or circuit-breaker control. Machine consumers should use `--json`. Inspect the
|
||||
conditional, redacted queue view with
|
||||
`vk admission queue list`, `vk admission queue get <queue-id>`, or the matching
|
||||
`/api/v1/admission/queue` endpoints. Queue position is evidence at the response
|
||||
generation time, never an exact start-time promise. Machine consumers may use
|
||||
the safe `navigation` identifiers to open related task, attempt, workflow, and
|
||||
execution-tree views without accessing the redacted launch payload.
|
||||
|
||||
Operators can stop a queued launch with `vk admission queue cancel <queue-id>
|
||||
--reason <text>` or cancel an entire execution tree with `vk admission
|
||||
cancel-tree <root-objective-id> --reason <text>`. Supply
|
||||
`--idempotency-key <key>` when an automation may retry the request. Tree
|
||||
cancellation is recorded on the root reservation before descendants are
|
||||
drained, so harness-driven resume, retry, fallback, workflow-step, and
|
||||
child-agent launches fail closed before any provider adapter runs. The command
|
||||
reports verified running attempts that the local supervisor could not
|
||||
interrupt; those require explicit operator reconciliation.
|
||||
|
||||
Veritas also applies one provider-neutral fan-out circuit breaker before every
|
||||
tree expansion. The default guard pauses a root at 256 descendants, depth 16,
|
||||
64 active reservations, 64 queued descendants, or 95 percent capacity/budget
|
||||
pressure once the tree has eight descendants. A paused decision includes
|
||||
bounded `execution-tree-breaker-evidence/v1` signals and returns
|
||||
`EXECUTION_TREE_EXPANSION_PAUSED`. Codex, Claude Code, Buzz, Grok Build, GitHub
|
||||
Copilot, Hermes, OpenClaw, and custom harnesses must treat that outcome as a
|
||||
durable stop signal, not transient provider throttling: do not start a local
|
||||
retry storm, change providers, or create a fresh root to evade it.
|
||||
|
||||
Inspect the root with `vk admission tree <root-objective-id>` or Operations.
|
||||
After the reported active, queued, capacity, or budget pressure clears, an
|
||||
administrator can run `vk admission resume-tree <root-objective-id> --reason
|
||||
<text>`. Veritas re-evaluates the same durable evidence after restart and
|
||||
rejects unsafe resumes with `EXECUTION_TREE_RESUME_BLOCKED`. A successful
|
||||
resume preserves the breaker history for audit and allows subsequent launches;
|
||||
tree cancellation remains terminal.
|
||||
|
||||
## Local And Cloud Profiles
|
||||
|
||||
| Profile | Provider | Default command | Auth / readiness |
|
||||
|
|
@ -907,6 +1194,34 @@ Recommended starting point:
|
|||
3. Enable `ollama-cloud` only when the workflow is allowed to leave local execution.
|
||||
4. Use explicit routing rules for local LLM profiles instead of making them global defaults on teams with mixed operating systems.
|
||||
|
||||
### Automatic retry and fallback
|
||||
|
||||
`agentRouting.maxRetries` and `agentRouting.fallbackOnFailure` are captured in
|
||||
each new run launch manifest. Task attempts and workflow steps use the same
|
||||
durable `run-recovery/v1` decision record:
|
||||
|
||||
- Only transient transport, provider unavailable, rate-limit, timeout, and
|
||||
verification failures are automatically retryable.
|
||||
- Invalid configuration and policy blocks require operator action. Explicit
|
||||
cancellation stops recovery. A failed or partial run with destructive side
|
||||
effects requires approval before another launch.
|
||||
- Retry backoff is exponential, capped at 30 seconds, and jittered by 20
|
||||
percent. The attempt chain retains its root and parent IDs, route, source and
|
||||
launched manifest digests, and cumulative budget.
|
||||
- A fallback is launched only after retry exhaustion and only after its runtime
|
||||
capabilities and sandbox policy pass the normal launch preflight. An
|
||||
incompatible fallback produces an actionable exhausted handoff.
|
||||
- Scheduled task and workflow recovery is reconciled after server restart.
|
||||
Revision claims and terminal idempotency prevent duplicate callbacks from
|
||||
starting multiple branches.
|
||||
|
||||
Inspect a task decision with `vk agent:recovery <task> --json`. Cancel the
|
||||
exact pending parent with
|
||||
`vk agent:cancel-recovery <task> --attempt <attempt-id>`. MCP clients use
|
||||
`cancel_agent_recovery` with the same task and parent attempt. REST clients use
|
||||
`GET /api/agents/:taskId/recovery` and
|
||||
`POST /api/agents/:taskId/recovery/cancel`.
|
||||
|
||||
---
|
||||
|
||||
## Hermes Agent (v2026.7.7.2)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ The Agent Registry is a service discovery and liveness tracking system for AI ag
|
|||
| **Persistence** | File-backed JSON survives server restarts |
|
||||
| **Dashboard** | Live agent cards in the board sidebar |
|
||||
|
||||
**Storage:** `.veritas-kanban/agent-registry.json`
|
||||
**Storage:** `<storage-root>/.veritas-kanban/agent-registry.json`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -406,13 +406,14 @@ The panel reads from the registry API and updates every 30 seconds (plus WebSock
|
|||
| ------------------------- | ------------------ | -------------------------------------------- |
|
||||
| `HEARTBEAT_TIMEOUT_MS` | 300,000 (5 min) | Time before marking agent offline |
|
||||
| `STALE_CHECK_INTERVAL_MS` | 60,000 (1 min) | How often the server checks for stale agents |
|
||||
| `VERITAS_DATA_DIR` | `.veritas-kanban/` | Directory for registry JSON file |
|
||||
| `VERITAS_DATA_DIR` | Project root | Storage root used when `DATA_DIR` is unset |
|
||||
|
||||
---
|
||||
|
||||
## File Format
|
||||
|
||||
The registry is stored as JSON at `.veritas-kanban/agent-registry.json`:
|
||||
The registry is stored as JSON at
|
||||
`<storage-root>/.veritas-kanban/agent-registry.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,225 +1,279 @@
|
|||
# AGENTS.md Template — Veritas Kanban Self-Reporting Protocol
|
||||
# Agent Guide and `AGENTS.md` Template
|
||||
|
||||
Use this template for agents that integrate with Veritas Kanban. Copy it into your agent's workspace and fill in the sections.
|
||||
Use this guide when an agent needs to work through Veritas Kanban. Start by
|
||||
choosing the correct integration mode. Managed harnesses and external
|
||||
self-reporting agents have different lifecycle responsibilities.
|
||||
|
||||
---
|
||||
## Choose the integration mode
|
||||
|
||||
| Mode | Use it when | Who owns task lifecycle |
|
||||
| ----------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------- |
|
||||
| Managed harness | VK launches Buzz Agent, Grok Build, Codex, Claude Code, GitHub Copilot CLI, Hermes, or OpenClaw | VK and the selected adapter |
|
||||
| External self-reporting agent | A separate process registers itself and calls VK APIs directly | The external agent |
|
||||
| Unmanaged MCP client | An assistant only needs typed VK tools and is not being launched as the task runner | The MCP client and its operator |
|
||||
|
||||
Do not combine the managed and self-reporting paths. A managed run must not
|
||||
register itself, send heartbeats, call start/complete endpoints, emit duplicate
|
||||
telemetry, or run `vk begin`/`vk done`. VK already created the attempt and owns
|
||||
its terminal state.
|
||||
|
||||
## Shared protocol for managed harnesses
|
||||
|
||||
Every managed Buzz, Grok Build, Codex, Claude Code, Copilot CLI, Hermes, and
|
||||
OpenClaw run follows this contract:
|
||||
|
||||
1. Treat the Veritas task envelope as the authority for the objective,
|
||||
acceptance criteria, constraints, worktree, side effects, commit policy,
|
||||
expected outputs, verification gates, and completion evidence.
|
||||
2. Read repository instructions available in the assigned worktree, including
|
||||
`AGENTS.md` and any harness-specific supplement that the adapter exposes.
|
||||
3. Work only in the assigned worktree. Preserve files that existed at launch
|
||||
unless the task explicitly authorizes changing them.
|
||||
4. Use only the tools and MCP servers in the run-scoped catalog. Prefer the
|
||||
provided VK tools over ad hoc HTTP calls.
|
||||
5. Never copy credentials into commands, prompts, files, comments, logs, or
|
||||
final output. Use only the brokered references and provider boot
|
||||
authentication supplied by VK.
|
||||
6. Record durable findings through an available task comment or artifact tool
|
||||
when they affect later work. If no such tool is available, include the
|
||||
finding in the final response.
|
||||
7. Run the smallest verification that proves the requested change. Do not run
|
||||
a full repository suite unless the task or release gate requires it.
|
||||
8. Return a concise final response with the outcome, files or artifacts
|
||||
changed, checks run, remaining risks, and blockers. The harness converts its
|
||||
native terminal result into VK completion evidence.
|
||||
|
||||
### Copy-ready project instruction
|
||||
|
||||
Add this block to a repository's canonical `AGENTS.md` when VK manages its
|
||||
agents:
|
||||
|
||||
```md
|
||||
## Veritas Kanban managed-run protocol
|
||||
|
||||
When Veritas Kanban launches this work:
|
||||
|
||||
1. Treat the supplied task envelope as authoritative.
|
||||
2. Work only in the assigned worktree and obey its commit policy.
|
||||
3. Use only the run-scoped tools and credentials supplied by Veritas.
|
||||
4. Do not register, heartbeat, start, complete, or emit telemetry manually.
|
||||
5. Do not call `vk begin` or `vk done`; Veritas already owns the attempt.
|
||||
6. Run focused verification that matches the requested change.
|
||||
7. Return the outcome, changed files or artifacts, checks, risks, and blockers
|
||||
through the harness's normal final response.
|
||||
```
|
||||
|
||||
## How each managed harness receives VK context
|
||||
|
||||
| Harness | VK transport | Agent-facing behavior |
|
||||
| ------------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Buzz Agent | ACP v1 stdio | Receives the immutable task envelope and selected run tools through the ACP session. Session load/resume is unavailable. |
|
||||
| Grok Build | ACP v1 stdio | Receives the immutable task envelope and selected catalog in a dedicated `grok agent --no-leader ... stdio` process. |
|
||||
| GitHub Copilot CLI | ACP v1 stdio | Receives the immutable task envelope and selected catalog with remote, plugins, custom instructions, and experimental features disabled. |
|
||||
| OpenAI Codex CLI/SDK/app-server | Native process, SDK, or app-server stream | Receives the task envelope plus supported run-scoped MCP configuration. The adapter owns terminal capture. |
|
||||
| Claude Code | Supervised bare-mode stream | Receives the task envelope and an explicit run-scoped MCP configuration. It does not inherit arbitrary local plugins, hooks, or MCP servers. |
|
||||
| Hermes | Supervised one-shot process | Reads `AGENTS.md` from the assigned worktree and returns scripted stdout. Resume is unavailable. |
|
||||
| OpenClaw | Gateway tool invocation | Receives the task request through the configured gateway and reports through the attempt-bound callback. |
|
||||
|
||||
The current support tier is determined by runtime evidence, not this table.
|
||||
Before enabling a profile, the operator must run:
|
||||
|
||||
```bash
|
||||
vk doctor --json
|
||||
```
|
||||
|
||||
See [Agent Providers](AGENT-PROVIDERS.md) and
|
||||
[Harness Compatibility](HARNESS-COMPATIBILITY.md) for tested versions,
|
||||
capabilities, authentication, sandbox behavior, limitations, and recovery.
|
||||
|
||||
## Run-scoped VK tools
|
||||
|
||||
Managed adapters receive only the catalog selected for that attempt:
|
||||
|
||||
- A tool with an `allow` decision may be injected natively when the transport
|
||||
can enforce the exact catalog.
|
||||
- An approval-backed or credential-bound tool is available only through the
|
||||
system-owned `veritas-run` bridge.
|
||||
- Tools absent from the catalog are not authorized.
|
||||
- If a required tool is missing or rejected, report the blocker. Do not install
|
||||
another MCP server, inherit a global configuration, or fall back to raw
|
||||
credentials.
|
||||
|
||||
Managed agents do not need a separate global VK MCP configuration. The adapter
|
||||
injects the permitted run-scoped view when supported. The global MCP setup
|
||||
below is for unmanaged clients.
|
||||
|
||||
## External self-reporting agents
|
||||
|
||||
Use this path only when VK does not launch the process through a built-in
|
||||
adapter.
|
||||
|
||||
### Reusable `AGENTS.md` template
|
||||
|
||||
```md
|
||||
# AGENTS.md
|
||||
|
||||
## Identity
|
||||
|
||||
- **Agent ID:** `my-agent-id` _(unique, lowercase, dashes)_
|
||||
- **Name:** My Agent
|
||||
- **Model:** anthropic/claude-sonnet-4-5
|
||||
- **Provider:** anthropic
|
||||
- **Version:** 1.0.0
|
||||
- Agent ID: `my-agent-id`
|
||||
- Name: My Agent
|
||||
- Model: provider/model
|
||||
- Provider: external
|
||||
- Version: 1.0.0
|
||||
|
||||
## Capabilities
|
||||
|
||||
List what this agent can do. Used for task routing.
|
||||
- `code`: Write, review, and refactor code
|
||||
- `research`: Research and analysis
|
||||
- `review`: Code review and validation
|
||||
- `documentation`: Write and maintain documentation
|
||||
|
||||
- `code` — Write, review, and refactor code
|
||||
- `research` — Deep web research and analysis
|
||||
- `review` — Code review and PR feedback
|
||||
- `deploy` — CI/CD and deployment operations
|
||||
- `documentation` — Write and maintain docs
|
||||
## Veritas Kanban external-agent protocol
|
||||
|
||||
## Registration
|
||||
1. Register on startup and send a heartbeat every two to three minutes.
|
||||
2. Treat VK as the source of truth for task and attempt state.
|
||||
3. Start work through the documented task API and retain the returned attempt
|
||||
and runtime-manifest identities.
|
||||
4. Work only inside the assigned repository/worktree boundary.
|
||||
5. Emit progress, token, and completion data once. Do not duplicate events.
|
||||
6. On completion, report the final outcome, verification evidence, and the
|
||||
exact attempt/runtime identities expected by VK.
|
||||
7. Deregister cleanly on shutdown.
|
||||
```
|
||||
|
||||
On startup, register with Veritas Kanban:
|
||||
### Authentication
|
||||
|
||||
Set the VK endpoint and an agent-role API key outside source control:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/agents/register \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
export VK_API_URL=http://localhost:3001
|
||||
export VK_API_KEY=your-agent-api-key
|
||||
```
|
||||
|
||||
Every protected example below assumes:
|
||||
|
||||
```bash
|
||||
-H "X-API-Key: ${VK_API_KEY}"
|
||||
```
|
||||
|
||||
### Registration
|
||||
|
||||
```bash
|
||||
curl -X POST "${VK_API_URL}/api/agents/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: ${VK_API_KEY}" \
|
||||
--data '{
|
||||
"id": "my-agent-id",
|
||||
"name": "My Agent",
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
"provider": "anthropic",
|
||||
"model": "provider/model",
|
||||
"provider": "external",
|
||||
"capabilities": [
|
||||
{"name": "code", "description": "Write and review code"},
|
||||
{"name": "research", "description": "Deep research and analysis"}
|
||||
{"name": "research", "description": "Research and analysis"}
|
||||
],
|
||||
"version": "1.0.0"
|
||||
}'
|
||||
```
|
||||
|
||||
## Heartbeat
|
||||
|
||||
Send periodic heartbeats to stay registered (every 2-3 minutes):
|
||||
### Heartbeat
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/agents/register/my-agent-id/heartbeat \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
curl -X POST "${VK_API_URL}/api/agents/register/my-agent-id/heartbeat" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: ${VK_API_KEY}" \
|
||||
--data '{
|
||||
"status": "busy",
|
||||
"currentTaskId": "task_20260205_abc123",
|
||||
"currentTaskTitle": "Implement feature X"
|
||||
}'
|
||||
```
|
||||
|
||||
### Status Values
|
||||
Valid states are `online`, `busy`, `idle`, and `offline`. VK marks an agent
|
||||
offline after its configured heartbeat timeout.
|
||||
|
||||
| Status | Meaning |
|
||||
| --------- | ------------------------------------------------------ |
|
||||
| `online` | Agent is available for work |
|
||||
| `busy` | Agent is actively working on a task |
|
||||
| `idle` | Agent is running but not doing anything |
|
||||
| `offline` | Agent hasn't sent a heartbeat in 5+ minutes (auto-set) |
|
||||
### Task lifecycle
|
||||
|
||||
## Deregistration
|
||||
For an external agent:
|
||||
|
||||
On shutdown, deregister cleanly:
|
||||
1. Send a busy heartbeat with `currentTaskId`.
|
||||
2. Call `POST /api/agents/:taskId/start`.
|
||||
3. Retain the returned `attemptId` and provider-runtime manifest digest.
|
||||
4. Report token usage to `POST /api/agents/:taskId/tokens` with the active
|
||||
attempt.
|
||||
5. Complete through `POST /api/agents/:taskId/complete` with the same attempt
|
||||
and runtime-manifest digest.
|
||||
6. Send an idle heartbeat and clear the current task.
|
||||
|
||||
Use the exact request and response schemas in
|
||||
[API Reference](API-REFERENCE.md). Do not guess field names from these summary
|
||||
steps.
|
||||
|
||||
### Telemetry
|
||||
|
||||
Managed runs project `run.started`, `run.completed`, and provider-reported token
|
||||
events automatically. External agents must emit them once:
|
||||
|
||||
```bash
|
||||
curl -X DELETE http://localhost:3001/api/agents/register/my-agent-id
|
||||
curl -X POST "${VK_API_URL}/api/telemetry/events" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: ${VK_API_KEY}" \
|
||||
--data '{"type":"run.started","taskId":"<TASK_ID>","agent":"my-agent-id"}'
|
||||
|
||||
curl -X POST "${VK_API_URL}/api/telemetry/events" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: ${VK_API_KEY}" \
|
||||
--data '{"type":"run.completed","taskId":"<TASK_ID>","agent":"my-agent-id","durationMs":<MS>,"success":true}'
|
||||
```
|
||||
|
||||
## Discovery
|
||||
Do not manually emit these events for a managed provider run.
|
||||
|
||||
### List all agents
|
||||
### Deregistration
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/agents/register
|
||||
curl -X DELETE "${VK_API_URL}/api/agents/register/my-agent-id" \
|
||||
-H "X-API-Key: ${VK_API_KEY}"
|
||||
```
|
||||
|
||||
### Filter by status
|
||||
## Unmanaged MCP clients
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/agents/register?status=online
|
||||
Use the VK MCP server when an assistant needs typed board tools but is not the
|
||||
managed task runner.
|
||||
|
||||
Generic stdio configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"veritas-kanban": {
|
||||
"command": "node",
|
||||
"args": ["/absolute/path/to/veritas-kanban/mcp/dist/index.js"],
|
||||
"env": {
|
||||
"VK_API_URL": "http://localhost:3001",
|
||||
"VK_API_KEY": "your-agent-api-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Filter by capability
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/agents/register?capability=code
|
||||
```
|
||||
|
||||
### Find agents for a capability
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/agents/register/capabilities/research
|
||||
```
|
||||
|
||||
### Registry stats
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/agents/register/stats
|
||||
```
|
||||
|
||||
## Task Integration
|
||||
|
||||
For an externally registered agent that is not launched by a built-in v6
|
||||
provider adapter:
|
||||
|
||||
1. Send heartbeat with `status: "busy"` and `currentTaskId`
|
||||
2. Use existing task APIs: `POST /api/agents/:taskId/start`
|
||||
3. Report tokens: `POST /api/agents/:taskId/tokens` with the active `attemptId`
|
||||
4. Complete: `POST /api/agents/:taskId/complete` with the active `attemptId` and
|
||||
`providerRuntimeManifestDigest` returned by the start/status response
|
||||
5. Send heartbeat with `status: "idle"` and clear task
|
||||
|
||||
Managed Buzz, Grok Build, Codex, Claude Code, GitHub Copilot CLI, Hermes, and
|
||||
OpenClaw runs must not emulate these callbacks. Their selected adapter owns the
|
||||
task envelope, launch manifest, run supervision, causal events, approvals,
|
||||
tools, credentials, lifecycle, and authoritative completion result. Run
|
||||
`vk doctor --json` and use [Agent Providers](AGENT-PROVIDERS.md) before enabling
|
||||
a harness profile.
|
||||
|
||||
## OpenAI Codex Notes
|
||||
|
||||
When this template is used by Codex, add these project-specific instructions:
|
||||
|
||||
```md
|
||||
## Veritas Kanban Protocol
|
||||
|
||||
When working on Veritas Kanban tasks:
|
||||
|
||||
1. Treat Veritas Kanban as the source of truth for task state.
|
||||
2. Before implementation, inspect the task, acceptance criteria, worktree, and related docs.
|
||||
3. Move the task to `in-progress` and ensure an attempt is tracked.
|
||||
4. Keep notes in task comments or progress files when findings affect future work.
|
||||
5. Run relevant tests/checks before completion.
|
||||
6. Report final summary, files changed, tests run, risks, and follow-ups.
|
||||
7. For code changes, request cross-model review before final completion.
|
||||
8. Use the Veritas MCP server when available instead of ad hoc HTTP calls.
|
||||
|
||||
For OpenAI product/API questions, use the OpenAI developer documentation MCP server first.
|
||||
```
|
||||
|
||||
Recommended Codex MCP setup:
|
||||
Codex CLI configuration:
|
||||
|
||||
```bash
|
||||
codex mcp add veritas-kanban \
|
||||
--env VK_API_URL=http://localhost:3001 \
|
||||
--env VK_API_KEY=your-agent-api-key \
|
||||
-- node /absolute/path/to/veritas-kanban/mcp/dist/index.js
|
||||
|
||||
codex mcp add openaiDeveloperDocs --url https://developers.openai.com/mcp
|
||||
```
|
||||
|
||||
See [SOP-codex-integration.md](SOP-codex-integration.md) for the full Codex workflow.
|
||||
Restart the MCP client after changing its configuration. Verify read and write
|
||||
permissions with the smoke procedure in the
|
||||
[MCP Server Guide](mcp/README.md).
|
||||
|
||||
## Telemetry Emission For External Agents
|
||||
## References
|
||||
|
||||
The dashboard's **Success Rate**, **Token Usage**, and **Average Run Duration**
|
||||
graphs require `run.*` telemetry events. Managed v6 provider runs project these
|
||||
from the causal event and completion contracts. Only an externally registered
|
||||
agent operating outside a built-in adapter must emit them manually.
|
||||
|
||||
> Do not double-report managed provider runs. Manual events are for the
|
||||
> external self-reporting path only.
|
||||
|
||||
### When Starting a Task
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/telemetry/events \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type":"run.started","taskId":"<TASK_ID>","agent":"my-agent-id"}'
|
||||
```
|
||||
|
||||
### When Completing a Task
|
||||
|
||||
```bash
|
||||
# Report run result (success or failure)
|
||||
curl -X POST http://localhost:3001/api/telemetry/events \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type":"run.completed","taskId":"<TASK_ID>","agent":"my-agent-id","durationMs":<MS>,"success":true}'
|
||||
|
||||
# Report token usage (powers Token Usage + Monthly Budget)
|
||||
curl -X POST http://localhost:3001/api/telemetry/events \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type":"run.tokens","taskId":"<TASK_ID>","agent":"my-agent-id","model":"<MODEL>","inputTokens":<N>,"outputTokens":<N>,"cacheTokens":<N>,"cost":<N>}'
|
||||
```
|
||||
|
||||
### On Failure
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/telemetry/events \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"type":"run.completed","taskId":"<TASK_ID>","agent":"my-agent-id","durationMs":<MS>,"success":false}'
|
||||
```
|
||||
|
||||
### What's auto-captured vs. manual
|
||||
|
||||
| Event Type | Managed adapter | External self-reporting agent |
|
||||
| --------------------- | ------------------------------------- | ----------------------------- |
|
||||
| `task.created` | VK server | VK server |
|
||||
| `task.status_changed` | VK server | VK server |
|
||||
| `task.archived` | VK server | VK server |
|
||||
| `run.started` | Automatic | Agent must POST |
|
||||
| `run.completed` | Automatic | Agent must POST |
|
||||
| `run.tokens` | Automatic when provider reports usage | Agent must POST |
|
||||
|
||||
## Multi-Agent Coordination
|
||||
|
||||
The registry enables agents to discover each other:
|
||||
|
||||
```bash
|
||||
# Find who can help with code review
|
||||
curl http://localhost:3001/api/agents/register/capabilities/review
|
||||
|
||||
# Check if a specific agent is available
|
||||
curl http://localhost:3001/api/agents/register/codex-1
|
||||
```
|
||||
|
||||
This is the foundation for multi-agent task assignment (#29) and @mention notifications (#30).
|
||||
- [Agent Providers](AGENT-PROVIDERS.md)
|
||||
- [Harness Compatibility](HARNESS-COMPATIBILITY.md)
|
||||
- [Buzz Integration](BUZZ-INTEGRATION.md)
|
||||
- [MCP Server Guide](mcp/README.md)
|
||||
- [CLI Guide](CLI-GUIDE.md)
|
||||
- [API Reference](API-REFERENCE.md)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -64,21 +64,13 @@ curl http://localhost:3001/api/workflows
|
|||
"id": "feature-dev",
|
||||
"name": "Feature Development Workflow",
|
||||
"version": 2,
|
||||
"description": "End-to-end feature development pipeline",
|
||||
"agentCount": 4,
|
||||
"stepCount": 7,
|
||||
"createdAt": "2026-02-09T12:00:00Z",
|
||||
"updatedAt": "2026-02-09T14:30:00Z"
|
||||
"description": "End-to-end feature development pipeline"
|
||||
},
|
||||
{
|
||||
"id": "security-audit",
|
||||
"name": "Security Audit & Remediation",
|
||||
"version": 1,
|
||||
"description": "Scan, prioritize, and fix security issues",
|
||||
"agentCount": 3,
|
||||
"stepCount": 5,
|
||||
"createdAt": "2026-02-09T10:00:00Z",
|
||||
"updatedAt": "2026-02-09T10:00:00Z"
|
||||
"description": "Scan, prioritize, and fix security issues"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -173,6 +165,49 @@ X-Resource-Revision: 2
|
|||
|
||||
---
|
||||
|
||||
### GET /api/workflows/:id/access
|
||||
|
||||
Resolve workflow-specific actions and provenance for the current identity. Clients use this server-owned result to distinguish editable user workflows from built-in or shared read-only definitions.
|
||||
|
||||
**Request**:
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/api/workflows/feature-dev/access
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"workflowId": "feature-dev",
|
||||
"canView": true,
|
||||
"canEdit": false,
|
||||
"canExecute": true,
|
||||
"canDuplicate": true,
|
||||
"readOnlyReason": "Built-in workflows are read-only. Duplicate this workflow to customize it.",
|
||||
"provenance": {
|
||||
"kind": "built-in",
|
||||
"owner": "system",
|
||||
"createdBy": "system",
|
||||
"updatedBy": "system",
|
||||
"createdAt": "2026-02-09T12:00:00Z",
|
||||
"updatedAt": "2026-02-09T14:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`provenance.kind` is `built-in`, `user-owned`, or `shared`. `canEdit` and `canExecute` combine the authenticated request permissions with the workflow ACL decision. `canDuplicate` reflects whether the authenticated request may create workflows. A read-only response includes an actionable reason suitable for the workflow browser.
|
||||
|
||||
**Status Codes**:
|
||||
|
||||
- `200 OK` — Access and provenance resolved
|
||||
- `403 Forbidden` — No view permission
|
||||
- `404 Not Found` — Workflow not found
|
||||
|
||||
**Permissions**: Requires `workflow:read` and workflow-level `view` permission.
|
||||
|
||||
---
|
||||
|
||||
### POST /api/workflows
|
||||
|
||||
Create a new workflow.
|
||||
|
|
@ -522,6 +557,14 @@ using the strictest positive limit. Soft thresholds write `budget-policy`
|
|||
governance traces. Hard thresholds pause/block, require approval, downgrade the
|
||||
model route, or cancel according to the effective policy.
|
||||
|
||||
The workflow root reserves durable admission capacity before the run becomes
|
||||
active. Every provider-backed step then obtains a child reservation against
|
||||
its resolved provider and selected host before its attempt becomes running.
|
||||
The response exposes the root binding and the latest step decision without
|
||||
including prompts or credentials. Use `/api/admission?workflowRunId=<run-id>`
|
||||
or `vk admission list --workflow-run <run-id>` for current lease and limiting
|
||||
policy details.
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
|
|
@ -2098,12 +2141,14 @@ export interface ParallelSubStep {
|
|||
|
||||
```typescript
|
||||
export type WorkflowRunStatus = 'pending' | 'running' | 'blocked' | 'completed' | 'failed';
|
||||
export type WorkflowAdmissionState = 'waiting' | 'dispatching' | 'active' | 'terminal';
|
||||
|
||||
export interface WorkflowRun {
|
||||
id: string; // run_<timestamp>_<nanoid>
|
||||
workflowId: string;
|
||||
workflowVersion: number;
|
||||
taskId?: string; // optional task association
|
||||
admission?: WorkflowRootAdmissionBinding; // durable root reservation or queue identity
|
||||
status: WorkflowRunStatus;
|
||||
currentStep?: string; // current step ID
|
||||
context: Record<string, unknown>; // shared context across steps
|
||||
|
|
@ -2132,6 +2177,7 @@ export interface StepRun {
|
|||
retries: number;
|
||||
output?: string; // path to output file
|
||||
error?: string;
|
||||
admission?: WorkflowStepAdmissionBinding; // latest executable attempt or queue decision
|
||||
|
||||
// Loop-specific state
|
||||
loopState?: {
|
||||
|
|
@ -2143,6 +2189,12 @@ export interface StepRun {
|
|||
}
|
||||
```
|
||||
|
||||
When root or step capacity is temporarily unavailable, the corresponding
|
||||
admission binding has `state: "waiting"` and a durable `queueEntryId`. The run
|
||||
and step remain `pending`; provider execution is not marked active. A claimed
|
||||
entry briefly uses `dispatching` while Veritas transfers durable ownership to
|
||||
workflow recovery, then becomes `active` before provider execution.
|
||||
|
||||
### ToolPolicy
|
||||
|
||||
```typescript
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ Codify what works (and what burns us) when running Veritas Kanban with humans +
|
|||
|
||||
## Do This
|
||||
|
||||
1. **Always track time**
|
||||
- Start timers with `vk begin` the moment you pick up a task.
|
||||
1. **Track time through the correct lifecycle owner**
|
||||
- Humans and external agents start timers with `vk begin` when they pick up a task.
|
||||
- Managed harness runs let VK and the adapter own timing automatically.
|
||||
- If you forgot, add a manual entry with reason. Time data fuels estimation and billing.
|
||||
|
||||
2. **Use subtasks as living checklists**
|
||||
|
|
@ -26,8 +27,9 @@ Codify what works (and what burns us) when running Veritas Kanban with humans +
|
|||
6. **Update SOP files after every lesson**
|
||||
- Mistake → update AGENTS.md/CLAUDE.md + Lessons Learned field.
|
||||
|
||||
7. **Respect cross-model review**
|
||||
- Treat it like CI. No code ships without the opposite model’s signoff.
|
||||
7. **Respect configured review requirements**
|
||||
- Run independent or cross-model review only when the task, governance
|
||||
policy, issue owner, or release owner requires it.
|
||||
|
||||
8. **Mirror important artifacts to Brain/knowledge base**
|
||||
- Use `scripts/brain-write.sh` or equivalent so humans can find deliverables later.
|
||||
|
|
@ -52,7 +54,8 @@ Codify what works (and what burns us) when running Veritas Kanban with humans +
|
|||
- “Implement feature + write docs + shoot video” belongs in separate tasks.
|
||||
|
||||
4. **Auto-piloting agents without supervision**
|
||||
- Always read summaries, review diffs, enforce cross-model review.
|
||||
- Read summaries, inspect diffs, and enforce the review policy selected for
|
||||
the task.
|
||||
|
||||
5. **Letting prompts drift**
|
||||
- Keep prompt registry updated or agents will regress.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ Comprehensive guide to the `vk` command-line tool for Veritas Kanban.
|
|||
- [Agent Status](#agent-status)
|
||||
- [Project Management](#project-management)
|
||||
- [Agent Commands](#agent-commands)
|
||||
- [Admission Commands](#admission-commands)
|
||||
- [Durable Goal Commands](#durable-goal-commands)
|
||||
- [Automation Commands](#automation-commands)
|
||||
- [Scheduler Commands](#scheduler-commands)
|
||||
- [Queue Monitor Commands](#queue-monitor-commands)
|
||||
|
|
@ -445,25 +447,33 @@ vk project create "rubicon" --color "#7c3aed" --description "Main product"
|
|||
|
||||
Manage AI agents on code tasks.
|
||||
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| `vk start <id>` | Start an agent; optionally require runtime capabilities |
|
||||
| `vk launch-preview <id>` | Preview effective launch inputs, blockers, and drift |
|
||||
| `vk stop <id>` | Stop a run only when its persisted manifest supports stop |
|
||||
| `vk agent:resume <id> --source-attempt <id> -m <text>` | Resume the exact persisted provider conversation |
|
||||
| `vk agent:follow-up <id> --source-attempt <id> -m <text>` | Start a provider-native follow-up turn |
|
||||
| `vk agent:fork <id> --source-attempt <id> -m <text>` | Fork provider history without mutating its source |
|
||||
| `vk agent:steer <id> --attempt <id> -m <text>` | Steer the exact active provider turn |
|
||||
| `vk agent:interrupt <id> --attempt <id>` | Interrupt the exact active provider turn |
|
||||
| `vk agent:compact <id> --attempt <id>` | Compact a supported provider conversation |
|
||||
| `vk agent:archive <id> --attempt <id>` | Archive a supported provider conversation |
|
||||
| `vk agent:close <id> --attempt <id>` | Close a supported provider conversation |
|
||||
| `vk acp status --json` | Check ACP server-view API and permission readiness |
|
||||
| `vk acp serve --stdio [--task <id>]` | Expose a Veritas-managed task to an ACP v1 client |
|
||||
| `vk agents:pending` | List pending agent requests |
|
||||
| `vk agents:status <id>` | Check agent running status |
|
||||
| `vk agents:complete <id> -s --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (success) |
|
||||
| `vk agents:complete <id> -f --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (failure) |
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `vk start <id> [--phase <phase>]` | Start an agent; optionally bind an execution phase |
|
||||
| `vk launch-preview <id> [--phase <phase>]` | Preview effective launch inputs, blockers, and drift |
|
||||
| `vk workspace-trust scan <id>` | Inventory repository-controlled execution configuration |
|
||||
| `vk workspace-trust decide <id> --mode <mode> --inventory <digest> --reason <text>` | Authorize or deny one exact inventory |
|
||||
| `vk workspace-trust revoke <id> --inventory <digest> --reason <text>` | Revoke the current exact-inventory decision |
|
||||
| `vk stop <id>` | Stop a run only when its persisted manifest supports stop |
|
||||
| `vk agent:recovery <id>` | Inspect the latest retry or fallback decision |
|
||||
| `vk agent:cancel-recovery <id> --attempt <id>` | Cancel the exact pending recovery parent |
|
||||
| `vk agent:phase <id> --attempt <id>` | Read effective launch phase, sources, and transition history |
|
||||
| `vk agent:transition-phase <id> ...` | Apply or request approval for one exact phase transition |
|
||||
| `vk agent:decide-phase-approval <approvalId> ...` | Approve or reject an exact pending phase expansion |
|
||||
| `vk agent:resume <id> --source-attempt <id> -m <text> [--phase <phase>]` | Resume the exact persisted provider conversation |
|
||||
| `vk agent:follow-up <id> --source-attempt <id> -m <text> [--phase <phase>]` | Start a provider-native follow-up turn |
|
||||
| `vk agent:fork <id> --source-attempt <id> -m <text> [--phase <phase>]` | Fork provider history without mutating its source |
|
||||
| `vk agent:steer <id> --attempt <id> -m <text>` | Steer the exact active provider turn |
|
||||
| `vk agent:interrupt <id> --attempt <id>` | Interrupt the exact active provider turn |
|
||||
| `vk agent:compact <id> --attempt <id>` | Compact a supported provider conversation |
|
||||
| `vk agent:archive <id> --attempt <id>` | Archive a supported provider conversation |
|
||||
| `vk agent:close <id> --attempt <id>` | Close a supported provider conversation |
|
||||
| `vk acp status --json` | Check ACP server-view API and permission readiness |
|
||||
| `vk acp serve --stdio [--task <id>]` | Expose a Veritas-managed task to an ACP v1 client |
|
||||
| `vk agents:pending` | List pending agent requests |
|
||||
| `vk agents:status <id>` | Check agent running status |
|
||||
| `vk agents:complete <id> -s --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (success) |
|
||||
| `vk agents:complete <id> -f --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (failure) |
|
||||
|
||||
Require one or more capabilities before launch:
|
||||
|
||||
|
|
@ -477,14 +487,47 @@ vk start TASK-001 --agent codex \
|
|||
Preview without dispatching, or compare a new launch with a parent attempt:
|
||||
|
||||
```bash
|
||||
vk launch-preview TASK-001 --agent codex --parent-attempt attempt_parent --json
|
||||
vk start TASK-001 --agent codex --parent-attempt attempt_parent
|
||||
vk launch-preview TASK-001 --agent codex \
|
||||
--phase verify \
|
||||
--parent-attempt attempt_parent \
|
||||
--json
|
||||
vk start TASK-001 --agent codex \
|
||||
--phase verify \
|
||||
--parent-attempt attempt_parent
|
||||
```
|
||||
|
||||
Preview output includes the immutable run-launch digest, redacted command and
|
||||
argument plan, per-field origins, enforcement blockers, and material drift.
|
||||
It applies the same readiness gate and override rules as start. Attempt IDs and
|
||||
probe timestamps do not count as material drift.
|
||||
argument plan, phase evidence and source references, per-field origins,
|
||||
enforcement blockers, and material drift. It applies the same readiness gate
|
||||
and override rules as start. A parent phase binding is material; attempt IDs and
|
||||
probe timestamps alone do not count as material drift.
|
||||
|
||||
`--phase` accepts `explore`, `plan`, `implement`, `verify`, or `publish`.
|
||||
Explicit phases fail closed before attempt mutation when the selected runtime,
|
||||
sandbox, host, or tool policy cannot prove every required dimension. Omitting
|
||||
the flag creates an explicit legacy phase for a new launch. Resume, follow-up,
|
||||
fork, retry, fallback, and provider changes inherit and intersect the exact
|
||||
parent phase, so a descendant cannot widen authority by changing providers or
|
||||
omitting the flag. In the current staged v6.x delivery, use
|
||||
`launch-preview --phase ...` to inspect the evidence; explicit task or workflow
|
||||
starts remain blocked until #1033 supplies command and external-action
|
||||
enforcement.
|
||||
|
||||
Inspect workspace execution trust before launching a newly cloned or changed
|
||||
repository:
|
||||
|
||||
```bash
|
||||
vk workspace-trust scan TASK-001
|
||||
vk workspace-trust decide TASK-001 \
|
||||
--mode restricted \
|
||||
--inventory sha256:... \
|
||||
--reason "Reviewed instructions; keep the run read-only"
|
||||
```
|
||||
|
||||
Decision modes are `trusted`, `restricted`, and `denied`. The exact inventory
|
||||
digest from `scan` is required, and stale content is rejected. Decision and
|
||||
revocation commands require an administrator. `launch-preview` reports the
|
||||
effective trust status and any resulting enforcement blocker.
|
||||
|
||||
`--require-capability <capabilities...>` is additive to the baseline launch,
|
||||
profile, sandbox, and budget requirements. The server returns a structured
|
||||
|
|
@ -510,6 +553,199 @@ the stop request, so a replacement run fails a delayed stop closed. The CLI
|
|||
preserves the server's reason when `run.stop` is unavailable or the active and
|
||||
persisted manifest digests do not match.
|
||||
|
||||
Automatic recovery is separate from stopping an active provider. Use
|
||||
`vk agent:recovery TASK-001 --json` to inspect its classification, backoff,
|
||||
route, causal parent, manifest evidence, and cumulative budget. A cancellation
|
||||
must include the exact parent attempt:
|
||||
|
||||
```bash
|
||||
vk agent:cancel-recovery TASK-001 --attempt attempt_parent --json
|
||||
```
|
||||
|
||||
The server rejects stale parent IDs, recoveries that already launched, and
|
||||
recoveries that are already terminal.
|
||||
|
||||
Inspect and transition the exact active phase:
|
||||
|
||||
```bash
|
||||
vk agent:phase TASK-001 --attempt attempt_123 --json
|
||||
vk agent:transition-phase TASK-001 \
|
||||
--attempt attempt_123 \
|
||||
--operation move-to-implement \
|
||||
--target-evidence ./implement-evidence.json \
|
||||
--reason "The approved plan is ready to implement." \
|
||||
--json
|
||||
```
|
||||
|
||||
`agent:phase` reports the launch phase even before the first transition. Human
|
||||
output distinguishes parent, agent-profile, sandbox, tool-catalog, and launch
|
||||
policy sources; `--json` returns the server-owned snapshot unchanged.
|
||||
|
||||
The first transition also requires `--from-evidence <file>` and
|
||||
`--manifest <sha256:...>`. Later requests read the current journal record and
|
||||
automatically bind its sequence, evidence digest, and manifest. Narrowing
|
||||
applies immediately. Expansion returns an exact approval; decide it with
|
||||
`vk agent:decide-phase-approval <id> --decision approve`, then retry the same
|
||||
transition with the same `--operation` and `--approval-id`. Emergency expansion
|
||||
requires `--override-until` and `--override-reason`; the authenticated caller
|
||||
must be an administrator and the expiry cannot exceed 24 hours.
|
||||
|
||||
---
|
||||
|
||||
### Admission Commands
|
||||
|
||||
Inspect the capacity reservation that must exist before a provider can start:
|
||||
|
||||
```bash
|
||||
vk admission list
|
||||
vk admission list --state active --provider codex-cli --json
|
||||
vk admission list --workflow-run run_20260725_abc123 --json
|
||||
vk admission list --workflow-run run_20260725_abc123 --workflow-step implement
|
||||
vk admission list --root-reservation admission_0123456789abcdef
|
||||
vk admission list --root-objective objective_0123456789abcdef --json
|
||||
vk admission get admission_0123456789abcdef --json
|
||||
vk admission tree objective_0123456789abcdef
|
||||
vk admission tree objective_0123456789abcdef --limit 25 --json
|
||||
vk admission queue list
|
||||
vk admission queue list --state queued requeued --source workflow --min-age 60000 --json
|
||||
vk admission queue get admission_queue_0123456789abcdef
|
||||
vk admission queue get admission_queue_0123456789abcdef --json
|
||||
vk admission queue cancel admission_queue_0123456789abcdef \
|
||||
--reason "Operator cancelled the queued launch." \
|
||||
--idempotency-key operator-queue-cancel-20260725
|
||||
vk admission cancel-tree objective_0123456789abcdef \
|
||||
--reason "Operator stopped runaway child-agent expansion." \
|
||||
--idempotency-key operator-tree-cancel-20260725
|
||||
vk admission resume-tree objective_0123456789abcdef \
|
||||
--reason "Operator confirmed fan-out pressure cleared." \
|
||||
--idempotency-key operator-tree-resume-20260725
|
||||
```
|
||||
|
||||
`admission list` filters by workspace, task, root task, provider, host, state,
|
||||
workflow run, workflow step, root reservation, root objective, node, parent
|
||||
node, and result limit. Active records
|
||||
show the lease expiry and requested run, process, and estimated-memory
|
||||
capacity. Workflow roots use provider `workflow-control`; executable child
|
||||
steps show their resolved provider, selected host, and root reservation.
|
||||
Released records retain the terminal reason and idempotency identity for
|
||||
operator diagnosis. `admission tree` reports committed and reserved tokens,
|
||||
cost, tool calls, runtime, retries, fan-out, policy availability, and bounded
|
||||
contributors without double counting descendant totals. It also shows durable
|
||||
cancellation or circuit-breaker control state when present. Inspection
|
||||
commands are read-only and require `agent:read`.
|
||||
|
||||
`admission queue list` filters by workspace, root objective, node, launch
|
||||
source, queue state, raw numeric priority, limiting scope, age window, page,
|
||||
and result limit. `admission queue get` shows one entry with position,
|
||||
priority aging, readiness, lease posture, redacted launch identity, limiting
|
||||
policy, conditional start factors, selection evidence, and safe navigation
|
||||
identifiers. Human output labels the snapshot as conditional and never presents
|
||||
an ETA or exact start time. Use `--json` for the complete versioned REST shape.
|
||||
|
||||
`admission queue cancel` stops one queued or leased launch before provider
|
||||
dispatch and releases its reservation. `admission cancel-tree` records
|
||||
cancellation on the root first, drains queued descendants, releases unbound
|
||||
reservations, and asks the local supervisor to interrupt verified running
|
||||
attempts. `admission resume-tree` re-evaluates durable breaker evidence and
|
||||
resumes a paused tree only after every blocking signal clears. All three
|
||||
commands require `admin:manage`, require an operator reason, and accept a stable
|
||||
`--idempotency-key` for safe retries. If omitted, the CLI generates a new key.
|
||||
Use `--json` to inspect the complete control or any verified running attempts
|
||||
that still require reconciliation. A blocked resume returns
|
||||
`EXECUTION_TREE_RESUME_BLOCKED`; do not retry it in a loop without changing the
|
||||
reported pressure. A cancelled root rejects late resume, retry, fallback,
|
||||
workflow-step, and child-agent launches before provider dispatch.
|
||||
|
||||
The Operations admission panel exposes the same controls. Queue rows can cancel
|
||||
one pending launch or its entire tree. Durable tree-control cards show bounded
|
||||
breaker signals and observed descendant/depth evidence, then allow an
|
||||
administrator to resume or cancel the tree with an audited reason.
|
||||
|
||||
---
|
||||
|
||||
### Durable Goal Commands
|
||||
|
||||
Create and control one evidence-gated objective across multiple runs:
|
||||
|
||||
```bash
|
||||
vk goals create \
|
||||
--objective "Deliver the provider migration." \
|
||||
--acceptance "All provider fixtures pass" "Operator docs are current" \
|
||||
--requirement "provider-tests|test|Focused provider fixtures pass." \
|
||||
--requirement "operator-docs|artifact|Operator documentation is reviewed." \
|
||||
--root-task task_0123456789abcdef \
|
||||
--mode automatic \
|
||||
--max-turns 20 \
|
||||
--max-rollovers 2 \
|
||||
--compact-after-tokens 120000 \
|
||||
--require-rollover-approval \
|
||||
--json
|
||||
|
||||
vk goals list --state active blocked awaiting-approval --json
|
||||
vk goals get goal_0123456789abcdef --json
|
||||
|
||||
vk goals transition goal_0123456789abcdef \
|
||||
--revision 3 \
|
||||
--state paused \
|
||||
--reason "Operator paused before external coordination." \
|
||||
--json
|
||||
|
||||
vk goals transition goal_0123456789abcdef \
|
||||
--revision 4 \
|
||||
--state complete \
|
||||
--reason "All configured evidence is verified." \
|
||||
--evidence-json '[{"requirementId":"provider-tests","evidenceId":"ci-30182450098","summary":"Focused provider fixtures passed."},{"requirementId":"operator-docs","evidenceId":"review-20260726","summary":"Operator docs reviewed."}]' \
|
||||
--json
|
||||
|
||||
vk goals link-run goal_0123456789abcdef \
|
||||
--revision 2 \
|
||||
--task task_0123456789abcdef \
|
||||
--attempt attempt_0123456789abcdef \
|
||||
--conversation conversation_0123456789abcdef \
|
||||
--json
|
||||
|
||||
vk goals rollover goal_0123456789abcdef \
|
||||
--revision 6 \
|
||||
--json
|
||||
```
|
||||
|
||||
`goals create` requires exactly one `--root-task` or `--root-workflow`.
|
||||
Completion requirements use `id|kind|description`; supported kinds are
|
||||
`test`, `build`, `artifact`, `operator`, `external`, and `other`. At least one
|
||||
required evidence item must exist, and a transition to `complete` fails until
|
||||
every required item has verified evidence.
|
||||
|
||||
Every mutation requires the current `--revision`. A stale revision returns a
|
||||
conflict instead of overwriting a newer operator or supervisor decision.
|
||||
Blocked transitions use `--blocker-json` for the exact blocker, attempt count,
|
||||
next safe action, and required authority or external state change. The server
|
||||
derives the transition actor, workspace, verification timestamp, and evidence
|
||||
verifier from authenticated context; caller-supplied identity fields are
|
||||
rejected. Use `--json` for stable automation output.
|
||||
|
||||
`goals get --json` includes the deduplicated `usageEvents`, full
|
||||
`continuationChain`, and restart-safe `continuationAttempts`. Automatic goals
|
||||
continue only after the prior completion is durable and required evidence,
|
||||
blockers, turn limits, and aggregate budgets are evaluated. A continuation is
|
||||
persisted as `planned` before it enters the normal admission path, then becomes
|
||||
`dispatched` with its attempt or queue identity. On restart, the same admission
|
||||
idempotency key is reused; an already-created child attempt is linked without a
|
||||
duplicate launch.
|
||||
|
||||
Manual goals pause after an incomplete run. Automatic goals block when the
|
||||
provider has no verified continuation handle and no rollover allowance, or
|
||||
when admission fails. They enter `usage-limited` or `budget-limited` at their
|
||||
configured boundary. A configured `--compact-after-tokens` threshold starts a
|
||||
fresh conversation only while `--max-rollovers` still has capacity. Each
|
||||
rollover persists `kind: "rollover"` before dispatch and carries a bounded
|
||||
goal contract with objective, constraints, acceptance criteria, verified
|
||||
evidence links, remaining requirements, recent state decisions, and aggregate
|
||||
usage. If `--require-rollover-approval` is set, the goal enters
|
||||
`awaiting-approval`; run `goals rollover` with the current revision to approve
|
||||
and dispatch that exact handoff. Resume other limited states explicitly after
|
||||
addressing the reported condition; do not build a second client-side
|
||||
continuation loop.
|
||||
|
||||
---
|
||||
|
||||
### Prompt Commands
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Companion docs:
|
|||
|
||||
- [SOP: OpenAI Codex Integration](SOP-codex-integration.md)
|
||||
- [Codex Workflow Examples](EXAMPLES-codex-workflows.md)
|
||||
- [SOP: Cross-Model Code Review](SOP-cross-model-code-review.md)
|
||||
- [Optional Independent Code Review](SOP-cross-model-code-review.md)
|
||||
- [AGENTS.md Template](AGENTS-TEMPLATE.md)
|
||||
|
||||
## Product Goal
|
||||
|
|
@ -80,7 +80,17 @@ POST /api/github/codex/delegate
|
|||
|
||||
## Architecture Direction
|
||||
|
||||
v4.3 uses an explicit provider adapter contract inside the agent service. `codex` agents resolve to the local Codex CLI runner, `codex-sdk` agents resolve to the SDK session runner, `codex-cloud` uses GitHub-native delegation, and existing agents keep the OpenClaw request-file behavior.
|
||||
Executable task providers resolve through the dedicated
|
||||
`AgentProviderAdapterRegistry`. The registry owns exact provider selection,
|
||||
task-envelope rendering, runtime probing, run-event mapping, start dispatch,
|
||||
and stop semantics. `ClawdbotAgentService` supplies shared admission,
|
||||
supervision, journaling, budget, and completion effects without selecting an
|
||||
implicit fallback adapter.
|
||||
|
||||
`codex` agents resolve to the local Codex CLI runner, `codex-sdk` agents resolve
|
||||
to the SDK session runner, and `codex-cloud` uses GitHub-native delegation.
|
||||
OpenClaw task dispatch uses the gateway `sessions_spawn` path and persists the
|
||||
returned session identity on the active attempt.
|
||||
|
||||
Expected long-term provider capabilities:
|
||||
|
||||
|
|
@ -93,7 +103,7 @@ Expected long-term provider capabilities:
|
|||
- optional `review`
|
||||
- optional `cloudDelegate`
|
||||
|
||||
The provider abstraction should support:
|
||||
The provider adapter interface supports:
|
||||
|
||||
- OpenClaw compatibility through an OpenClaw provider adapter.
|
||||
- Codex CLI through a local process provider.
|
||||
|
|
|
|||
|
|
@ -67,17 +67,39 @@ Data is persisted in a Docker named volume (`kanban-data`), so it survives conta
|
|||
|
||||
### Dockerfile Overview
|
||||
|
||||
The multi-stage Dockerfile produces a minimal production image (< 200 MB):
|
||||
The multi-stage Dockerfile enforces architecture-specific production image budgets:
|
||||
|
||||
| Stage | Purpose |
|
||||
| -------------- | --------------------------------------- |
|
||||
| `deps` | Install all pnpm workspace dependencies |
|
||||
| `build-shared` | Compile the shared TypeScript package |
|
||||
| `build-web` | Build the React frontend with Vite |
|
||||
| `build-server` | Compile the Express server TypeScript |
|
||||
| `production` | Minimal Node.js 22 Alpine runtime |
|
||||
| Architecture | Maximum compressed image size | 6.1.2 implementation baseline |
|
||||
| ------------ | ----------------------------- | ----------------------------- |
|
||||
| `arm64` | 200,000,000 bytes | 195,910,880 bytes |
|
||||
| `amd64` | 600,000,000 bytes | 571,590,173 bytes |
|
||||
|
||||
The production stage runs as a non-root user (`veritas`, UID 1001) for security.
|
||||
The final release candidate is remeasured at the release milestone; these
|
||||
implementation baselines are not substituted for final artifact evidence.
|
||||
|
||||
| Stage | Purpose |
|
||||
| -------------- | ------------------------------------------------------------------------ |
|
||||
| `deps` | Install all pnpm workspace dependencies |
|
||||
| `build-shared` | Compile the shared TypeScript package |
|
||||
| `build-web` | Build the React frontend with Vite |
|
||||
| `build-server` | Compile the server and deploy its production dependency closure |
|
||||
| `production` | Copy only the server closure and built web assets into Node.js 22 Alpine |
|
||||
|
||||
The production stage does not contain npm, pnpm, the root workspace/lockfile,
|
||||
CLI dependencies, or MCP dependencies. It retains only the server and shared
|
||||
package identity manifests required for module resolution and version health.
|
||||
It runs as the non-root `veritas` user (UID 1001).
|
||||
|
||||
The `amd64` image is larger because the Linux Codex runtime bundled by `@openai/codex-sdk`
|
||||
occupies about 302 MB of its unpacked filesystem, including a roughly 245 MB executable.
|
||||
Retaining it keeps the `codex-sdk` provider functional without an operator-supplied binary.
|
||||
The budgets leave about 2% headroom on `arm64` and 5% on `amd64`, so material dependency growth
|
||||
still fails the contract instead of being normalized by one loose cross-platform ceiling.
|
||||
|
||||
CI builds the production target and runs `pnpm check:docker-image`. The contract fails when the
|
||||
image reaches its architecture budget or when the runtime smoke cannot prove non-root execution,
|
||||
SQLite startup, API authentication, static web serving, health checks, and the native `bcrypt`
|
||||
module. `VERITAS_DOCKER_MAX_BYTES` can set an explicit budget for another architecture.
|
||||
|
||||
**Path Resolution (v2.1.3):** All services use the shared `paths.ts` utility for consistent path resolution. The resolution priority is: `DATA_DIR` / `VERITAS_DATA_DIR` env var → auto-discovery of monorepo root (walks up from cwd looking for `pnpm-workspace.yaml`) → fallback to cwd. A filesystem root guard prevents silent `/` resolution, which previously caused `EACCES: permission denied` errors in Docker. The production image uses `WORKDIR /app/server` for backwards compatibility.
|
||||
|
||||
|
|
@ -232,10 +254,10 @@ If you need to debug inside a container, use `docker exec` to inspect — don't
|
|||
|
||||
### Prerequisites
|
||||
|
||||
| Requirement | Version |
|
||||
| ----------- | ------- |
|
||||
| Node.js | 22.0.0+ |
|
||||
| pnpm | 11.1.1+ |
|
||||
| Requirement | Version |
|
||||
| ----------- | --------------- |
|
||||
| Node.js | 22.22.1+ |
|
||||
| pnpm | 11.1.1 (pinned) |
|
||||
|
||||
Install pnpm if not present:
|
||||
|
||||
|
|
@ -434,18 +456,15 @@ sends API requests to `/kanban/api/...`.
|
|||
> or the equivalent changes to `vite.config.ts`, `web/src/lib/config.ts`, and
|
||||
> `web/src/lib/api/helpers.ts`.
|
||||
|
||||
**Docker volumes for sub-path:** When using Docker with sub-path deployment, ensure both
|
||||
the task data and the config directory are on persistent volumes:
|
||||
**Docker volumes for sub-path:** One volume at `DATA_DIR` persists tasks and runtime state:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- kanban-data:/app/data # Task files
|
||||
- kanban-config:/app/.veritas-kanban # Config, sprints, enforcement gates
|
||||
- kanban-data:/app/data # tasks/ plus .veritas-kanban/
|
||||
```
|
||||
|
||||
Without a config volume, settings (enforcement gates, transition hooks, sprints) are lost
|
||||
on every container rebuild because `.veritas-kanban/` lives on the overlay filesystem, not
|
||||
on the data volume.
|
||||
Do not mount a second volume at `/app/.veritas-kanban`; that is a legacy location used only
|
||||
as a read-only source during startup migration.
|
||||
|
||||
### systemd Service
|
||||
|
||||
|
|
@ -535,13 +554,14 @@ All variables are set in `server/.env` (or passed as environment variables in Do
|
|||
|
||||
### Networking & Security
|
||||
|
||||
| Variable | Default | Description |
|
||||
| ----------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `TRUST_PROXY` | — | Express trust proxy setting for reverse proxy deployments. Common: `1` (single hop), `loopback`. Required for correct rate limiting behind nginx/Caddy/Traefik. `true` is blocked for safety |
|
||||
| `CORS_ORIGINS` | `http://localhost:3000,http://localhost:5173,...` | Comma-separated list of allowed CORS origins |
|
||||
| `RATE_LIMIT_MAX` | `300` | Max API requests per minute per IP (localhost exempt). Auth endpoints have a stricter 15 req/min limit |
|
||||
| `CSP_REPORT_ONLY` | `false` | Use Content-Security-Policy-Report-Only instead of enforcing |
|
||||
| `CSP_REPORT_URI` | — | URL to receive CSP violation reports |
|
||||
| Variable | Default | Description |
|
||||
| ------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `TRUST_PROXY` | — | Express trust proxy setting for reverse proxy deployments. Common: `1` (single hop), `loopback`. Required for correct rate limiting behind nginx/Caddy/Traefik. `true` is blocked for safety |
|
||||
| `VERITAS_EGRESS_UPSTREAM_PROXY` | — | Optional operator HTTP proxy for policy-approved run egress. The gateway tunnels to the DNS-pinned destination and never persists proxy credentials |
|
||||
| `CORS_ORIGINS` | `http://localhost:3000,http://localhost:5173,...` | Comma-separated list of allowed CORS origins |
|
||||
| `RATE_LIMIT_MAX` | `300` | Max API requests per minute per IP (localhost exempt). Auth endpoints have a stricter 15 req/min limit |
|
||||
| `CSP_REPORT_ONLY` | `false` | Use Content-Security-Policy-Report-Only instead of enforcing |
|
||||
| `CSP_REPORT_URI` | — | URL to receive CSP violation reports |
|
||||
|
||||
### Prometheus metrics
|
||||
|
||||
|
|
@ -553,16 +573,16 @@ All variables are set in `server/.env` (or passed as environment variables in Do
|
|||
|
||||
### Data & Storage
|
||||
|
||||
| Variable | Default | Description |
|
||||
| -------------------------- | -------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| `VERITAS_DATA_DIR` | `.veritas-kanban` (relative to project root) | Directory for config, logs, and internal data |
|
||||
| `DATA_DIR` | `/app/data` (Docker only) | Mapped data directory inside the Docker container |
|
||||
| `VERITAS_STORAGE` | `file` | Selects `file` or `sqlite` storage |
|
||||
| `VERITAS_SQLITE_PATH` | Runtime `veritas.db` | SQLite database override; must resolve to verified durable local storage |
|
||||
| `VERITAS_SQLITE_TOPOLOGY` | — | Set explicitly to `single-host` before compatibility/override maintenance |
|
||||
| `VERITAS_SQLITE_HOST_ID` | — | Stable unique host binding for SQLite compatibility ownership policy |
|
||||
| `TELEMETRY_RETENTION_DAYS` | `30` | Days to keep telemetry event files before deletion |
|
||||
| `TELEMETRY_COMPRESS_DAYS` | `7` | Days after which NDJSON telemetry files are gzip-compressed (0 = disabled) |
|
||||
| Variable | Default | Description |
|
||||
| -------------------------- | ------------------------- | -------------------------------------------------------------------------- |
|
||||
| `VERITAS_DATA_DIR` | Project root when unset | Storage root used when `DATA_DIR` is unset |
|
||||
| `DATA_DIR` | `/app/data` (Docker only) | Preferred storage root; takes precedence over `VERITAS_DATA_DIR` |
|
||||
| `VERITAS_STORAGE` | `file` | Selects `file` or `sqlite` storage |
|
||||
| `VERITAS_SQLITE_PATH` | Runtime `veritas.db` | SQLite database override; must resolve to verified durable local storage |
|
||||
| `VERITAS_SQLITE_TOPOLOGY` | — | Set explicitly to `single-host` before compatibility/override maintenance |
|
||||
| `VERITAS_SQLITE_HOST_ID` | — | Stable unique host binding for SQLite compatibility ownership policy |
|
||||
| `TELEMETRY_RETENTION_DAYS` | `30` | Days to keep telemetry event files before deletion |
|
||||
| `TELEMETRY_COMPRESS_DAYS` | `7` | Days after which NDJSON telemetry files are gzip-compressed (0 = disabled) |
|
||||
|
||||
### Integration
|
||||
|
||||
|
|
@ -618,9 +638,25 @@ wscat -c "ws://localhost:3001/ws?api_key=<api-key>"
|
|||
| `.veritas-kanban/worktree-manifests/` | Durable worktree ownership, base, lifecycle, and override evidence |
|
||||
| `.veritas-kanban/agent-requests/` | Pending AI agent requests |
|
||||
|
||||
In Docker, the `DATA_DIR` environment variable maps to `/app/data` by default inside the container.
|
||||
In Docker, `DATA_DIR=/app/data`. Tasks live under `/app/data/tasks` and all runtime state
|
||||
lives under `/app/data/.veritas-kanban`; no persistent state is written to `/app` or
|
||||
`/app/server` outside that volume.
|
||||
|
||||
**Auth state persistence fix (v3.1.1):** Runtime config/state files (including `security.json`) now always live under `${DATA_DIR}/.veritas-kanban`. On startup, Veritas Kanban will automatically migrate any legacy runtime files it finds in container-only paths (for example, `/app/.veritas-kanban` or `/app/server/.veritas-kanban`) into the Docker volume.
|
||||
**Auth state persistence fix (v3.1.1):** Runtime config/state files (including `security.json`) now always live under `${DATA_DIR}/.veritas-kanban`. On startup, Veritas Kanban automatically migrates legacy runtime files it can see at container-only paths (for example, `/app/.veritas-kanban` or `/app/server/.veritas-kanban`) into the Docker volume. A replaced container cannot see data left in an old container layer or an unmounted legacy volume.
|
||||
|
||||
If the old runtime state is in a named volume, mount that volume read-only at its former path for one startup. For example, add the legacy mount temporarily to your Compose service:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
veritas-kanban:
|
||||
volumes:
|
||||
- kanban-data:/app/data
|
||||
- legacy-veritas-config:/app/.veritas-kanban:ro
|
||||
```
|
||||
|
||||
Start the service, verify the expected files now exist under
|
||||
`/app/data/.veritas-kanban`, then remove the legacy mount from Compose. The migration is
|
||||
copy-only: it does not delete the legacy source, and an existing destination file wins.
|
||||
|
||||
If you upgraded from an older image and already lost auth state, you can recover by copying `security.json` from a still-running/old container (if available) into the volume:
|
||||
|
||||
|
|
@ -696,7 +732,7 @@ docker compose down
|
|||
docker run --rm \
|
||||
-v kanban-data:/data \
|
||||
-v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/veritas-backup-20260129.tar.gz -C /data"
|
||||
alpine sh -c 'set -eu; archive=/backup/veritas-backup-20260129.tar.gz; test -d /data; test "$(readlink -f /data)" = /data; test -r "$archive"; tar tzf "$archive" >/dev/null; find /data -mindepth 1 -delete; tar xzf "$archive" -C /data'
|
||||
|
||||
# Restart
|
||||
docker compose up -d
|
||||
|
|
|
|||
|
|
@ -50,9 +50,11 @@ live repository install into a production-only dependency state.
|
|||
|
||||
## GitHub Workflows
|
||||
|
||||
`Desktop Artifacts` runs on desktop/package/release-workflow pull requests,
|
||||
after server/web/shared/desktop changes merge to `main`, and on manual
|
||||
dispatch. It builds unsigned artifacts on:
|
||||
`Desktop Artifacts` runs only for a pull request carrying `ci:full` or through
|
||||
manual dispatch. Ordinary pull requests and `main` pushes do not package
|
||||
desktop applications. A release candidate keeps `ci:full` applied through its
|
||||
final synchronization so the artifacts correspond to the reviewed head. The
|
||||
workflow builds unsigned artifacts on:
|
||||
|
||||
- `macos-15`: DMG, ZIP, blockmap, and update YAML.
|
||||
- `ubuntu-24.04`: x64 AppImage, deb, rpm, blockmap, and update YAML.
|
||||
|
|
@ -67,7 +69,10 @@ targets.
|
|||
|
||||
`Desktop Release` runs on manual dispatch or a published GitHub release. It
|
||||
requires the signing secrets below, builds signed/notarized macOS artifacts,
|
||||
and publishes update metadata with the GitHub provider.
|
||||
and publishes update metadata with the GitHub provider. A published-release run
|
||||
first verifies that the live GitHub body exactly matches
|
||||
`docs/releases/vX.Y.Z.md`; a mismatch stops the workflow before signing or
|
||||
packaging.
|
||||
|
||||
## Homebrew Cask
|
||||
|
||||
|
|
@ -184,9 +189,25 @@ policy is tracked in
|
|||
- Run `pnpm desktop:package:windows:unsigned` on Windows or the
|
||||
`Desktop Artifacts` Windows job and inspect preview artifact names. This is
|
||||
not a v6 GA release gate.
|
||||
- Run `Desktop Artifacts` and download the uploaded DMG/ZIP/update metadata.
|
||||
- Apply `ci:full` to the release-candidate pull request, then download the
|
||||
uploaded DMG/ZIP/update metadata from its `Desktop Artifacts` run.
|
||||
- Edit `docs/releases/vX.Y.Z.md`, run
|
||||
`pnpm validate:release -- --version X.Y.Z`, and publish that exact file with
|
||||
`gh release create --notes-file` or `gh release edit --notes-file`. Do not
|
||||
hand-author or repair the live body separately.
|
||||
- Use one logical source line per prose paragraph and let GitHub wrap it to the
|
||||
available width. Keep release structure to level-two headings. Prefer one
|
||||
cohesive full-width paragraph for a handful of related changes. Use Markdown
|
||||
lists only when every item is concise enough to avoid multi-line hanging
|
||||
indentation; the format gate caps list items at 160 source characters. Never
|
||||
stack bold-led paragraphs or long labeled list items that look like accidental
|
||||
carriage returns.
|
||||
- Run `Desktop Release` only after Developer ID signing secrets and exactly one
|
||||
complete notarization credential set are configured.
|
||||
- Inspect the rendered release on both the releases index and tag page. Confirm
|
||||
prose uses natural page-width wrapping, lists remain compact, and no ragged
|
||||
hanging-indent block, unmarked paragraph stack, or sentence-sized fragment was
|
||||
introduced.
|
||||
- Confirm notarization succeeds with the intended credential mode and the DMG
|
||||
installs without Gatekeeper warnings on a clean Mac.
|
||||
- Confirm a first run creates the profile/workspace app data directories.
|
||||
|
|
|
|||
|
|
@ -44,7 +44,13 @@ When completing a task that changes user-facing behavior:
|
|||
|
||||
### Freshness Indicators
|
||||
|
||||
Each doc should include a freshness header:
|
||||
The Settings → Doc Freshness registry is the authoritative freshness source.
|
||||
Each tracked record stores its path, last review date, reviewer, maximum age,
|
||||
tags, and notes. The service computes scores and alerts from those records; it
|
||||
does not scan or rewrite Markdown headers.
|
||||
|
||||
A maintained living document may also include this optional human-readable
|
||||
marker when repository reviewers find it useful:
|
||||
|
||||
```markdown
|
||||
<!-- doc-freshness: 2026-03-25 | v4.0.0 | @veritas -->
|
||||
|
|
@ -52,24 +58,32 @@ Each doc should include a freshness header:
|
|||
|
||||
Format: `date | version | last-updater`
|
||||
|
||||
When a doc is older than the current version, it may need review.
|
||||
The optional marker is not required for release notes, historical evidence,
|
||||
generated references, or every file under `docs/`. When a tracked document is
|
||||
older than its configured maximum age or its maintained version, review it and
|
||||
update the authoritative registry record.
|
||||
|
||||
### Last Sweep
|
||||
|
||||
| Date | Scope | Agent |
|
||||
| ---------- | ------------------------------------------------------------------- | ------- |
|
||||
| 2026-07-24 | v6.0.0 harness, Buzz, release, upgrade, compatibility, and evidence | Release |
|
||||
| 2026-07-12 | v5.2.2 UI-audit fixes, release gates, desktop state, and evidence | Release |
|
||||
| 2026-06-05 | v5.0.0 stable release docs, install paths, release assets, RC notes | Codex |
|
||||
| 2026-03-25 | Full v3→v4 version references, governance docs, CHANGELOG, examples | VERITAS |
|
||||
| 2026-03-21 | v4.0 release documentation | TARS |
|
||||
| Date | Scope | Agent |
|
||||
| ---------- | ------------------------------------------------------------------------------------------ | ------- |
|
||||
| 2026-08-24 | README; v6.1.2 audit, storage, provider, CI, security, release, distribution, and SOP docs | Release |
|
||||
| 2026-08-22 | v6.1.1 maintenance, dependency, release, upgrade, and evidence docs | Release |
|
||||
| 2026-07-26 | v6.1.0 roadmap, harness, governance, knowledge, and release docs | Release |
|
||||
| 2026-07-24 | v6.0.2 desktop recovery, version support, release, and evidence | Release |
|
||||
| 2026-07-24 | v6.0.1 stabilization, release, upgrade, API, MCP, and evidence | Release |
|
||||
| 2026-07-24 | v6.0.0 harness, Buzz, release, upgrade, compatibility, and evidence | Release |
|
||||
| 2026-07-12 | v5.2.2 UI-audit fixes, release gates, desktop state, and evidence | Release |
|
||||
| 2026-06-05 | v5.0.0 stable release docs, install paths, release assets, RC notes | Codex |
|
||||
| 2026-03-25 | Full v3→v4 version references, governance docs, CHANGELOG, examples | VERITAS |
|
||||
| 2026-03-21 | v4.0 release documentation | TARS |
|
||||
|
||||
## Automation Plan
|
||||
|
||||
### Phase 1: Manual (Current)
|
||||
|
||||
- Doc update checklist in PR template
|
||||
- Freshness headers in docs
|
||||
- Doc Freshness registry records, with optional source markers where useful
|
||||
- Agent instructions include "update docs" step
|
||||
|
||||
### Phase 2: Hook-Based
|
||||
|
|
@ -108,7 +122,8 @@ project template, and harness-specific files only supplement the canonical
|
|||
rules. Key rules:
|
||||
|
||||
1. **Always update docs alongside code** — no code-only PRs for user-facing changes
|
||||
2. **Use freshness headers** — every doc starts with `<!-- doc-freshness: ... -->`
|
||||
2. **Track maintained living docs** — use the Doc Freshness registry; optional
|
||||
source headers are a reviewer aid, not the system of record
|
||||
3. **JSDoc is documentation** — route handlers and services must have JSDoc
|
||||
4. **Examples must work** — if you change an API, update the examples
|
||||
5. **CHANGELOG is mandatory** — every release gets an entry
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Steal these end-to-end flows when building your own automations. Each example sh
|
|||
```
|
||||
2. **Prompt (worker)**
|
||||
```
|
||||
Implement markdown lessonsLearned field on tasks (UI + API). Include migration + docs. Cross-model review required.
|
||||
Implement markdown lessonsLearned field on tasks (UI + API). Include migration + docs. Run the task's configured review gate, if any.
|
||||
```
|
||||
3. **Workflow**
|
||||
- `vk begin <id>`
|
||||
|
|
@ -38,7 +38,7 @@ Steal these end-to-end flows when building your own automations. Each example sh
|
|||
- Patch bulk archive handler
|
||||
- Add regression test (Playwright)
|
||||
3. CLI flow: `vk begin`, fix, `vk done "Bulk archive now calls API"`
|
||||
4. Cross-model review ensures UI + API parity.
|
||||
4. Focused tests verify UI + API parity; add independent review when required.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -54,11 +54,11 @@ Steal these end-to-end flows when building your own automations. Each example sh
|
|||
|
||||
## 4. Security Audit (RF-002 style)
|
||||
|
||||
**Goal:** Run cross-model audit on repo.
|
||||
**Goal:** Run a focused security audit on the repository.
|
||||
|
||||
1. Task -> `type=security`, `project=veritas-kanban`.
|
||||
2. Subtasks: scope, run Codex audit, run Claude review, compile findings, create issues.
|
||||
3. Agents spawn using research prompt template, save results to `refactoring/rf-002/*`.
|
||||
2. Subtasks: scope, inspect trust boundaries, validate findings, compile evidence, create issues.
|
||||
3. Use the security-review prompt and save durable results to the task's declared artifact path.
|
||||
4. Deliverables: Markdown report, HTML deck, GitHub issues.
|
||||
|
||||
---
|
||||
|
|
@ -88,7 +88,7 @@ For any workflow:
|
|||
2. **Prompt** stored in registry.
|
||||
3. **API/CLI** calls scripted (vk begin/done, time tracking, status updates).
|
||||
4. **Artifacts** saved to predictable paths and mirrored to Brain/engram if needed.
|
||||
5. **Cross-model review** if code/critical.
|
||||
5. **Focused review** when the task or configured governance policy requires it.
|
||||
6. **Lessons learned** field updated for systemic knowledge.
|
||||
|
||||
Use these recipes as seeds for your own automation playbooks.
|
||||
|
|
|
|||
|
|
@ -41,15 +41,16 @@ Use these recipes as starting points for v4.3 OpenAI Codex workflows in Veritas
|
|||
- test command output
|
||||
- task comment or deliverable listing changed files
|
||||
|
||||
6. Review:
|
||||
- Create a cross-model review task.
|
||||
- Assign it to a non-Codex reviewer.
|
||||
6. Optional review:
|
||||
- If the task or governance policy requires independent review, create a
|
||||
review task and assign it to the requested reviewer.
|
||||
|
||||
---
|
||||
|
||||
## 2. Codex Review Of A Claude-authored PR
|
||||
## 2. Optional Independent Review With Codex
|
||||
|
||||
**Goal:** Use Codex as the opposite-model reviewer for a Claude-authored branch.
|
||||
**Goal:** Use Codex as an independent reviewer when a task or governance policy
|
||||
explicitly requires one. This is not a default delivery step.
|
||||
|
||||
1. Keep the original implementation task `in-progress`.
|
||||
2. Trigger a Codex review action:
|
||||
|
|
@ -152,7 +153,7 @@ steps:
|
|||
agent: reviewer
|
||||
depends_on: [implement]
|
||||
input: |
|
||||
Review Codex's implementation using docs/SOP-cross-model-code-review.md.
|
||||
Run the configured independent review using docs/SOP-cross-model-code-review.md.
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
|
|
|
|||
201
docs/FEATURES.md
201
docs/FEATURES.md
|
|
@ -117,9 +117,10 @@ The Kanban board is the central interface — a drag-and-drop workspace that ref
|
|||
- **Markdown storage** — Tasks stored as human-readable `.md` files with YAML frontmatter
|
||||
- **Dark/light mode** — Ships dark by default with a toggle in Settings → General → Appearance; persists to localStorage; inline script in `index.html` prevents flash of wrong theme on load
|
||||
- **Filter bar** — Search tasks by text, filter by project and task type; filters persist in URL query params
|
||||
- **Desktop shell controls** — Native-app-style toolbar with workspace selection, health state, view toggles, and left/right/bottom panel controls shared by the web and macOS app shells
|
||||
- **Desktop shell controls** — Native-app-style toolbar with workspace selection, health state, view toggles, and bounded left/right/chat dock controls shared by the web and macOS app shells
|
||||
- **Native version identity** — The macOS application menu opens an offline About panel and copies a redacted support string from the same authoritative Electron version, embedded release commit, release channel, OS, and architecture record exposed by the desktop bridge
|
||||
- **Mobile shell controls** — Compact navigation uses bounded labels and full accessible names; Board Chat stays fixed above the bottom navigation and device safe area
|
||||
- **Resizable Workbench** — Board Chat and Squad Chat live in a collapsible bottom panel that can be resized vertically for longer chat sessions
|
||||
- **Resizable Workbench** — Board Chat and Squad Chat open in a right dock by default, can switch to Bottom without losing the active conversation, and clamp their width or height to keep the application shell recoverable
|
||||
- **Bulk operations** — Select multiple tasks to move, archive, or delete in batch; select-all toggle
|
||||
- **Keyboard shortcuts** — Navigate tasks (j/k, arrows), open (Enter), close (Esc), create (c), move to column (1-4), help (?)
|
||||
- **Loading skeleton** — Shimmer placeholders while the board loads
|
||||
|
|
@ -296,6 +297,9 @@ First-class support for autonomous coding agents.
|
|||
- **Running indicator on cards** — Animated spinner on task cards when an agent is actively working
|
||||
- **Agent output stream** — Real-time agent output via WebSocket with auto-scroll and clear
|
||||
- **Causal run-event journal** — OpenClaw, Codex CLI, Codex SDK, Codex app-server, Claude Code, ACP stdio, and Hermes map provider output into one bounded, redacted, append-only `run-event/v1` stream with per-attempt ordering, provider deduplication, REST cursor replay, gap-free WebSocket reconnect, and compatible legacy output projections
|
||||
- **Provider-neutral progress watchdog** — A versioned, bounded evaluator detects identical tool, error, and assistant-tail repetition, short multi-step cycles, repeated failed edits, and sustained time or spend without durable progress from the shared run journal. Policy controls confidence escalation, progress signals, allowed repetition leases, and recovery posture. The restart-safe server coordinator journals attributed findings and action outcomes, rehydrates per-turn and per-run recovery use, uses verified provider-native steering, and stops the exact attempt for configured pause or cancel. Retry and fallback stay behind the governed recovery planner. Permission-gated APIs expose durable findings and actor-attributed acknowledge, continue, or cancel overrides
|
||||
- **Governed oversized-output spill** — Tool, command, MCP, provider, and other oversized run payloads use one provider-neutral policy: the complete redacted body is stored behind an opaque workspace/run-scoped artifact ID while the event carries a bounded preview, integrity hash, retention state, and safe query hints. Text/JSON support byte, line, and bounded JSON-path queries; binary, invalid UTF-8, and compressed bodies quarantine by default
|
||||
- **Dependency health and load shedding** — Provider/model calls, selected agent-host starts, MCP discovery and tools, outbound integrations, and storage operations report into durable restart-safe `dependency-circuit/v1` state. Failure-rate and slow-call thresholds open circuits; bounded half-open probes test recovery; route selection excludes unhealthy candidates; shared retry budgets prevent retry amplification; launch explain, run events, telemetry, completion evidence, and deep health expose redacted posture; and admin-only reset plus expiring allow/block overrides provide governed recovery controls
|
||||
- **Provider-neutral runtime hooks** — Trusted in-process features can register bounded `runtime-hook/v1` pre-dispatch decisions and passive post-event observations with deterministic scope ordering, timeouts, reentrancy protection, dry-run, and causal evidence; arbitrary executable and HTTP handlers remain unsupported
|
||||
- **Provider-native approval broker** — Provider requests pause on an exact action hash, persist a bounded workspace-scoped review record, and resume only after an authenticated compare-and-set approve/reject decision; expiry, interruption, cancellation, stale evidence, changed arguments, and duplicate decisions fail closed
|
||||
- **Run-scoped tool control plane** — Versioned MCP definitions and discovery,
|
||||
|
|
@ -337,6 +341,25 @@ First-class support for autonomous coding agents.
|
|||
- **Team roster routing manifests** — Workspace coordinators can define enabled members, capabilities, routing rules, fallbacks, reviewers, and escalation posture before `/api/agents/route` selects an agent
|
||||
- **Workspace capability discovery** — Trusted workspace catalogs expose supported task types, SLA/queue posture, intake requirements, and delegated-work packaging so cross-workspace handoffs are explicit
|
||||
- **Agent profile packages** — Reusable YAML/JSON packages bundle role, runtime, model, prompt instructions, tools, permissions, sandbox, budget, workflow, and health metadata for portable task launches
|
||||
- **Phase capability contract and transition journal** — Versioned explore,
|
||||
plan, implement, verify, and publish profiles compile a monotonic
|
||||
intersection across parent, agent, sandbox, tool-catalog, and launch policy
|
||||
authority. Unsupported required dimensions fail closed, legacy mode remains
|
||||
explicit, and plan artifacts stay bound to one harness-owned exact path.
|
||||
Active runs persist append-only compare-and-set transitions with actor,
|
||||
authority delta, policy, approval or override, manifest, and event evidence.
|
||||
Expansion requires exact-action approval; administrator overrides expire and
|
||||
durably restore the prior phase. Task and workflow launches, retries and
|
||||
fallbacks, provider changes, conversation continuations, and active-run
|
||||
controls bind the effective phase and exact parent evidence before attempt
|
||||
mutation. Run tool catalogs omit disallowed tools and credentials, mediated
|
||||
calls re-check active transition evidence, approvals cannot outlive their
|
||||
bound phase, and completion plus the task timeline expose the same
|
||||
server-owned evidence. ACP stdio provides pre-execution command and external
|
||||
action mediation; other adapters fail explicit phases closed when equivalent
|
||||
controls are unavailable. See
|
||||
[Phase Capability Profiles](architecture/PHASE-CAPABILITY-PROFILES.md) and
|
||||
[Phase Transition Journal](architecture/PHASE-TRANSITION-JOURNAL.md).
|
||||
- **Provider runtime manifests** — Every executable adapter records a versioned, evidence-backed capability snapshot and digest on the attempt, history, trace, and log; provider version skew reruns conformance and unsupported configured providers fail closed instead of falling back to OpenClaw
|
||||
- **Cross-harness compatibility matrix** — Buzz, Grok Build, OpenAI Codex app-server, Claude Code, and GitHub Copilot CLI publish exact reviewed builds, source-availability caveats, deterministic fixture identity, capability evidence, limitations, and live support tiers through one API record consumed by Settings, `vk doctor`, telemetry, and [operator guidance](HARNESS-COMPATIBILITY.md)
|
||||
- **Harness conformance suites** — Versioned seeded scenarios compare
|
||||
|
|
@ -449,7 +472,7 @@ Implemented:
|
|||
successful provider result fails closed.
|
||||
- **Session continuity evidence** — Claude `session_id` is stored on the attempt
|
||||
and separately from turn/item identity in the event schema.
|
||||
- **Versioned readiness** — The exact v2.1.218 runtime, probe revision 14,
|
||||
- **Versioned readiness** — The exact v2.1.218 runtime, probe revision 16,
|
||||
authentication posture, and safe agent-discovery summary determine support
|
||||
status.
|
||||
- **Capability truth** — The shared approval broker is available, but this
|
||||
|
|
@ -582,6 +605,15 @@ Reviewed promotion queue for agent corrections, repeated mistakes, and durable l
|
|||
- **Task lesson promotion** — Accepted task-linked candidates append a reviewed reflection lesson to the task's lessons field
|
||||
- **Duplicate grouping and merge** — Similar candidates share a duplicate key and can be soft-merged into a representative while preserving audit history
|
||||
- **Redaction at ingestion** — Tokens, credentials, and local private paths are redacted before candidates are stored
|
||||
- **Durable extraction jobs** — `reflection-extraction-job/v1` persists only source task, attempt, completion, digest, and event identities; raw conversations and unrestricted transcripts are not copied into the queue
|
||||
- **Lease-safe worker foundation** — File and SQLite repositories atomically enforce global and per-workspace concurrency, stable idempotent enqueue, lease ownership and renewal, deterministic retry backoff, restart recovery, and bounded dead-lettering
|
||||
- **Non-blocking completion intake** — Eligible terminal completions schedule extraction after the authoritative task update; interrupted or empty completions are skipped
|
||||
- **Bounded extraction worker** — The background worker reloads the identified durable completion, verifies its identity and digest, and exposes only bounded summaries, blockers, verified evidence, and verification results to a typed extractor
|
||||
- **Safe pending output** — Extracted candidates include run/event attribution, proposed scope, confidence, rationale, applicability, and contradiction links; deterministic candidate idempotency prevents duplicates after retries
|
||||
- **Ranked reviewed retrieval** — Accepted task lessons are selected by task relevance, confidence, freshness, and observed use; only the top eight enter a run
|
||||
- **Run attribution** — Persisted task envelopes carry the reflection, source run, and source event IDs that influenced the run; preview envelopes do not increment use
|
||||
- **Inspectable consolidation proposals** — Explicit candidate sets are serialized per memory domain into durable, idempotent merge, contradiction, decay, and wider-promotion review diffs; no candidate is silently deleted or promoted
|
||||
- **Typed durable promotions** — Memory, team roster, agent profile, task template, decision, and policy changes require an authenticated reviewer plus target-specific validated input; unowned targets fail closed
|
||||
- **Settings UI** — Review, accept, reject, delete, and merge candidates from Settings → Reflections
|
||||
- **Audit trail** — Create, accept, reject, merge, and delete actions write metadata-only audit events
|
||||
|
||||
|
|
@ -602,11 +634,11 @@ Reusable resources mountable across projects with full CRUD API and Settings tab
|
|||
Automated staleness detection for project documentation with real-time tracking and alerting. Added in v3.2.
|
||||
|
||||
- **Freshness tracking** — Track document staleness with freshness scores, alerts, and optional auto-review task creation
|
||||
- **Freshness headers** — YAML frontmatter with `fresh-days`, `owner`, `last-verified` fields
|
||||
- **Tracked metadata** — Registry records store review dates, owners, paths, thresholds, and tags without rewriting source documents
|
||||
- **Steward workflow** — Assigned doc owners responsible for periodic review
|
||||
- **Staleness API** — Query which docs need review based on freshness thresholds at `/api/doc-freshness`
|
||||
- **Configurable thresholds** — Set staleness thresholds via Settings → Doc Freshness
|
||||
- **3-phase automation** — Manual → scheduled checks → CI integration
|
||||
- **3-phase automation** — Manual registry review → scheduled checks → CI integration
|
||||
- **Inspired by** @mvoutov's BoardKit Orchestrator ("stale docs = hallucinating AI")
|
||||
|
||||
---
|
||||
|
|
@ -620,7 +652,7 @@ Real-time agent-to-agent communication channel for multi-agent collaboration. Sh
|
|||
|  |  |
|
||||
|
||||
- **WebSocket-powered chat** — Messages broadcast in real time to all connected clients
|
||||
- **Resizable Workbench panel** — Board Chat and Squad Chat share the bottom Workbench surface, which can be collapsed or resized vertically instead of floating off-screen
|
||||
- **Resizable Workbench dock** — Board Chat and Squad Chat share one dock that defaults Right, optionally moves to Bottom, isolates chat scrolling, and keeps Close, Escape, Back, and Reset Layout recovery available
|
||||
- **Local shared log** — Squad Chat stores and streams messages; it does not wake or reply through an external agent unless a webhook, OpenClaw Direct path, or orchestrator is configured
|
||||
- **Threaded coordination** — Reply-to links render compact threads for long multi-agent runs
|
||||
- **Unread and mentions** — Per-actor unread state persists across refreshes, and mentions create local notifications linked back to messages
|
||||
|
|
@ -957,6 +989,16 @@ Execute a single agent prompt with configurable retries.
|
|||
- Template rendering with `{{variable}}` and `{{nested.path}}` substitution
|
||||
- Acceptance criteria validation (substring, regex, JSON path)
|
||||
- Retry routing: retry same step, retry different step, escalate
|
||||
- Production retry/fallback state machine: only explicitly transient failure
|
||||
classes retry; each decision persists causal parents, jittered backoff,
|
||||
route and manifest evidence, and cumulative budget. Fallback agents must pass
|
||||
runtime capability and sandbox preflight before launch.
|
||||
- Optional `phase` values are `explore`, `plan`, `implement`, `verify`, and
|
||||
`publish`. The phase is resolved before the step enters a running state.
|
||||
Retries, fallbacks, reused sessions, and provider changes intersect the exact
|
||||
parent phase and cannot widen its authority. Explicit phase steps remain
|
||||
fail-closed until the selected adapter and tool policy provide the
|
||||
command/external-action enforcement completed in #1033.
|
||||
|
||||
#### 2. Loop Steps
|
||||
|
||||
|
|
@ -1073,8 +1115,15 @@ All three types are backward-compatible — substring matching was the original
|
|||
|
||||
Every workflow run persists its state to disk, enabling:
|
||||
|
||||
- **Server restart recovery** — Runs can resume from last checkpoint
|
||||
- **Retry with exponential backoff** — Configurable `retry_delay_ms` prevents rapid retry loops
|
||||
- **Server restart recovery** — Scheduled retries and fallbacks are restored
|
||||
from their durable step records
|
||||
- **Retry with exponential backoff** — `retry_delay_ms` supplies the base delay;
|
||||
recovery applies bounded exponential backoff with jitter
|
||||
- **Fail-closed fallback** — Explicit `agent:<id>` escalation and compatible
|
||||
workspace fallback routes run only after retry exhaustion and runtime/sandbox
|
||||
preflight
|
||||
- **Operator cancellation** — Exact pending workflow recovery can be cancelled
|
||||
before another provider launch
|
||||
- **Progress file tracking** — Shared `progress.md` per run for context passing:
|
||||
- Each step appends its output with timestamp
|
||||
- Templates can access `{{progress}}` for previous step context
|
||||
|
|
@ -1131,13 +1180,40 @@ Reusable launch-time sandbox presets for provider execution guardrails.
|
|||
|
||||
- Create, edit, enable, disable, and delete custom presets in **Settings -> Agents -> Sandbox Policies**.
|
||||
- Assign presets to agent profiles or workflow agents; one-off agent starts can pass `sandboxPresetId` to override the profile default.
|
||||
- Presets declare filesystem read/write paths, denied paths, dotfile masking, network default egress, allowed hosts and paths, environment passthrough keys, credential mode, and broker references.
|
||||
- Presets declare filesystem read/write paths, denied paths, dotfile masking,
|
||||
network default egress, allowed and denied hosts, methods and paths,
|
||||
private/loopback/metadata protection, scoped approval eligibility, environment
|
||||
passthrough keys, credential mode, and broker references.
|
||||
- Dry-runs compare a preset against provider capabilities before execution and show the effective sandbox mode, network state, environment allowlist, unsupported controls, and governance trace ID.
|
||||
- Dry-runs also compile `run-egress-policy/v1`: host rules are normalized, deny
|
||||
rules take precedence, unsafe global allow wildcards fail validation, and a
|
||||
deterministic policy digest binds later gateway launch evidence.
|
||||
- Selective local-provider policies start an authenticated loopback gateway
|
||||
before provider dispatch. Veritas injects HTTP and HTTPS proxy variables plus
|
||||
an authenticated `socks5h` all-proxy listener, clears proxy bypass variables,
|
||||
pins the evaluated DNS address for transport, and stops both listeners with
|
||||
the run. Remote OpenClaw execution fails closed when the preset requires this
|
||||
local gateway.
|
||||
- Optional `VERITAS_EGRESS_UPSTREAM_PROXY` routing sends only policy-approved,
|
||||
DNS-pinned destinations through an operator HTTP CONNECT proxy. Credentials
|
||||
stay memory-only and evidence exposes only the upstream mode.
|
||||
- Approval-eligible blocks pause at the gateway on a durable exact-action
|
||||
approval. Explicit denies and protected address classes cannot be overridden.
|
||||
Approved requests retain the approval ID in metadata-only governance and
|
||||
`network.egress` telemetry evidence.
|
||||
|
||||
**Enforcement:**
|
||||
|
||||
- Required controls fail closed before agent or workflow launch when the selected provider cannot support them.
|
||||
- Advisory controls warn and record trace evidence without blocking the run.
|
||||
- Required filesystem rules compile into a pre-spawn, descendant-inherited
|
||||
boundary with exact read, write, deny, dotfile, protected-metadata,
|
||||
run-scoped temporary-directory, and cleanup evidence. Ambiguous mounts,
|
||||
external hard-link aliases, backend byte drift, or cleanup paths with
|
||||
symlinked ancestors fail closed.
|
||||
- Provider-native enforcement qualifies only when the exact runtime manifest
|
||||
proves every active filesystem and lifecycle capability. Coarse sandbox
|
||||
modes remain advisory.
|
||||
- Credential references and environment-style `name=value` values are redacted in dry-run output and governance traces.
|
||||
- Credential definitions and run-bound leases use metadata-only versioned
|
||||
records, opaque hashed handles, exact action/manifest binding, atomic
|
||||
|
|
@ -1150,7 +1226,9 @@ Reusable launch-time sandbox presets for provider execution guardrails.
|
|||
catalogs. Existing provider authentication and explicit environment
|
||||
passthrough are not mislabeled as brokered.
|
||||
- Provider capability checks currently distinguish Codex CLI, Codex SDK,
|
||||
Codex app-server, Claude Code, Hermes, and OpenClaw execution behavior.
|
||||
Codex app-server, Claude Code, ACP stdio harnesses, Hermes, and OpenClaw
|
||||
execution behavior. Gateway capability evidence is invalidated by provider
|
||||
runtime probe revision 16.
|
||||
|
||||
### Session Isolation
|
||||
|
||||
|
|
@ -1675,6 +1753,10 @@ New endpoints for advanced metrics and visualization (v1.6):
|
|||
- **Status timeline** — Daily Activity (75%) + Recent Status Changes (25%) side-by-side layout
|
||||
- **Section collapsing** — Dashboard sections apply `overflow-hidden` only when collapsed
|
||||
- **Daily digest** — Summary of the day's activity: tasks completed/created, agent runs, token usage, failures and issues
|
||||
- **Reconciled Operations Digest** — Current active/blocked/stuck state is
|
||||
labeled separately from windowed completions, runs, tokens, and observed
|
||||
runtime; board inventory, exclusion reasons, source IDs, and unknown metadata
|
||||
findings make every headline count auditable
|
||||
- **Task-level metrics** — Per-task panel showing attempt history, token counts, duration, cost, and status timeline
|
||||
- **Export dialog** — Export dashboard data for external analysis
|
||||
|
||||
|
|
@ -1684,7 +1766,7 @@ New endpoints for advanced metrics and visualization (v1.6):
|
|||
|
||||
Event-based telemetry system powering dashboard analytics.
|
||||
|
||||
- **Event types** — `run.started`, `run.completed`, `run.tokens` for tracking agent execution lifecycle
|
||||
- **Event types** — `run.started`, `run.completed`, `run.tokens`, and metadata-only `network.egress` decisions for tracking execution and network policy outcomes
|
||||
- **Token tracking** — Input tokens, output tokens, cache tokens, and cost per run
|
||||
- **Duration tracking** — Millisecond-precision run duration with 7-day cap validation (604,800,000 ms)
|
||||
- **Retention policy** — Configurable retention period (default: 30 days) with automatic cleanup of old events
|
||||
|
|
@ -1770,23 +1852,26 @@ Added in v3.3.2.
|
|||
|
||||
### Agent Commands
|
||||
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `vk start <id>` | Start an agent on a code task (`--agent` to choose) |
|
||||
| `vk launch-preview <id>` | Preview immutable launch evidence without dispatch |
|
||||
| `vk stop <id>` | Stop a running agent |
|
||||
| `vk agent:resume <id> --source-attempt <id> -m <text>` | Resume an exact provider conversation |
|
||||
| `vk agent:follow-up <id> --source-attempt <id> -m <text>` | Start a native follow-up turn |
|
||||
| `vk agent:fork <id> --source-attempt <id> -m <text>` | Fork native provider history |
|
||||
| `vk agent:steer <id> --attempt <id> -m <text>` | Steer the exact active provider turn |
|
||||
| `vk agent:interrupt <id> --attempt <id>` | Interrupt the exact active attempt |
|
||||
| `vk agent:compact <id> --attempt <id>` | Compact a supported provider conversation |
|
||||
| `vk agent:archive <id> --attempt <id>` | Archive a supported provider conversation |
|
||||
| `vk agent:close <id> --attempt <id>` | Close a supported provider conversation |
|
||||
| `vk agents:pending` | List pending agent requests |
|
||||
| `vk agents:status <id>` | Check agent running status |
|
||||
| `vk agents:complete <id> -s --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (success) |
|
||||
| `vk agents:complete <id> -f --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (failure) |
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `vk start <id>` | Start an agent on a code task (`--agent` to choose) |
|
||||
| `vk launch-preview <id>` | Preview immutable launch evidence without dispatch |
|
||||
| `vk workspace-trust scan <id>` | Inventory repository-controlled launch inputs |
|
||||
| `vk workspace-trust decide <id> --mode <mode> --inventory <digest> --reason <text>` | Record an exact-inventory trust decision |
|
||||
| `vk workspace-trust revoke <id> --inventory <digest> --reason <text>` | Revoke the current workspace authorization |
|
||||
| `vk stop <id>` | Stop a running agent |
|
||||
| `vk agent:resume <id> --source-attempt <id> -m <text>` | Resume an exact provider conversation |
|
||||
| `vk agent:follow-up <id> --source-attempt <id> -m <text>` | Start a native follow-up turn |
|
||||
| `vk agent:fork <id> --source-attempt <id> -m <text>` | Fork native provider history |
|
||||
| `vk agent:steer <id> --attempt <id> -m <text>` | Steer the exact active provider turn |
|
||||
| `vk agent:interrupt <id> --attempt <id>` | Interrupt the exact active attempt |
|
||||
| `vk agent:compact <id> --attempt <id>` | Compact a supported provider conversation |
|
||||
| `vk agent:archive <id> --attempt <id>` | Archive a supported provider conversation |
|
||||
| `vk agent:close <id> --attempt <id>` | Close a supported provider conversation |
|
||||
| `vk agents:pending` | List pending agent requests |
|
||||
| `vk agents:status <id>` | Check agent running status |
|
||||
| `vk agents:complete <id> -s --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (success) |
|
||||
| `vk agents:complete <id> -f --attempt-id <id> --manifest-digest <sha256:...>` | Mark the matching agent attempt complete (failure) |
|
||||
|
||||
### Automation Commands
|
||||
|
||||
|
|
@ -1861,7 +1946,7 @@ vk done <id> "Added OAuth2 with Google and GitHub providers"
|
|||
|
||||
## MCP Server
|
||||
|
||||
Model Context Protocol server for AI assistant integration (Claude Desktop, OpenClaw, Cursor, Codex, etc.). 41 tools across task management, agent orchestration, automation, notifications, summaries, sprint management, comments, projects, and run-scoped tool control.
|
||||
Model Context Protocol server for AI assistant integration (Claude Desktop, OpenClaw, Cursor, Codex, etc.). 42 tools across task management, agent orchestration, automation, notifications, summaries, sprint management, comments, projects, and run-scoped tool control.
|
||||
|
||||
### Tools
|
||||
|
||||
|
|
@ -1875,6 +1960,7 @@ Model Context Protocol server for AI assistant integration (Claude Desktop, Open
|
|||
| `delete_task` | Permanently delete a task |
|
||||
| `start_agent` | Start an AI agent on a code task |
|
||||
| `stop_agent` | Stop a running agent |
|
||||
| `cancel_agent_recovery` | Cancel an exact pending retry or fallback |
|
||||
| `list_pending_automation` | List automation tasks awaiting execution |
|
||||
| `list_running_automation` | List currently running automation tasks |
|
||||
| `start_automation` | Start an automation task via sub-agent |
|
||||
|
|
@ -1946,6 +2032,28 @@ Defense-in-depth security model with multiple authentication methods and hardene
|
|||
- **Password strength indicator** — Visual strength meter in the Security settings tab (weak/fair/good/strong/very strong)
|
||||
- **Password change** — Change password from the Security settings tab with current password verification
|
||||
|
||||
### Workspace Execution Trust
|
||||
|
||||
- **Pre-launch inventory** - Scans repository-controlled harness instructions,
|
||||
provider configuration, MCP servers, hooks, language-server settings,
|
||||
workflows, extensions, skills, and agent definitions before an executable
|
||||
provider launch.
|
||||
- **Stable identity** - Binds decisions to the canonical worktree, repository,
|
||||
Git common directory, and credential-redacted remote identity instead of a
|
||||
reusable path string.
|
||||
- **Exact authorization** - Trusted, restricted, denied, and revoked records
|
||||
are actor-attributed and inventory-bound. Content drift, expiry, or revocation
|
||||
fails closed.
|
||||
- **Restricted mode** - Requires enforced read-only filesystem access, disabled
|
||||
network, no task credentials, no project tool servers, and no external
|
||||
mutation.
|
||||
- **Immutable launch evidence** - Records only redacted identity, inventory,
|
||||
capability, project-policy, and decision evidence, then rescans immediately
|
||||
before provider creation.
|
||||
|
||||
See
|
||||
[Workspace Execution Trust](architecture/WORKSPACE-EXECUTION-TRUST.md).
|
||||
|
||||
### Network & Headers
|
||||
|
||||
- **CSP headers** — Content Security Policy via [Helmet](https://helmetjs.github.io/) with nonce-based script/style allowlisting and a documented `style-src-attr` exception for runtime React style attributes
|
||||
|
|
@ -2098,6 +2206,7 @@ RESTful API designed for both human and AI agent consumption.
|
|||
| `/api/v1/changes` | Efficient polling change feed |
|
||||
| `/api/v1/agents/register` | Agent registry (register, list, heartbeat, stats, deregister) |
|
||||
| `/api/v1/agents/permissions` | Agent permission levels and approval workflows |
|
||||
| `/api/v1/run-terminals` | Approved run commands, handles, output, waits, and control |
|
||||
| `/api/v1/hooks` | Task lifecycle hooks (list, create, update, delete, events) |
|
||||
| `/api/v1/errors` | Error learning (record, search, stats) |
|
||||
| `/api/v1/docs` | Documentation freshness (list, staleness, verify) |
|
||||
|
|
@ -2234,13 +2343,13 @@ TRUST_PROXY=true
|
|||
|
||||
## Storage & Architecture
|
||||
|
||||
Abstract storage layer that decouples business logic from the filesystem.
|
||||
Deep storage modules decouple business logic from filesystem and SQLite details.
|
||||
|
||||
- **Repository pattern** — 5 repository interfaces abstract data access: `ActivityRepository`, `TemplateRepository`, `StatusHistoryRepository`, `ManagedListRepository`, `TelemetryRepository`
|
||||
- **StorageProvider** — Central provider extended with all repository implementations; services depend on interfaces, not filesystem calls
|
||||
- **`fs-helpers.ts`** — Centralized filesystem access module; the only file in the codebase that imports `fs` directly
|
||||
- **Service migration** — All 10 services migrated off direct `fs` imports to use the repository interfaces
|
||||
- **Extensibility** — Repository interfaces enable future storage backends (database, cloud storage) without changing service logic
|
||||
- **Repository contracts** — Persisted activity, progress, status history, deliverables, workflows, broadcasts, conflicts, delegation, ceremony, error analysis, permissions, lifecycle configuration, schedules, reflection, chat, tasks, telemetry, and managed content use explicit interfaces.
|
||||
- **File and SQLite parity** — Both backends preserve validated schemas, containment, locking, atomic mutation, pagination, and migration behavior appropriate to each domain.
|
||||
- **Service boundary gate** — Production services cannot introduce direct filesystem imports; authoritative reads and writes flow through the storage layer.
|
||||
- **Canonical runtime paths** — `DATA_DIR` and `VERITAS_DATA_DIR`, legacy discovery, backup, integrity, migration, health, and Docker mounts resolve through the same path contract.
|
||||
- **Extensibility** — Business services depend on domain operations instead of storage layout, allowing backend changes without duplicating product rules.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -2262,7 +2371,8 @@ Production-ready deployment and development tooling.
|
|||
|
||||
- **GitHub Actions** — CI pipeline on push to `main` and pull requests
|
||||
- **Concurrency control** — In-progress runs cancelled when new commits push
|
||||
- **Pipeline jobs** — Lint and warning budget, type check, workspace unit tests, production build, and security audit
|
||||
- **Fast pull-request jobs** — Source-policy selection, lint and warning budget, typecheck, production build, dependency audit, CodeQL, and gitleaks
|
||||
- **Milestone jobs** — Workspace tests, critical-path coverage, Playwright, desktop artifacts, load checks, and Docker contracts run for `ci:full`, scheduled, or manual milestones
|
||||
- **Scheduled QA** — Weekly and manually triggered Playwright and k6 gates run outside the fast PR path
|
||||
- **Release validation** — `pnpm validate:release` checks root/shared/server/web/CLI/MCP/desktop versions, the release-major document set, built artifacts, and optional GitHub tag/release state
|
||||
- **pnpm caching** — Dependency cache for faster CI runs
|
||||
|
|
@ -2288,12 +2398,14 @@ Production-ready deployment and development tooling.
|
|||
|
||||
## Testing
|
||||
|
||||
Multi-layer testing strategy.
|
||||
Multi-layer, milestone-scoped verification strategy. Exact release counts live
|
||||
in `docs/V6-RC-EVIDENCE-PACKET.md`; historical counts are not treated as current
|
||||
proof.
|
||||
|
||||
### Unit Tests (Vitest)
|
||||
|
||||
- **119 test files** · **1,699 tests passing** across server and frontend
|
||||
- **Server (105 files, 1,570 tests):**
|
||||
- **Workspace coverage** — Server, web, CLI, MCP, shared contracts, and desktop packages are included in the canonical release gate.
|
||||
- **Server coverage includes:**
|
||||
- All middleware (auth, rate limiting, request ID, API versioning, cache control, validation, response envelope, request timeout)
|
||||
- Core services (task, template, telemetry, notification, activity, sprint, diff, conflict, summary, status history, digest, attachment, text extraction, migration, managed list, broadcast, automation, blocking, failure alert, metrics, settings, JWT rotation, MIME validation, preview, trace, circuit breaker)
|
||||
- Route handlers (tasks, task archive, task comments, task subtasks, task time, auth, agent status, automation, config, notifications, templates, health, misc routes)
|
||||
|
|
@ -2302,7 +2414,7 @@ Multi-layer testing strategy.
|
|||
- Prometheus metrics (counters, gauges, histograms, registry, collector middleware)
|
||||
- Environment variable validation
|
||||
- Circuit breaker transitions (18 tests covering open/half-open/closed states — added in v3.3.2)
|
||||
- **Frontend (14 files, 129 tests):**
|
||||
- **Frontend coverage includes:**
|
||||
- API client helpers and task operations
|
||||
- Custom hooks: useWebSocket, useKeyboard (keyboard shortcuts)
|
||||
- Components: KanbanBoard, TaskCard, ErrorBoundary, AgentStatusIndicator, WebSocketIndicator
|
||||
|
|
@ -2311,9 +2423,8 @@ Multi-layer testing strategy.
|
|||
|
||||
### End-to-End Tests (Playwright)
|
||||
|
||||
- **7 spec files** covering critical user flows
|
||||
- **19/19 tests passing**
|
||||
- **Test suites:**
|
||||
- **Chromium and WebKit projects** cover critical user flows at declared QA and release milestones.
|
||||
- **Test suites include:**
|
||||
- Health check
|
||||
- Settings management
|
||||
- Task creation
|
||||
|
|
@ -2414,7 +2525,9 @@ Define scoring profiles with weighted criteria and evaluate agent outputs agains
|
|||
|
||||
**Key capabilities:**
|
||||
|
||||
- Four scorer types: `RegexMatch`, `KeywordContains`, `NumericRange`, `CustomExpression`
|
||||
- Four bounded scorer types: `RegexMatch`, `KeywordContains`, `NumericRange`, `OccurrenceRatio`
|
||||
- Regex evaluation runs outside the server event loop with input, pattern, and time limits
|
||||
- Occurrence ratios use literal values and optional numeric normalization; arbitrary code is never evaluated
|
||||
- Weighted scorers with optional `target`: `action`, `output`, or `combined`
|
||||
- Composite scoring methods: `weightedAvg`, `minimum`, `geometricMean`
|
||||
- Per-evaluation history with scorer-level breakdowns
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ A working board is not the same as agent-ready or external wake/delivery-ready.
|
|||
|
||||
| What | Command | Notes |
|
||||
| ----------------- | ------------------ | ----------------------------------------------------------------------- |
|
||||
| Node.js | `node -v` | Requires **22+**. Install via Volta/nvm if older. |
|
||||
| pnpm | `pnpm -v` | Requires **11.1.1+**. Prefer `corepack prepare pnpm@11.1.1 --activate`. |
|
||||
| Git | `git --version` | Any current version works. |
|
||||
| Node.js | `node -v` | Requires **22.22.1+**. Install via Volta/nvm if older. |
|
||||
| pnpm | `pnpm -v` | Use the repository-pinned **11.1.1** release. |
|
||||
| Git | `git --version` | Requires **2.38+**. |
|
||||
| (Optional) Docker | `docker --version` | Needed only if you prefer containers. |
|
||||
|
||||
That's it. No database, no extra services.
|
||||
|
|
@ -190,8 +190,12 @@ This section is optional. Agents interact through HTTP + WebSocket; nothing is h
|
|||
```
|
||||
7. **Agent workflow** (example prompt to an agent runner):
|
||||
```
|
||||
Hey Veritas, pick up task <ID>. Set status to in-progress, start the timer, do the work, then call `vk done <id> "summary"` when finished. Use cross-model review if you wrote code.
|
||||
Start the configured agent on task <ID>. Use the persisted task envelope,
|
||||
assigned worktree, and run-scoped tool catalog. Return focused verification
|
||||
and a concise completion summary through the harness result.
|
||||
```
|
||||
Managed agents must not call `vk begin`, `vk done`, or lifecycle callbacks.
|
||||
See [AGENTS-TEMPLATE.md](AGENTS-TEMPLATE.md).
|
||||
8. **Agent completion**
|
||||
- Verify `tasks/active/...` reflects status/time tracking
|
||||
- Check `.veritas-kanban/logs/agents.log` for run details
|
||||
|
|
@ -249,7 +253,7 @@ BoardKit Orchestrator inspired us here: keep prompts, skills, and guidelines in
|
|||
prompt-registry/
|
||||
├── sprint-planning.md # Break epics into sprints
|
||||
├── worker-handoff.md # PM → Worker assignment
|
||||
├── cross-model-review.md # Claude ↔ GPT review gate
|
||||
├── cross-model-review.md # Optional independent review
|
||||
├── feature-development.md # E2E feature implementation
|
||||
├── bug-triage.md # Investigation and fix
|
||||
├── research-report.md # Deep research deliverable
|
||||
|
|
|
|||
|
|
@ -34,7 +34,10 @@ in Settings -> Maintenance and is backed by `/api/v1/maintenance`.
|
|||
- Destructive cleanup must require explicit confirmation and must never delete
|
||||
active task worktrees or current run state silently.
|
||||
- Debug bundles include redacted log tails, health metadata, storage summaries,
|
||||
lifecycle policy metadata, and work-product preview metadata.
|
||||
lifecycle policy metadata, work-product preview metadata, and a bounded
|
||||
`phase-authority.json` diagnostic export. Phase diagnostics retain identities,
|
||||
source kinds, scope counts, transition expansions, and completion bindings
|
||||
while omitting exact scopes, paths, credential references, and full digests.
|
||||
- Maintenance summaries and log-tail responses redact local log paths before
|
||||
returning data to the UI.
|
||||
- SQLite posture diagnostics omit the database path, mount point, and mount
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
# SOP: Agent Task Workflow (Create → Work → Complete)
|
||||
|
||||
Use this playbook anytime an agent (human or LLM) takes a task from **todo** to **done**. It standardizes status changes, time tracking, summaries, and ensures telemetry stays usable.
|
||||
Use this playbook when a human or external self-reporting agent takes a task
|
||||
from **todo** to **done**. It standardizes status changes, time tracking,
|
||||
summaries, and telemetry.
|
||||
|
||||
This SOP assumes an agent or human is already doing the work. Veritas Kanban can create agent requests and track status, but it does not execute model work unless a runner/provider such as OpenClaw, Codex CLI/SDK, Codex Cloud, or a custom process is configured.
|
||||
> Managed Buzz, Grok Build, Codex, Claude Code, Copilot CLI, Hermes, and
|
||||
> OpenClaw runs do not call `vk begin`, `vk done`, lifecycle endpoints, or
|
||||
> telemetry APIs. VK and the selected adapter own those transitions. Managed
|
||||
> agents follow the shared protocol in [AGENTS-TEMPLATE.md](AGENTS-TEMPLATE.md).
|
||||
|
||||
---
|
||||
|
||||
## Roles
|
||||
|
||||
| Role | Responsibilities |
|
||||
| ------------------ | --------------------------------------------------------------------------------------- |
|
||||
| **Human / PM** | Defines clear task + acceptance criteria, reviews results, enforces cross-model review. |
|
||||
| **Worker Agent** | Picks up a task, updates status/time, posts results, flags blockers. |
|
||||
| **Reviewer Agent** | Opposite-model reviewer for code or high-risk work (see Cross-Model SOP). |
|
||||
| Role | Responsibilities |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------- |
|
||||
| **Human / PM** | Defines clear task and acceptance criteria, reviews results, and sets any required review policy. |
|
||||
| **Worker Agent** | Picks up a task, updates status/time, posts results, and flags blockers. |
|
||||
| **Reviewer Agent** | Performs an independent review when the task or configured policy requires it. |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -25,7 +30,7 @@ This SOP assumes an agent or human is already doing the work. Veritas Kanban can
|
|||
| 2. Work | Agent executes subtasks; marks subtasks complete as it goes. | ✅ |
|
||||
| 3. Update | Post intermediate comment(s) or blockers; set status `blocked` if waiting on human. | As needed |
|
||||
| 4. Complete | Stop timer, set status `done`, provide completion summary + attachments, capture lessons learned. | ✅ |
|
||||
| 5. Review | Trigger cross-model review if code touched or risk level ≥ medium. | ✅ for code |
|
||||
| 5. Review | Run an independent or cross-model review when the task or governance policy requires it. | Conditional |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -193,7 +198,8 @@ URL: http://localhost:3000/task/<ID>
|
|||
- Stop timer + set status done (vk done <id> "summary").
|
||||
- Attach deliverables / link to repo.
|
||||
- Fill the lessons learned field if anything should go into AGENTS/CLAUDE.
|
||||
5. If you touched code, queue cross-model review task before marking done.
|
||||
5. If the task or configured governance policy requires independent review,
|
||||
queue the review before marking done.
|
||||
```
|
||||
|
||||
Store this under `prompt-registry/agent-task-workflow.md` so every agent run is consistent.
|
||||
|
|
@ -240,11 +246,11 @@ System events render as divider lines in the UI — visually distinct from regul
|
|||
|
||||
## Escalation
|
||||
|
||||
| Situation | Action |
|
||||
| ----------------------- | ------------------------------------------------------------------------------- |
|
||||
| Blocked > 15 minutes | Set status `blocked`, leave blocker comment, ping PM. |
|
||||
| Time tracking forgotten | Start timer immediately, add manual entry for elapsed time with reason. |
|
||||
| Reviewer disagrees | Re-open task, create subtasks for fixes, keep cross-model reviewer in the loop. |
|
||||
| Situation | Action |
|
||||
| ----------------------- | --------------------------------------------------------------------------------- |
|
||||
| Blocked > 15 minutes | Set status `blocked`, leave blocker comment, ping PM. |
|
||||
| Time tracking forgotten | Start timer immediately, add manual entry for elapsed time with reason. |
|
||||
| Reviewer disagrees | Re-open task, create subtasks for fixes, and keep the assigned reviewer informed. |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Use this playbook when Veritas Kanban delegates work to OpenAI Codex. v4.3 inclu
|
|||
| **Human / PM** | Defines task scope, confirms Codex mode, reviews outputs, approves final merge. |
|
||||
| **Veritas Orchestrator** | Creates worktree, selects provider, starts attempt, tracks status/logs/telemetry. |
|
||||
| **Codex Worker** | Implements, tests, reports final summary, and leaves useful run evidence. |
|
||||
| **Reviewer Agent** | Performs cross-model review when Codex authored code or reviewed another agent. |
|
||||
| **Reviewer Agent** | Performs an independent review when the task or governance policy requires one. |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -31,16 +31,16 @@ Default for v4.3 is **Codex CLI**. Use **Codex SDK** when a task needs a durable
|
|||
|
||||
## Lifecycle Overview
|
||||
|
||||
| Stage | Action | Required? |
|
||||
| ------------ | ---------------------------------------------------------------------- | --------- |
|
||||
| 0. Configure | Add Codex agent profile and verify `codex` install/auth. | Yes |
|
||||
| 1. Prepare | Create or verify task worktree; render task prompt. | Yes |
|
||||
| 2. Start | Veritas starts provider attempt and marks task `in-progress`. | Yes |
|
||||
| 3. Run | Codex executes with scoped prompt and emits progress/log events. | Yes |
|
||||
| 4. Observe | Veritas maps JSONL/SDK events into attempt logs, activity, telemetry. | Yes |
|
||||
| 5. Complete | Veritas records final summary, deliverables, usage, and task outcome. | Yes |
|
||||
| 6. Review | Opposite-model review runs for code or high-risk changes. | For code |
|
||||
| 7. Close | Human or automation approves, merges, archives, or creates follow-ups. | Yes |
|
||||
| Stage | Action | Required? |
|
||||
| ------------ | ----------------------------------------------------------------------- | ----------- |
|
||||
| 0. Configure | Add Codex agent profile and verify `codex` install/auth. | Yes |
|
||||
| 1. Prepare | Create or verify task worktree; render task prompt. | Yes |
|
||||
| 2. Start | Veritas starts provider attempt and marks task `in-progress`. | Yes |
|
||||
| 3. Run | Codex executes with scoped prompt and emits progress/log events. | Yes |
|
||||
| 4. Observe | Veritas maps JSONL/SDK events into attempt logs, activity, telemetry. | Yes |
|
||||
| 5. Complete | Veritas records final summary, deliverables, usage, and task outcome. | Yes |
|
||||
| 6. Review | Independent review runs when required by the task or governance policy. | Conditional |
|
||||
| 7. Close | Human or automation approves, merges, archives, or creates follow-ups. | Yes |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -95,7 +95,8 @@ redacted governance trace.
|
|||
- `error`
|
||||
6. Append human-readable attempt logs.
|
||||
7. Preserve final response as the completion summary.
|
||||
8. Emit telemetry and token usage when available.
|
||||
8. Let Veritas project lifecycle telemetry and provider-reported token usage;
|
||||
do not emit duplicate events from the managed Codex run.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -214,25 +215,23 @@ codex mcp add openaiDeveloperDocs --url https://developers.openai.com/mcp
|
|||
|
||||
---
|
||||
|
||||
## AGENTS.md Codex Snippet
|
||||
## AGENTS.md managed-run snippet
|
||||
|
||||
Add this to a repository where Codex will work with Veritas:
|
||||
Use the harness-neutral managed-run block from
|
||||
[AGENTS-TEMPLATE.md](AGENTS-TEMPLATE.md). Codex does not need a separate VK
|
||||
lifecycle protocol:
|
||||
|
||||
```md
|
||||
## Veritas Kanban Protocol
|
||||
## Veritas Kanban managed-run protocol
|
||||
|
||||
When working on Veritas Kanban tasks:
|
||||
|
||||
1. Treat Veritas Kanban as the source of truth for task state.
|
||||
2. Before implementation, inspect the task, acceptance criteria, worktree, and related docs.
|
||||
3. Move the task to `in-progress` and ensure an attempt is tracked.
|
||||
4. Keep notes in task comments or progress files when findings affect future work.
|
||||
5. Run relevant tests/checks before completion.
|
||||
6. Report final summary, files changed, tests run, risks, and follow-ups.
|
||||
7. For code changes, request cross-model review before final completion.
|
||||
8. Use the Veritas MCP server when available instead of ad hoc HTTP calls.
|
||||
|
||||
For OpenAI product/API questions, use the OpenAI developer documentation MCP server first.
|
||||
1. Treat the supplied task envelope as authoritative.
|
||||
2. Work only in the assigned worktree and obey its commit policy.
|
||||
3. Use only the run-scoped tools and credentials supplied by Veritas.
|
||||
4. Do not register, heartbeat, start, complete, or emit telemetry manually.
|
||||
5. Do not call `vk begin` or `vk done`; Veritas already owns the attempt.
|
||||
6. Run focused verification that matches the requested change.
|
||||
7. Return the outcome, changed files or artifacts, checks, risks, and blockers
|
||||
through the normal Codex final response.
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -255,16 +254,14 @@ If `autoTelemetry` is enabled, avoid double-emitting lifecycle events. Token usa
|
|||
|
||||
---
|
||||
|
||||
## Review Rules
|
||||
## Optional review rules
|
||||
|
||||
| Author | Reviewer Recommendation |
|
||||
| ------------ | -------------------------------------------------- |
|
||||
| Codex | Claude, Gemini, or another non-Codex reviewer |
|
||||
| Claude | Codex review or GPT-family reviewer |
|
||||
| Human | Codex review for complex code or high-risk changes |
|
||||
| Codex review | Human adjudicates blocking findings |
|
||||
Independent review is not a default completion gate. Enable it only when the
|
||||
task, configured review gate, issue owner, or release owner requires it.
|
||||
|
||||
Follow [SOP-cross-model-code-review.md](SOP-cross-model-code-review.md) for scoring, findings, and final gate handling.
|
||||
When required, follow
|
||||
[SOP-cross-model-code-review.md](SOP-cross-model-code-review.md) for scoring,
|
||||
findings, and final gate handling.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
# SOP: Cross-Model Code Review (Claude ↔ GPT)
|
||||
# Optional Independent Code Review Playbook
|
||||
|
||||
**Rule (non-negotiable):** If Claude wrote it, GPT reviews it. If GPT wrote it, Claude reviews it. The author may self-check during development, but the final gate must be a different model.
|
||||
This legacy-path document describes an optional independent-review workflow.
|
||||
It is not part of the default delivery SOP or release gate. Use it only when
|
||||
the task, configured review gate, issue owner, or release owner explicitly
|
||||
requests it.
|
||||
|
||||
---
|
||||
|
||||
## When to Trigger
|
||||
|
||||
| Work Type | Review Required? |
|
||||
| -------------------------------- | ----------------------------------- |
|
||||
| Application code, infra, scripts | ✅ Always |
|
||||
| Docs/content | ⚠️ Only if accuracy/safety critical |
|
||||
| Research summaries | Optional (human discretion) |
|
||||
| Work Type | Review Required? |
|
||||
| -------------------------------- | ------------------------ |
|
||||
| Application code, infra, scripts | When explicitly required |
|
||||
| Docs/content | When explicitly required |
|
||||
| Research summaries | When explicitly required |
|
||||
|
||||
If in doubt, review.
|
||||
If no review requirement is present, use focused self-verification and the
|
||||
normal human/CI review path.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -24,7 +28,7 @@ If in doubt, review.
|
|||
- Type: `code`
|
||||
- Sprint/project identical
|
||||
- Description includes acceptance criteria + diff link(s)
|
||||
3. **Assign to opposite model** (via OpenClaw or other orchestrator):
|
||||
3. **Assign an independent reviewer** (human or configured agent):
|
||||
```
|
||||
Hey Codex, review PR for task_1234. Checklist below.
|
||||
```
|
||||
|
|
@ -44,7 +48,8 @@ If in doubt, review.
|
|||
## Verdict
|
||||
Changes required.
|
||||
```
|
||||
7. **Audit trail**: Update commit message or PR description with `[author: claude-sonnet-4-5][reviewed-by: gpt-5.1-codex]`.
|
||||
7. **Audit trail**: Record the reviewer and outcome in the task or pull request
|
||||
when attribution is required by the configured policy.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -65,7 +70,7 @@ Adapt per task type.
|
|||
## Prompt Template (Reviewer)
|
||||
|
||||
```
|
||||
You are the cross-model reviewer. The code was authored by <model>. Apply the checklist:
|
||||
You are the independent reviewer. Apply the checklist:
|
||||
1. Pull latest branch <branch>.
|
||||
2. Run tests (if any).
|
||||
3. For each issue, note severity (High/Medium/Low/Nit) + file/line + fix suggestion.
|
||||
|
|
@ -97,15 +102,16 @@ Store in `prompt-registry/cross-model-review.md`.
|
|||
|
||||
## Review Gates (Veritas Kanban Enforcement)
|
||||
|
||||
VK's built-in enforcement gates integrate directly with the cross-model review workflow, turning this SOP from a process suggestion into a structural guarantee:
|
||||
VK's built-in enforcement gates can make this optional playbook a structural
|
||||
requirement for selected workspaces or tasks:
|
||||
|
||||
1. **reviewGate** — Blocks task completion unless all four reviewScores (security, reliability, performance, accessibility) are 10. This is the automated enforcement layer that ensures the cross-model review checklist has been completed rigorously.
|
||||
1. **reviewGate** — Blocks task completion unless all four reviewScores (security, reliability, performance, accessibility) are 10. This is the automated enforcement layer that ensures the configured review checklist has been completed rigorously.
|
||||
|
||||
2. **closingComments** — Requires a substantive review comment (≥20 characters) before task completion. Ensures the reviewer leaves documented findings, not just scores.
|
||||
|
||||
3. **How they work together**:
|
||||
- Author (Model A) completes code; task remains `in-progress`
|
||||
- Reviewer (Model B) runs the cross-model review checklist
|
||||
- Independent reviewer runs the configured review checklist
|
||||
- Reviewer scores all 4 dimensions via the API: `PATCH /api/tasks/{id}` with `reviewScores`
|
||||
- Reviewer leaves findings as comments (must be ≥20 chars if closingComments enabled)
|
||||
- If reviewGate is enabled, task **cannot** move to `done` until all scores are 10
|
||||
|
|
@ -121,10 +127,13 @@ VK's built-in enforcement gates integrate directly with the cross-model review w
|
|||
|
||||
5. **Handling gate failures** — If task completion returns a 400 error with `REVIEW_GATE_FAILED` or `CLOSING_COMMENT_REQUIRED`, the reviewer must address the deficiency (raise a score, add a comment) and retry.
|
||||
|
||||
6. **Recommendation**: Enable both `reviewGate` and `closingComments` for production workflows. This transforms the cross-model review from a process suggestion into a structural guarantee—no task can slip through without evidence of a thorough review.
|
||||
6. **Recommendation**: Enable `reviewGate` and `closingComments` only when the
|
||||
workspace deliberately requires independent scored review. Leave them off
|
||||
when normal task verification and human/CI review are sufficient.
|
||||
|
||||
7. Full documentation: See [Enforcement Gates](enforcement.md) for all available gates, configuration options, and API reference.
|
||||
|
||||
---
|
||||
|
||||
This SOP preserved a 91% accuracy rate in RF-002. Keep following it.
|
||||
RF-002 recorded a 91% accuracy rate for this review method. That result supports
|
||||
using the method when selected; it does not make it a universal gate.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ Every project should maintain these files:
|
|||
| `prompt-registry/*.md` | Workflow prompts | When prompts drift or improve |
|
||||
| `README.md` | Project overview, quick start | After major releases |
|
||||
|
||||
Register maintained living documents in Settings → Doc Freshness. The registry
|
||||
record, not an optional Markdown comment, is authoritative for the last review,
|
||||
reviewer, maximum age, score, and alerts. Historical evidence and release notes
|
||||
do not need synthetic freshness headers.
|
||||
|
||||
### Optional Model-Specific Files
|
||||
|
||||
- `GPT.md` — GPT-specific notes (if behavior differs from Claude)
|
||||
|
|
@ -45,7 +50,9 @@ Update docs **within the same session** when:
|
|||
|
||||
1. **A bug was caused by missing context** — Add durable shared context to
|
||||
`AGENTS.md`, or a harness-specific supplement when it truly differs
|
||||
2. **Cross-model review catches a pattern** — Document the pattern
|
||||
2. **Focused review catches a pattern** — Document the pattern regardless of
|
||||
whether the reviewer is a maintainer, an independent agent, or a configured
|
||||
governance gate
|
||||
3. **A workaround is discovered** — Add it to Troubleshooting or the nearest
|
||||
applicable instruction file
|
||||
4. **API behavior changes** — Update relevant docs
|
||||
|
|
@ -86,7 +93,8 @@ Run this monthly or after major releases:
|
|||
|
||||
- [ ] Prompts reference current API endpoints
|
||||
- [ ] No prompts for removed features
|
||||
- [ ] Cross-model review prompt matches current checklist
|
||||
- [ ] Optional review prompts match the current checklist and are not described
|
||||
as default delivery gates
|
||||
|
||||
### README.md
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ When you ask “Hey Veritas, can you be the PM for this sprint and assign sub-ag
|
|||
- Spawn worker agent with clear instructions + acceptance criteria.
|
||||
4. **Track**:
|
||||
- Update Agent Status panel using `vk agent sub-agent <count>`.
|
||||
- Make sure each worker uses `vk begin/done` so timers stay accurate.
|
||||
- Managed workers rely on VK-owned attempt timing. Only human or external
|
||||
self-reporting workers use `vk begin`/`vk done`.
|
||||
5. **Review**:
|
||||
- Run cross-model review before marking tasks done.
|
||||
- Run independent or cross-model review only when required by the task or
|
||||
configured governance policy.
|
||||
- Request fixes via subtasks or comments.
|
||||
6. **Report**:
|
||||
- Post updates in task comments and daily standups (`vk summary standup --text`).
|
||||
|
|
@ -40,11 +42,11 @@ Task: <ID> — <Title>
|
|||
Context: <link to research/requirements>
|
||||
Deliverable: <clear definition of done>
|
||||
Steps:
|
||||
1. Run vk begin <id>.
|
||||
1. Treat the Veritas task envelope as authoritative.
|
||||
2. Complete subtasks in order. Leave notes if deviations occur.
|
||||
3. If blocked, set status blocked + explain.
|
||||
4. On completion, vk done <id> "summary".
|
||||
5. Request cross-model review by creating task <new id> tagged review.
|
||||
3. If blocked, report the blocker through an available VK tool or final output.
|
||||
4. Return a concise completion summary through the native harness result.
|
||||
5. Request an independent review only when the task or policy requires it.
|
||||
Uploads: <where to store artifacts>
|
||||
```
|
||||
|
||||
|
|
@ -116,8 +118,8 @@ See [SQUAD-CHAT-PROTOCOL.md](SQUAD-CHAT-PROTOCOL.md) for full details.
|
|||
1. **Human**: `sessions_spawn` Opus with task “Be PM for US-1600”.
|
||||
2. **Opus (PM)**: Reads sprint tasks, assigns `US-1601` to itself (docs) and `US-1602` to Codex.
|
||||
3. **Opus**: Runs `vk agent sub-agent 1` to show a worker is active.
|
||||
4. **Opus**: Spawns Codex worker with handoff template; instructs to run `vk begin task_...` etc.
|
||||
5. **Codex**: Executes, posts completion summary, requests cross-model review from Claude.
|
||||
4. **Opus**: Spawns the Codex worker with the managed-run handoff template.
|
||||
5. **Codex**: Executes and returns its completion summary through the managed run.
|
||||
6. **Opus**: Reviews, marks done, updates sprint recap comment.
|
||||
7. **Opus**: Sets agent status back to idle once all workers complete (`vk agent idle`).
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,26 @@ The Scoring Framework lets you define profiles with weighted criteria that evalu
|
|||
|
||||
**Scorer types:**
|
||||
|
||||
| Type | What it checks |
|
||||
| ------------------- | ------------------------------------------------------------ |
|
||||
| `RegexMatch` | Whether the output matches a regular expression |
|
||||
| `KeywordContains` | Whether the output contains required keywords |
|
||||
| `NumericRange` | Whether a numeric field in the output falls within a range |
|
||||
| `CustomExpression` | A custom evaluation expression |
|
||||
| Type | What it checks |
|
||||
| ------------------ | --------------------------------------------------------------------- |
|
||||
| `RegexMatch` | Whether bounded worker-isolated regex evaluation matches |
|
||||
| `KeywordContains` | Whether the output contains required keywords |
|
||||
| `NumericRange` | Whether a numeric field in the output falls within a range |
|
||||
| `OccurrenceRatio` | Literal occurrence density with optional numeric-path normalization |
|
||||
|
||||
`RegexMatch` accepts patterns up to 256 characters and any valid JavaScript regex flag set supported
|
||||
by the active Node runtime. Evaluation uses a globally bounded four-worker pool outside the server
|
||||
event loop, a bounded wait queue, and a 100 ms limit. Output is limited to 100,000 characters,
|
||||
action text to 10,000 characters, and their combined scoring target to 110,001 characters.
|
||||
|
||||
`OccurrenceRatio` is the declarative replacement for legacy custom expressions. It accepts one to
|
||||
32 literal `needles` and divides their occurrence count by either a fixed `denominator` or a numeric
|
||||
`denominatorPath`, optionally scaled with `denominatorScale`. `wholeWord`, `caseSensitive`,
|
||||
`minimumDenominator`, and `invert` provide bounded transformations without executing code.
|
||||
|
||||
Persisted profiles containing the removed `CustomExpression` scorer fail closed during evaluation.
|
||||
Replace those scorers through the profile API before retrying; the server never evaluates or
|
||||
silently converts the stored expression.
|
||||
|
||||
**Composite methods:**
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ Example frontmatter:
|
|||
id: cross-model-review
|
||||
name: Cross Model Review
|
||||
category: evaluation
|
||||
description: Opposite-model review checklist
|
||||
description: Optional independent review checklist
|
||||
---
|
||||
|
||||
# Cross Model Review
|
||||
|
|
|
|||
|
|
@ -134,13 +134,17 @@ Publish shared resources as a package:
|
|||
|
||||
## What to Share
|
||||
|
||||
### Always Share
|
||||
### Common Shared Resources
|
||||
|
||||
- Cross-model review prompt (consistency is critical)
|
||||
- Security review checklist
|
||||
- Definition of Done template
|
||||
- Sprint planning prompt
|
||||
|
||||
### Share Only When Configured
|
||||
|
||||
- Optional independent-review prompt when the consuming workspace explicitly
|
||||
enables that workflow
|
||||
|
||||
### Share Carefully
|
||||
|
||||
- Agent personality (AGENTS.md) — may need project-specific tweaks
|
||||
|
|
@ -236,7 +240,7 @@ Fixture contracts live in
|
|||
```markdown
|
||||
## Instructions
|
||||
|
||||
Follow the standard code review process.
|
||||
Follow the configured independent review process.
|
||||
See prompt: `prompt-registry/cross-model-review.md`
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ Example: 6 tasks × 4 subtasks × 0.5d = 12 agent-days. With 3 agents @ 4 days f
|
|||
| US-1602: Task Workflow SOP | docs | high | Defines lifecycle. |
|
||||
| US-1603: Sprint Planning SOP | docs | medium | This document. |
|
||||
| US-1604: Multi-Agent Orchestration | docs | medium | PM + workers. |
|
||||
| US-1605: Cross-Model Review | docs | medium | Opposite model gate. |
|
||||
| US-1605: Review Policy | docs | medium | Optional review criteria. |
|
||||
| US-1606: Best Practices | docs | medium | Patterns + anti-patterns. |
|
||||
|
||||
Clone this pattern for your own projects; rename sprint `US-YYYY` and fill tasks accordingly.
|
||||
|
|
|
|||
|
|
@ -174,7 +174,9 @@ Thresholds (hardcoded in v4.0):
|
|||
|
||||
**Status shows `elevated` with all agents appearing online:** Check the operations signal — `status: critical` also triggers `elevated`. The agent registry shows registered agents, not process health.
|
||||
|
||||
**`system.disk: false` immediately after startup:** The data directory path may be wrong. Check the `DATA_DIR` environment variable — it should point to the `.veritas-kanban` data directory.
|
||||
**`system.disk: false` immediately after startup:** The storage root may be wrong. Check
|
||||
`DATA_DIR` (or `VERITAS_DATA_DIR` when `DATA_DIR` is unset); runtime health checks use its
|
||||
`.veritas-kanban` child directory.
|
||||
|
||||
**Health endpoint returns 500:** The metrics service or agent registry service failed to initialize. Check the server startup logs.
|
||||
|
||||
|
|
|
|||
|
|
@ -1065,16 +1065,20 @@ SQLite tables with JSON payload columns plus query indexes. This keeps the v4
|
|||
service contracts intact while preventing SQLite mode from writing operational
|
||||
state back to `.veritas-kanban/*.json` or telemetry NDJSON files.
|
||||
|
||||
| Runtime table | Stored data |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `activity_events` | Complete activity entries plus type, task, agent, and created-time columns. |
|
||||
| `status_history` | Complete status transition entries plus previous/new status and task columns. |
|
||||
| `telemetry_events` | Complete telemetry events plus type, task, project, token, duration, and result columns. |
|
||||
| `run_events` | Complete `run-event/v1` envelopes plus ordered attempt cursor, provider identity, dedupe, and receive columns. |
|
||||
| `run_supervisors` | Complete `run-supervisor/v1` snapshots plus task/attempt, state, revision, lease owner/expiry, and recovery indexes. |
|
||||
| Runtime table | Stored data |
|
||||
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `activity_events` | Complete activity entries plus type, task, agent, and created-time columns. |
|
||||
| `status_history` | Complete status transition entries plus previous/new status and task columns. |
|
||||
| `telemetry_events` | Complete telemetry events plus type, task, project, token, duration, and result columns. |
|
||||
| `run_events` | Complete `run-event/v1` envelopes plus ordered attempt cursor, provider identity, dedupe, and receive columns. |
|
||||
| `run_supervisors` | Complete `run-supervisor/v1` snapshots plus task/attempt, state, revision, lease owner/expiry, and recovery indexes. |
|
||||
| `durable_goals` | Complete `durable-goal/v1` objective state, root task/workflow identity, compare-and-set revision, blockers, continuation chain, usage, and completion-evidence requirements. |
|
||||
| `reflection_extraction_jobs` | Bounded `reflection-extraction-job/v1` source identities, state, revision, idempotency key, retry availability, lease owner/expiry, candidate IDs, and failure history. |
|
||||
| `admission_reservations` | Versioned capacity and execution-tree budget reservations plus task/workspace/root/provider/host scopes, root objective/node/parent indexes, lease state, revision, and idempotency evidence. |
|
||||
|
||||
`ActivityService`, `StatusHistoryService`, `TelemetryService`,
|
||||
`RunEventJournalService`, and `RunSupervisorService` select these SQLite repositories when
|
||||
`RunEventJournalService`, `RunSupervisorService`, `DurableGoalService`, and
|
||||
`ReflectionExtractionJobService`, and `AdmissionControlService` select these SQLite repositories when
|
||||
`VERITAS_STORAGE=sqlite`. File storage still forces the file-backed services to
|
||||
`storageType='file'`, so explicit file mode cannot be accidentally flipped by
|
||||
the environment.
|
||||
|
|
|
|||
|
|
@ -141,22 +141,23 @@ pnpm install
|
|||
pnpm build
|
||||
```
|
||||
|
||||
If errors persist, check your Node.js version — **Node 22+** is required:
|
||||
If errors persist, check your Node.js version. **Node 22.22.1+** is required:
|
||||
|
||||
```bash
|
||||
node -v # Should be v22.x or higher
|
||||
node -v # Must be v22.22.1 or higher
|
||||
```
|
||||
|
||||
### `pnpm` not found
|
||||
|
||||
Veritas Kanban uses pnpm workspaces. Install it first:
|
||||
Veritas Kanban uses pnpm workspaces. Activate the repository-pinned version:
|
||||
|
||||
```bash
|
||||
npm install -g pnpm
|
||||
# or
|
||||
corepack enable && corepack prepare pnpm@latest --activate
|
||||
corepack enable
|
||||
corepack prepare pnpm@11.1.1 --activate
|
||||
```
|
||||
|
||||
Do not install this workspace with npm, Yarn, or Bun.
|
||||
|
||||
### Port already in use
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# Veritas Kanban v6 Compatibility And Release Policy
|
||||
|
||||
This policy defines supported v6.0.0 combinations, harness evidence, release
|
||||
This policy defines supported v6.1.2 combinations, harness evidence, release
|
||||
channels, and rollback limits. The machine-readable harness record at
|
||||
`GET /api/config/harness-compatibility` is authoritative for exact capability
|
||||
digests, fixture revisions, and the current host's live state.
|
||||
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
||||
Documentation freshness: 2026-08-24 for Veritas Kanban 6.1.2.
|
||||
|
||||
## Harness Support Tiers
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ are incompatible with v6.
|
|||
|
||||
| Component | Supported v6 combination | Detection/evidence | Fail-closed boundary |
|
||||
| -------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Server, web, shared, CLI, MCP, desktop | All release packages are exactly 6.0.0. | Package manifests, `/api/health.version`, `vk --version`, MCP metadata, desktop bundle/update metadata. | Mixed release packages are unsupported for publication. |
|
||||
| Server, web, shared, CLI, MCP, desktop | All release packages are exactly 6.1.2. | Package manifests, `/api/health.version`, `vk --version`, MCP metadata, desktop bundle/update metadata. | Mixed release packages are unsupported for publication. |
|
||||
| Public API | REST API remains `v1` at `/api/v1`, with `/api` compatibility aliases where documented. | `X-API-Version`, OpenAPI/reference docs, CLI/MCP smoke. | Unknown API versions or incompatible auth fail before mutation. |
|
||||
| Buzz Agent | Buzz v0.4.24 commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`; `buzz-agent 0.1.0`; ACP v1. | Exact initialize identity, capability digest, probe revision, composed Buzz fixtures. | Unknown build, `buzz-acp`, resume, HTTP/SSE MCP, or capability drift blocks. |
|
||||
| Buzz relay integration | Buzz v0.4.24; NIP-11, NIP-29, NIP-42; optional NIP-43 membership. | Pinned relay compatibility evidence, signed query/event fixtures, mapping state. | Host/TLS drift, unsafe URL, bad signature, identity mismatch, replay, or disabled mapping blocks. |
|
||||
|
|
@ -37,7 +37,7 @@ are incompatible with v6.
|
|||
| GitHub Copilot CLI | v1.0.74 public-preview ACP; tag commit `2b809c84e87dbcc88f897cb4f3fb97c43b77af95`. | Version and ACP initialize handshake; authentication remains provider-managed. | Version drift, broad allow, remote/plugin/config injection, or unsupported controls blocks. |
|
||||
| Hermes Agent | v2026.7.7.2 one-shot process adapter. | `hermes --version` and allowlisted boot authentication. | Resume/follow-up remains unsupported. |
|
||||
| OpenClaw | v2026.6.11 gateway adapter. | Gateway health, runtime manifest, explicit operator tool policy. | Missing `sessions_spawn`/`sessions_send`, unknown evidence, or unsupported task controls blocks. |
|
||||
| macOS desktop | macOS arm64 signed/notarized app with bundled 6.0.0 server/web. | Bundle version, signature, Gatekeeper, stapling, `/api/health.version`, update metadata. | Mixed bundle/runtime, failed readiness, signature, or metadata checks blocks stable publication. |
|
||||
| macOS desktop | macOS arm64 signed/notarized app with bundled 6.1.2 server/web. | Bundle version, signature, Gatekeeper, stapling, `/api/health.version`, update metadata. | Mixed bundle/runtime, failed readiness, signature, or metadata checks blocks stable publication. |
|
||||
| Linux/Windows desktop | Unsigned preview artifacts only. | Cross-platform packaging workflows. | Not a supported stable install or update channel. |
|
||||
| Desktop SQLite/profile | Existing v5.2.5 workspace upgraded in place after a complete backup. | Data/profile counts, integrity check, startup normalization, board/runtime smoke. | Competing writers, unsafe filesystem, failed migration, or missing recovery evidence blocks acceptance. |
|
||||
|
||||
|
|
@ -73,8 +73,11 @@ Provider-specific opt-in smoke commands are documented in
|
|||
- Required filesystem, process, environment, network, MCP, tool, approval,
|
||||
budget, and lifecycle controls must be supported by current runtime evidence.
|
||||
Advisory controls may proceed with an attributed warning.
|
||||
- The fine-grained egress gateway in issue 855 is deferred. v6.0.0 does not
|
||||
claim method/path/domain proxy enforcement that it does not have.
|
||||
- Run-scoped network policy resolves and pins allowed destinations, routes
|
||||
governed traffic through the egress gateway, enforces protocol, host, port,
|
||||
HTTP method, and normalized path rules, and records redacted decision
|
||||
evidence. Direct or unverifiable network paths fail closed when enforcement
|
||||
is required.
|
||||
|
||||
## Release Channels
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,191 @@
|
|||
# Veritas Kanban v6 GA Checklist
|
||||
|
||||
This checklist is the stable-release gate for Veritas Kanban 6.0.0. Command
|
||||
results, platform details, workflow links, limitations, and artifact hashes
|
||||
belong in [v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md).
|
||||
This checklist contains the active stable-release gate for Veritas Kanban
|
||||
6.1.2 and retains the completed 6.1.1, 6.1.0, and 6.0.2 evidence below. Command results, platform
|
||||
details, workflow links, limitations, and artifact hashes belong in
|
||||
[v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md).
|
||||
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
||||
Documentation freshness: 2026-08-24 for Veritas Kanban 6.1.2.
|
||||
|
||||
## Source And Scope
|
||||
## 6.1.2 Release Gate
|
||||
|
||||
- [x] Audit issues #1162-#1173 are closed through merged, evidence-linked pull
|
||||
requests and the single final regression milestone.
|
||||
- [x] Root, shared, server, web, CLI, MCP, and desktop manifests are 6.1.2.
|
||||
- [x] README, canonical instructions, API reference, compatibility policy,
|
||||
upgrade guide, release notes, canonical GitHub body, freshness record, and
|
||||
changelog are synchronized for 6.1.2.
|
||||
- [x] Runtime paths, storage repositories, provider adapters and lifecycle,
|
||||
credential-aware frontend requests, immutable actions, continuous
|
||||
scanning, critical coverage, dependency cleanup, lint ratchets, and the
|
||||
production Docker contract are represented in release documentation.
|
||||
- [x] Independent and cross-model review remain optional; they are not part of
|
||||
the default delivery or release gate.
|
||||
- [x] The coordinated security fix is integrated, released in supported
|
||||
artifacts, and published through the approved repository advisory.
|
||||
- [x] One clean final candidate passes the complete Node-floor and current-Node
|
||||
verification matrix with exact counts, skips, retries, image size, and
|
||||
limitations recorded in the evidence packet.
|
||||
- [x] The release PR merges and its exact merge is published as annotated
|
||||
`v6.1.2` with a live body matching `docs/releases/v6.1.2.md`.
|
||||
- [x] Signed/notarized macOS assets, updater metadata, installed-app readiness,
|
||||
the live Homebrew cask, and the advisory disposition are verified.
|
||||
- [x] Every publication readback required before closing release tracker #1174
|
||||
has passed; close the tracker after this evidence update merges.
|
||||
|
||||
## Historical 6.1.1 Completed Release Gate
|
||||
|
||||
- [x] Issue #1153 and pull requests #1148, #1149, #1150, #1154, and #1155
|
||||
received an evidence-backed maintainer disposition.
|
||||
- [x] Long Task Detail content is constrained and scrollable, with Chromium
|
||||
layout, overflow, and wheel-input regression coverage (#1153, #1154).
|
||||
- [x] Dependency updates were audited for runtime compatibility, peer ranges,
|
||||
advisories, lockfile integrity, tests, builds, and desktop packaging;
|
||||
jsdom 30 was rejected rather than weakening the Node.js floor (#1148,
|
||||
#1149, #1150, #1155).
|
||||
- [x] Root, shared, server, web, CLI, MCP, and desktop manifests are 6.1.1.
|
||||
- [x] README, API reference, compatibility policy, upgrade guide, release
|
||||
notes, canonical GitHub release body, and changelog agree on 6.1.1.
|
||||
- [x] Frozen install, production and full audits, lint and warning budget,
|
||||
typecheck, build, workspace tests, Playwright, Mantine QA, CLI/MCP smoke,
|
||||
desktop checks, and release validators pass on the consolidated candidate.
|
||||
- [x] Independent review is owner-directed and is not part of the active
|
||||
6.1.1 release gate; exact local and CI evidence carries the release
|
||||
decision.
|
||||
- [x] The release PR merges and the exact merge is published as annotated
|
||||
`v6.1.1` with a live body matching `docs/releases/v6.1.1.md`.
|
||||
- [x] Signed/notarized macOS assets, updater metadata, independent installed-app
|
||||
readiness, and the Homebrew cask are published and verified.
|
||||
|
||||
## Historical 6.1.0 Completed Release Gate
|
||||
|
||||
- [x] Roadmap issues #855, #864, #865, #866, #867, #868, #871, #872, #873,
|
||||
#876, and #879 are closed through merged pull requests.
|
||||
- [x] Root, shared, server, web, CLI, MCP, and desktop manifests are 6.1.0.
|
||||
- [x] README, canonical agent instructions, API/MCP references, compatibility
|
||||
policy, upgrade guide, release notes, and changelog agree on 6.1.0.
|
||||
- [x] The reviewed GitHub release body exists at `docs/releases/v6.1.0.md` and
|
||||
passes the full-width release-format gate.
|
||||
- [x] Focused verification passed for each roadmap slice before merge.
|
||||
- [x] The milestone workspace suite is green. `pnpm test:unit` completed
|
||||
successfully across every workspace on the consolidated candidate;
|
||||
server reported 3,234 passing and 5 skipped tests, web reported 469
|
||||
passing tests, and desktop reported 67 passing tests.
|
||||
- [x] Required release-PR CI, production audit, lint, typecheck, build,
|
||||
compatibility smoke, and packaging gates pass on the reviewed candidate.
|
||||
- [x] The release PR merges, annotated `v6.1.0` tag and GitHub release publish,
|
||||
and the desktop workflow uploads signed/notarized assets and updater
|
||||
metadata.
|
||||
- [x] Independent download, signature, Gatekeeper, stapling, readiness,
|
||||
updater, GitHub release-body, and Homebrew cask verification pass.
|
||||
|
||||
## Final Release Validation Commands
|
||||
|
||||
Apply `ci:full` to the release pull request and keep it applied through the
|
||||
final candidate synchronization. That single milestone runs the complete
|
||||
workspace suite, critical-path coverage, unsigned desktop artifacts, and
|
||||
Docker image contract. Run the following commands once from the clean 6.1.2
|
||||
release candidate at the supported Node floor and current supported Node:
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm check:pnpm-settings
|
||||
pnpm check:security-artifacts
|
||||
pnpm check:delivery-cadence
|
||||
pnpm test:ci-scope
|
||||
pnpm audit --prod --audit-level=high
|
||||
pnpm audit:all
|
||||
pnpm check:gitleaks
|
||||
pnpm lint
|
||||
pnpm lint:budget
|
||||
pnpm lint:report
|
||||
pnpm qa:mantine
|
||||
pnpm typecheck
|
||||
pnpm build
|
||||
pnpm test
|
||||
pnpm test:unit
|
||||
pnpm test:e2e
|
||||
pnpm smoke:cli-mcp
|
||||
pnpm desktop:test
|
||||
pnpm desktop:build
|
||||
pnpm desktop:check:electron-artifacts
|
||||
pnpm desktop:test:readiness
|
||||
pnpm desktop:dev:fresh
|
||||
pnpm desktop:smoke:mac:local
|
||||
pnpm desktop:package:mac:unsigned
|
||||
pnpm test:release-format
|
||||
pnpm validate:release -- --version 6.1.2 --skip-build-output
|
||||
pnpm validate:release -- --version 6.1.2 --docker-build
|
||||
```
|
||||
|
||||
Mount and inspect the unsigned DMG and ZIP, exercise the visible native
|
||||
single-instance/reopen/clean-close/quit lifecycle with an isolated profile, and
|
||||
run the production image as its non-root user against an isolated volume.
|
||||
Record health, auth, SQLite, static-web, canonical-path, backup, integrity,
|
||||
image-size, and clean-shutdown evidence. The same candidate must pass these
|
||||
gates at Node 22.22.1 and the current supported Node runtime.
|
||||
|
||||
## Distribution And Post-Publication
|
||||
|
||||
The 6.1.2 publication gate is complete. The final candidate, release merge,
|
||||
annotated tag, signed/notarized artifacts, independent launch verification,
|
||||
post-publication validator, live Homebrew cask, and approved advisory
|
||||
disposition are verified in the evidence packet. Completed 6.1.1 evidence
|
||||
remains recorded below.
|
||||
|
||||
## Historical 6.0.2 Source And Scope
|
||||
|
||||
- [x] The Buzz integration epic and every required child are closed through
|
||||
merged, focused pull requests.
|
||||
- [x] The equal-footing harness epic and every required child are closed
|
||||
through merged, focused pull requests.
|
||||
- [ ] The release tracker lists the exact main baseline, release branch, release
|
||||
- [x] The release tracker lists the exact main baseline, release branch, release
|
||||
PR, deferred v6.x work, and no unresolved release blocker.
|
||||
- [x] Root, shared, server, web, CLI, MCP, and desktop manifests are 6.0.0.
|
||||
- [x] Root, shared, server, web, CLI, MCP, and desktop manifests are 6.0.2.
|
||||
- [x] `AGENTS.md`, README badge, health, CLI, MCP, desktop bundle, artifact
|
||||
names, updater metadata, changelog, and current docs agree on 6.0.0.
|
||||
names, updater metadata, changelog, and current docs agree on 6.0.2.
|
||||
- [x] The public API remains intentionally `v1`, with additive v6 contracts and
|
||||
tested CLI/MCP compatibility.
|
||||
|
||||
## 6.0.1 Stabilization
|
||||
|
||||
- [x] Task drawers, shared overlays, Archive cards, scoring profiles, and
|
||||
template authoring have focused scroll, resize, compact-window, and
|
||||
keyboard coverage (#935, #938, #939, #941).
|
||||
- [x] Workflow loading, route/task/overlay history, and scoring-profile
|
||||
creation have focused recovery and state-transition coverage
|
||||
(#936, #937, #943).
|
||||
- [x] Operations Digest inventory, filters, exclusions, source IDs, window
|
||||
semantics, run de-duplication, and data quality reconcile in JSON,
|
||||
Markdown, scheduled snapshots, and UI tests (#944).
|
||||
- [x] Chat has visible, Escape, browser Back, persisted-state, compact-window,
|
||||
and native menu recovery coverage; the independently downloaded signed
|
||||
app passes the same recovery checks (#945).
|
||||
- [x] Desktop setup is version-neutral and the bridge consumes Electron's
|
||||
application version; the published bundle, health endpoint, updater, and
|
||||
bridge all report 6.0.1 (#986).
|
||||
|
||||
## 6.0.2 Desktop Recovery Hotfix
|
||||
|
||||
- [x] Board Chat and Squad Chat default to a bounded right-side Workbench dock
|
||||
and can switch between Right and Bottom without remounting the active
|
||||
conversation (#1004).
|
||||
- [x] Chat width and height clamp to the live viewport; scrolling, wheel input,
|
||||
Close, Escape, browser Back, Reset Layout, persisted-state recovery, and
|
||||
focus restoration have focused coverage (#1004).
|
||||
- [x] Native About, copied support information, the desktop bridge, and updater
|
||||
fallback consume one authoritative version/build/channel/OS/architecture
|
||||
record (#1005).
|
||||
- [x] Ordinary pull-request verification records affected workspaces without
|
||||
running tests; manual focused diagnostics and explicit `ci:full`,
|
||||
scheduled, or release milestones own the test suites (#1000, #1227).
|
||||
- [x] Published release notes are sourced from
|
||||
`docs/releases/vX.Y.Z.md`, use one full-width Markdown line per paragraph
|
||||
or list item, reject blockquotes and overlong prose blocks, and are
|
||||
compared with GitHub during post-publication validation. Run
|
||||
`pnpm test:release-format`, `pnpm validate:release`, and the
|
||||
post-publication `pnpm validate:release -- --github` check.
|
||||
|
||||
## Provider Certification
|
||||
|
||||
- [x] Buzz Agent v0.4.24 / `buzz-agent 0.1.0` passes the composed
|
||||
|
|
@ -107,7 +273,7 @@ Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
|||
- [x] Bundle sizes remain within the recorded QA budgets or have an explicit
|
||||
release-risk acceptance.
|
||||
|
||||
## Final Release Validation Commands
|
||||
## Historical 6.0.2 Final Release Validation Commands
|
||||
|
||||
Run from the clean release-candidate worktree:
|
||||
|
||||
|
|
@ -132,8 +298,8 @@ pnpm desktop:build
|
|||
pnpm desktop:check:electron-artifacts
|
||||
pnpm desktop:smoke:mac:local
|
||||
pnpm desktop:package:mac:unsigned
|
||||
pnpm validate:release -- --version 6.0.0
|
||||
pnpm validate:release -- --version 6.0.0 --docker-build
|
||||
pnpm validate:release -- --version 6.0.2
|
||||
pnpm validate:release -- --version 6.0.2 --docker-build
|
||||
```
|
||||
|
||||
Provider-specific deterministic suites are part of `pnpm test:unit`; record
|
||||
|
|
@ -141,27 +307,30 @@ their test counts and exact fixture baselines separately. Run credential-gated
|
|||
provider smoke only when the exact binary, authentication, subscription, and
|
||||
quota are available.
|
||||
|
||||
## Distribution And Post-Publication
|
||||
## Historical 6.0.2 Distribution And Post-Publication
|
||||
|
||||
- [ ] The ready release PR passes required CI and the `ci:full` workspace suite,
|
||||
receives focused standards/spec review, and merges to main.
|
||||
- [ ] Annotated tag `v6.0.0` peels to the exact release merge commit.
|
||||
- [ ] The GitHub release is published from reviewed v6 release notes.
|
||||
- [ ] Desktop Release completes with signed/notarized arm64 DMG and ZIP,
|
||||
- [x] The ready release PR passes required CI and the milestone-wide workspace
|
||||
suite, then merges to main.
|
||||
- [x] Annotated tag `v6.0.2` peels to the exact release merge commit.
|
||||
- [x] The GitHub release is published from reviewed
|
||||
`docs/releases/v6.0.2.md` without hard-wrapped prose.
|
||||
- [x] Desktop Release completes with signed/notarized arm64 DMG and ZIP,
|
||||
blockmaps, `latest-mac.yml`, and SHA-256 sidecars.
|
||||
- [ ] Independent downloads match GitHub digests, sidecars, updater metadata,
|
||||
- [x] Independent downloads match GitHub digests, sidecars, updater metadata,
|
||||
byte sizes, and SHA-256 values.
|
||||
- [ ] DMG and ZIP app signatures, hardened runtime, Gatekeeper, and notarization
|
||||
- [x] DMG and ZIP app signatures, hardened runtime, Gatekeeper, and notarization
|
||||
stapling pass.
|
||||
- [ ] The downloaded signed app launches with an isolated profile, reports
|
||||
6.0.0, verifies provider support, checks updates, executes a bounded task,
|
||||
and quits cleanly.
|
||||
- [ ] `pnpm validate:release -- --version 6.0.0 --github --repo BradGroux/veritas-kanban`
|
||||
- [x] The downloaded signed app launches with an isolated profile, reports
|
||||
6.0.2 through bundle, health, updater, native About, copied support
|
||||
information, and desktop bridge metadata; verifies Right and Bottom Chat
|
||||
recovery at the minimum supported window; executes a bounded task; and
|
||||
quits cleanly.
|
||||
- [x] `pnpm validate:release -- --version 6.0.2 --github --repo BradGroux/veritas-kanban`
|
||||
passes.
|
||||
- [ ] The Homebrew cask PR uses the published ZIP checksum, merges, and the
|
||||
- [x] The Homebrew cask PR uses the published ZIP checksum, merges, and the
|
||||
registered tap passes style, strict online audit, dry-run install, and
|
||||
livecheck.
|
||||
- [ ] The evidence packet contains release/workflow/asset/Homebrew links,
|
||||
- [x] The evidence packet contains release/workflow/asset/Homebrew links,
|
||||
exact hashes, runtime results, limitations, and deferred v6.x issues.
|
||||
- [ ] The release tracker closes only after every distribution surface above is
|
||||
- [x] The release tracker closes only after every distribution surface above is
|
||||
independently verified.
|
||||
|
|
|
|||
|
|
@ -1,27 +1,204 @@
|
|||
# Veritas Kanban v6 Release Candidate Evidence Packet
|
||||
|
||||
This packet is the retained evidence target for Veritas Kanban 6.0.0. It
|
||||
separates merged implementation, deterministic conformance, live provider
|
||||
evidence, local runtime proof, signed publication, and Homebrew availability.
|
||||
This packet records the active Veritas Kanban 6.1.2 audit release candidate and
|
||||
retains historical evidence for the completed 6.1.1 and 6.1.0 releases, the quarantined 6.0.0 prerelease, the 6.0.1
|
||||
stabilization release, and the 6.0.2 desktop recovery hotfix. It separates
|
||||
merged implementation, deterministic conformance, local runtime proof, signed
|
||||
publication, and Homebrew availability.
|
||||
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
||||
Veritas Kanban 6.1.2 is the supported stable v6 release. Do not use 6.0.0 for
|
||||
installation or upgrade validation.
|
||||
|
||||
## Release Scope
|
||||
Documentation freshness: 2026-08-24 for the published Veritas Kanban 6.1.2 release.
|
||||
|
||||
| Field | Value |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| Release version | 6.0.0 |
|
||||
| Release tracker | [Veritas Kanban 6.0.0 harness parity and Buzz integration](https://github.com/BradGroux/veritas-kanban/issues/924) |
|
||||
| Buzz epic | [First-class Buzz integration](https://github.com/BradGroux/veritas-kanban/issues/904) |
|
||||
| Harness epic | [Equal-footing agent harness support](https://github.com/BradGroux/veritas-kanban/issues/915) |
|
||||
| Implementation baseline | `398fe7f67e94c3d6cabee0702b2331d9c873fd0f` |
|
||||
| Release branch | `release/v6.0.0` |
|
||||
| Release PR | [#985](https://github.com/BradGroux/veritas-kanban/pull/985) |
|
||||
| Release merge | Pending source publication |
|
||||
| Tag and GitHub release | Pending source publication |
|
||||
| Desktop Release workflow | Pending tag publication |
|
||||
| Homebrew PR | Pending signed ZIP publication |
|
||||
| Evidence host | macOS 26.5.2 arm64; Node 26.5.0; pnpm 11.1.1; Git 2.55.0 |
|
||||
## 6.1.2 Audit Release Candidate
|
||||
|
||||
| Field | Value |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release version | 6.1.2 |
|
||||
| Source branch | `release/6.1.2-audit` |
|
||||
| Source baseline | `3851fea93ecfe5119e4092739662443d29059ac7`, `main` after release-gate fix PR #1243 |
|
||||
| Validated candidate | `2a21178df97a00395cfc6c43774a57496a5c1f6a`, the frozen release PR head before this evidence-only update |
|
||||
| Included issues | Audit tracker [#1174](https://github.com/BradGroux/veritas-kanban/issues/1174), closed findings #1162-#1173, and closed CodeQL baseline #1231 |
|
||||
| Public implementation | #1162-#1173 are closed through merged work. Coordinated remediation and release-gate fixes are merged through #1236, #1239, #1241, and #1243 |
|
||||
| Security disposition | Remediation is integrated into supported 6.1.2 artifacts. The approved [repository security advisory](https://github.com/BradGroux/veritas-kanban/security/advisories/GHSA-4r99-qpvh-wrqf) was published after artifact verification |
|
||||
| Publication state | Complete. Release PR #1237, annotated `v6.1.2`, the stable GitHub release, signed/notarized assets, updater metadata, post-publication validator, Homebrew cask/install, and advisory disposition are verified |
|
||||
|
||||
### 6.1.2 issue and pull request traceability
|
||||
|
||||
| Phase | Issue | Merged evidence |
|
||||
| -------------------- | --------------------------------------------------------------- | -------------------------- |
|
||||
| Verification | #1172 deterministic and milestone-scoped test gates | #1175, #1177, #1181, #1228 |
|
||||
| Verification | #1171 native-loader Vite/Vitest configuration | #1178 |
|
||||
| Supply chain | #1167 immutable actions | #1179 |
|
||||
| Security gates | #1168 continuous scanning | #1180 |
|
||||
| Coverage | #1169 critical-path baselines and ratchets | #1183 |
|
||||
| Runtime paths | #1162 canonical `DATA_DIR` behavior | #1184 |
|
||||
| Persistence | #1163 storage boundary restoration | #1190-#1220 |
|
||||
| Provider runtime | #1164 lifecycle and provider decomposition | #1223-#1230 |
|
||||
| Frontend API | #1165 credential-aware requests | #1218 |
|
||||
| Dependencies | #1170 unused direct dependencies | #1217 |
|
||||
| Container | #1166 production runtime and size contract | #1222 |
|
||||
| Type safety | #1173 lint-debt ratchet | #1221 |
|
||||
| CodeQL baseline | #1231 initial alert triage, remediation, and disposition | #1232-#1235 |
|
||||
| Coordinated security | Private release blocker integrated without premature disclosure | #1236 |
|
||||
| Release validation | Recovery-key alphabet and WebSocket header forwarding | #1238, #1239 |
|
||||
| Release validation | Same-task lifecycle invocation ordering | #1240, #1241 |
|
||||
| Release validation | Sanitized URI prefix validation | #1242, #1243 |
|
||||
|
||||
The initial CodeQL baseline contained 195 open alerts. All were reviewed: 67
|
||||
were closed through source remediation and 128 received specific,
|
||||
evidence-backed dispositions. The post-merge default-branch Security Gates run
|
||||
[`32700390853`](https://github.com/BradGroux/veritas-kanban/actions/runs/32700390853)
|
||||
completed successfully at `1cdcd6ec60e3f48b6017146b2583fa82f7061c68`
|
||||
with zero open alerts.
|
||||
|
||||
The 2026-08-24 pre-release and post-publication GitHub security readbacks
|
||||
confirm Dependabot security updates, secret scanning, and push protection are
|
||||
enabled, and open Dependabot, secret-scanning, and default-branch CodeQL alert
|
||||
counts are all zero. Every external workflow action reference is pinned to a
|
||||
full commit SHA. The final readback was taken after the exact-main Security
|
||||
Gates run passed.
|
||||
|
||||
Final-milestone preflight on 2026-08-24 found local Node 26.7.0, pnpm 11.1.1,
|
||||
Git 2.55.0, and an available Docker 29.2.1 server. The Node 22 floor remains
|
||||
the `ci:full` runner gate; no separate local Node 22 installation is present.
|
||||
The required macOS signing secret names and the complete App Store Connect
|
||||
notarization secret-name set are configured, without reading their values. The
|
||||
validation host had no existing Veritas Kanban app or cask before publication.
|
||||
The live Homebrew install therefore replaced no active application or user data.
|
||||
|
||||
### 6.1.2 verification matrix
|
||||
|
||||
The final matrix ran once on the fully integrated candidate. The checked-in
|
||||
evidence update is documentation-only and does not alter the validated runtime.
|
||||
|
||||
| Gate | Environment | Candidate result |
|
||||
| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Frozen install, package-manager, security-artifact, delivery-cadence, and CI-scope policy | Local Node 26.7.0 and CI Node 22; pnpm 11.1.1 | Pass. Frozen install completed; package-manager, 1,864-file security-artifact, 18/18 delivery-cadence, and 18/18 CI-scope gates passed |
|
||||
| Typecheck, lint, 458-warning budget, lint report, production/full audit, and gitleaks | Local Node 26.7.0 and CI Node 22 | Pass. Typecheck and lint completed with zero errors and the exact 458-warning budget; production and full audits found no known vulnerabilities; gitleaks passed |
|
||||
| Workspace and orchestration units | Local clean worktree and [CI run 32732019845](https://github.com/BradGroux/veritas-kanban/actions/runs/32732019845) | Pass. Local workspace packages reported 4,289 passed and 24 skipped, plus 3/3 root orchestration tests. CI independently reported server 3,376 passed/5 skipped, web 780 passed, CLI 62 passed, MCP 71 passed/19 skipped, and dual-storage parity 4/4 |
|
||||
| Critical-path coverage ratchets | CI Node 22 on frozen candidate | Pass. All seven boundaries passed: server dispatch 65.2% lines, auth 59.05%, storage 50.45%, web 55.25%, CLI 52.07%, MCP 52.39%, and desktop 66.24%; [coverage artifact](https://github.com/BradGroux/veritas-kanban/actions/runs/32732019845/artifacts/9521857932) SHA-256 `395ae4aef57b8dcb24eabe2ad2ac628f8bbe51564195b7525e5c2be57613a096` |
|
||||
| Playwright Chromium and WebKit | [Scheduled QA run 32732019821](https://github.com/BradGroux/veritas-kanban/actions/runs/32732019821) | Pass. 37/37 cases passed in 3.6 minutes with zero retries; k6 completed 7/7 checks, 5 requests, zero request failures, and one uninterrupted smoke iteration |
|
||||
| Build, Mantine QA, CLI/MCP smoke | Local clean worktree and CI Node 22 | Pass. Build and Mantine QA passed; initial JS/CSS were 242.3/53.7 KiB gzip. CLI/MCP compatibility had zero failures or warnings; two live read/write checks were explicitly skipped because the isolated profile had no `VK_API_KEY` |
|
||||
| Desktop tests, build, readiness, native lifecycle, and unsigned package | macOS arm64 isolated profile and [artifact run 32732019898](https://github.com/BradGroux/veritas-kanban/actions/runs/32732019898) | Pass. Desktop 67/67, Electron artifacts 4/4, readiness 7/7, package smoke, visible setup/readiness, single-instance, close/reopen, and clean quit all passed. Mounted DMG and ZIP report 6.1.2 arm64. DMG: 265,821,857 bytes, SHA-256 `562aa08c1d93653227aa0deee7cc0020f42bfe4ead9e404005fbe67a87822d7a`; ZIP: 270,676,980 bytes, SHA-256 `e1dc99f95e1c3396cda78c5e38582cdc7554baf2757aa57cfee411ba6a94de60`. CI macOS/Linux/Windows unsigned artifacts all passed |
|
||||
| Production Docker build, image-size contract, and runtime smoke | amd64 [Docker contract run 32732019831](https://github.com/BradGroux/veritas-kanban/actions/runs/32732019831) | Pass. Image size 571,628,184 bytes, below 600,000,000; non-root user, version, mounted paths, SQLite, backup, auth, static web, health, bcrypt, and clean shutdown passed |
|
||||
| Release-format and release validators | Version 6.1.2 | Pass with one classified host limitation. Canonical release format passed 3/3 and every source/build-output check passed. Post-publication GitHub, tag, and body validation passed. The local Docker-enabled wrapper reached image assembly before Docker Desktop returned an `overlayfs` containerd metadata I/O error on a full host filesystem; the clean CI Docker build and complete runtime contract passed on the same frozen source |
|
||||
|
||||
## 6.1.1 Maintenance Release Candidate
|
||||
|
||||
| Field | Value |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release version | 6.1.1 |
|
||||
| Source branch | `fix/release-6-1-1-1156` |
|
||||
| Source baseline | `main` after the audited task-drawer fix, dependency rollup, and Chalk 6 disposition (#1154, #1155, #1149) |
|
||||
| Included issues | [#1153](https://github.com/BradGroux/veritas-kanban/issues/1153) and release tracker [#1156](https://github.com/BradGroux/veritas-kanban/issues/1156) |
|
||||
| Pull-request audit | #1154 accepted and merged with browser proof; #1155 accepted with refreshed security floors; #1149 accepted after Node.js and test review; #1150 closed as unnecessary because `pnpm/action-setup@v6` already resolves to 6.0.9; #1148 rejected because its engine floor and 65 failing tests violate the release contract |
|
||||
| Publication state | Complete. PR #1157 merged as `2cfb89396da7e115571f3c1449449ef60bde53d7`; annotated `v6.1.1`, the live GitHub release body, signed/notarized assets, installed-app verification, and Homebrew PR #49 are verified. |
|
||||
|
||||
### 6.1.1 verification evidence
|
||||
|
||||
| Gate | Candidate result |
|
||||
| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Task Detail and mobile browser regressions | Chromium verifies the Mantine tabs root is a flex column, the overlay has real overflow, and wheel input increases the scroll position. Chromium and WebKit verify nested task-card controls do not activate the card and status movement completes through the visible save lifecycle |
|
||||
| Workflow storage readiness | The release-PR workspace job exposed an `ENOENT` startup race between asynchronous workflow-directory creation and the first write. File-backed workflow operations now await the shared readiness promise; the complete optimistic-concurrency route test passes in ten consecutive isolated runs. |
|
||||
| Dependency security | `pnpm audit --prod` and full `pnpm audit` report no known vulnerabilities after stale override floors were refreshed |
|
||||
| Dependency PR CI | Build, lint/typecheck, security audit, affected-workspace tests, and unsigned macOS, Linux, and Windows packaging passed on #1155 |
|
||||
| Consolidated candidate | Frozen install, production and full audits, package-manager policy, release formatting, lint and 592-warning budget, typecheck, build, 3,771 workspace tests, 37 Playwright cases across Chromium and WebKit, Mantine QA, CLI/MCP smoke, 50 Buzz compatibility tests, 67 desktop tests, seven readiness tests, unsigned macOS packaging, packaged-app smoke, and the 6.1.1 release validator pass. The local Docker build variant was unavailable because the Docker daemon was not running. Independent review is owner-directed and is not part of the active release SOP. |
|
||||
|
||||
## 6.1.0 Roadmap Release
|
||||
|
||||
| Field | Value |
|
||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release version | 6.1.0 |
|
||||
| Source branch | `release/v6.1.0` |
|
||||
| Source baseline | `0a19b9050da8915042860f55ef9ca009d164e816`, the release integration of `main` after #1142 cleared the milestone blockers |
|
||||
| Included issues | [#855](https://github.com/BradGroux/veritas-kanban/issues/855), [#864](https://github.com/BradGroux/veritas-kanban/issues/864), [#865](https://github.com/BradGroux/veritas-kanban/issues/865), [#866](https://github.com/BradGroux/veritas-kanban/issues/866), [#867](https://github.com/BradGroux/veritas-kanban/issues/867), [#868](https://github.com/BradGroux/veritas-kanban/issues/868), [#871](https://github.com/BradGroux/veritas-kanban/issues/871), [#872](https://github.com/BradGroux/veritas-kanban/issues/872), [#873](https://github.com/BradGroux/veritas-kanban/issues/873), [#876](https://github.com/BradGroux/veritas-kanban/issues/876), and [#879](https://github.com/BradGroux/veritas-kanban/issues/879) |
|
||||
| Source verification | Focused verification passed per merged roadmap slice. PR #1142 completed append-only admission writes, restored the complete filesystem test-double surface, and mapped the exact knowledge-collection permission prefix. The consolidated candidate then passed `pnpm test:unit` across every workspace; server reported 3,234 passing and 5 skipped tests, web reported 469 passing tests, and desktop reported 67 passing tests. |
|
||||
| Publication state | Complete. PR #1137 merged as `e5aba49e61fca35c14616574a22174c0f848812f`; annotated `v6.1.0`, signed/notarized assets, post-publication validation, isolated installed-app readiness, and Homebrew PR #46 are verified. |
|
||||
|
||||
### 6.1.0 issue traceability
|
||||
|
||||
| Issue | Outcome |
|
||||
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| [#855](https://github.com/BradGroux/veritas-kanban/issues/855) | Run-scoped egress gateway and enforceable network policy |
|
||||
| [#864](https://github.com/BradGroux/veritas-kanban/issues/864) | Durable admission, aggregate budgets, fairness, cancellation, and circuit breaking |
|
||||
| [#865](https://github.com/BradGroux/veritas-kanban/issues/865) | Durable goal supervision across turns, restarts, and continuations |
|
||||
| [#866](https://github.com/BradGroux/veritas-kanban/issues/866) | Reviewed, attributable memory extraction and consolidation |
|
||||
| [#867](https://github.com/BradGroux/veritas-kanban/issues/867) | Source-grounded workspace knowledge collections |
|
||||
| [#868](https://github.com/BradGroux/veritas-kanban/issues/868) | Knowledge integrity linting, material-claim lifecycle, scheduling, semantic candidates, and health |
|
||||
| [#871](https://github.com/BradGroux/veritas-kanban/issues/871) | Run-scoped background-command and monitor supervision |
|
||||
| [#872](https://github.com/BradGroux/veritas-kanban/issues/872) | Preview-first turn-boundary checkpoints and attributable rewind |
|
||||
| [#873](https://github.com/BradGroux/veritas-kanban/issues/873) | Stalled and repetitive run detection with bounded recovery |
|
||||
| [#876](https://github.com/BradGroux/veritas-kanban/issues/876) | Governed artifact spill for oversized run output |
|
||||
| [#879](https://github.com/BradGroux/veritas-kanban/issues/879) | Agent-dependency load shedding integrated with circuit breakers |
|
||||
|
||||
## 6.0.2 Desktop Recovery Candidate
|
||||
|
||||
| Field | Value |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release version | 6.0.2 |
|
||||
| Release tracker | [#1010](https://github.com/BradGroux/veritas-kanban/issues/1010) |
|
||||
| Source branch | `release/v6.0.2-1010` |
|
||||
| Source baseline | `2f18229ff24153ef33b5a21de21f0befb94d8a08`, the merged #1005 main commit with green CI and cross-platform unsigned desktop artifacts |
|
||||
| Included issues | [#1000](https://github.com/BradGroux/veritas-kanban/issues/1000), [#1004](https://github.com/BradGroux/veritas-kanban/issues/1004), [#1005](https://github.com/BradGroux/veritas-kanban/issues/1005), and [#1010](https://github.com/BradGroux/veritas-kanban/issues/1010) |
|
||||
| Source verification | Focused tests and rendered smoke per fix; reviewed full workspace evidence on the exact #1005 merge ancestry; one milestone release validation gate |
|
||||
| Signed runtime gate | Exact 6.0.2 equality across bundle, health, updater, native About, copied support text, and desktop bridge; Right/Bottom Chat recovery at the minimum supported window; bounded task; clean quit |
|
||||
| Distribution gate | Annotated tag, reviewed full-width GitHub release body, signed/notarized DMG and ZIP, updater metadata, independent digests, GitHub release validation, and verified Homebrew cask |
|
||||
|
||||
### 6.0.2 issue traceability
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| ---------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| [#1000](https://github.com/BradGroux/veritas-kanban/issues/1000) | [#1003](https://github.com/BradGroux/veritas-kanban/pull/1003) | Deterministic documentation, focused, and full CI test scope |
|
||||
| [#1004](https://github.com/BradGroux/veritas-kanban/issues/1004) | [#1008](https://github.com/BradGroux/veritas-kanban/pull/1008) | Bounded right/bottom Chat dock with complete visible recovery paths |
|
||||
| [#1005](https://github.com/BradGroux/veritas-kanban/issues/1005) | [#1009](https://github.com/BradGroux/veritas-kanban/pull/1009) | Authoritative native version/build/channel/OS/architecture record and offline copy action |
|
||||
| [#1010](https://github.com/BradGroux/veritas-kanban/issues/1010) | [#1011](https://github.com/BradGroux/veritas-kanban/pull/1011) | Version, documentation, full-width release body, signed publication, downloaded-app verification, and Homebrew gate |
|
||||
|
||||
## 6.0.1 Stabilization Candidate
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Release version | 6.0.1 |
|
||||
| Stabilization tracker | [#924](https://github.com/BradGroux/veritas-kanban/issues/924) |
|
||||
| Source branch | `release/v6.0.1` |
|
||||
| Superseded publication | [`v6.0.0`](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.0), retained as a quarantined prerelease |
|
||||
| Stabilization issues | [#935](https://github.com/BradGroux/veritas-kanban/issues/935), [#936](https://github.com/BradGroux/veritas-kanban/issues/936), [#937](https://github.com/BradGroux/veritas-kanban/issues/937), [#938](https://github.com/BradGroux/veritas-kanban/issues/938), [#939](https://github.com/BradGroux/veritas-kanban/issues/939), [#941](https://github.com/BradGroux/veritas-kanban/issues/941), [#943](https://github.com/BradGroux/veritas-kanban/issues/943), [#944](https://github.com/BradGroux/veritas-kanban/issues/944), [#945](https://github.com/BradGroux/veritas-kanban/issues/945), and [#986](https://github.com/BradGroux/veritas-kanban/issues/986) |
|
||||
| Source verification | Focused tests plus changed-file CI per issue; one full workspace suite is reserved for the release PR |
|
||||
| Signed runtime gate | Exact 6.0.1 equality across bundle, health, updater, and desktop bridge metadata; Chat recovery; bounded task; clean quit |
|
||||
| Distribution gate | Signed/notarized DMG and ZIP, updater metadata, independent checksums, GitHub release validation, and verified Homebrew cask |
|
||||
|
||||
### Stabilization issue traceability
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| -------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
|
||||
| [#935](https://github.com/BradGroux/veritas-kanban/issues/935) | [#992](https://github.com/BradGroux/veritas-kanban/pull/992) | Task drawers and shared overlays remain usable at compact heights |
|
||||
| [#936](https://github.com/BradGroux/veritas-kanban/issues/936) | [#989](https://github.com/BradGroux/veritas-kanban/pull/989) | Workflow actions normalize omitted collections and surface recoverable failures |
|
||||
| [#937](https://github.com/BradGroux/veritas-kanban/issues/937) | [#993](https://github.com/BradGroux/veritas-kanban/pull/993) | Navigation and nested overlays preserve the actual route origin and scroll state |
|
||||
| [#938](https://github.com/BradGroux/veritas-kanban/issues/938) | [#995](https://github.com/BradGroux/veritas-kanban/pull/995) | Scoring profile content has intentional nested scroll ownership |
|
||||
| [#939](https://github.com/BradGroux/veritas-kanban/issues/939) | [#991](https://github.com/BradGroux/veritas-kanban/pull/991) | Archive cards and layout remain reachable at compact sizes |
|
||||
| [#941](https://github.com/BradGroux/veritas-kanban/issues/941) | [#996](https://github.com/BradGroux/veritas-kanban/pull/996) | Template editing is a complete visible authoring flow |
|
||||
| [#943](https://github.com/BradGroux/veritas-kanban/issues/943) | [#990](https://github.com/BradGroux/veritas-kanban/pull/990) | New scoring profiles open as visible validated drafts |
|
||||
| [#944](https://github.com/BradGroux/veritas-kanban/issues/944) | [#994](https://github.com/BradGroux/veritas-kanban/pull/994) | Operations Digest reconciles current state, windowed events, exclusions, and source evidence |
|
||||
| [#945](https://github.com/BradGroux/veritas-kanban/issues/945) | [#988](https://github.com/BradGroux/veritas-kanban/pull/988) | Chat has visible, keyboard, browser-history, persisted-state, compact-window, and native-menu recovery paths |
|
||||
| [#986](https://github.com/BradGroux/veritas-kanban/issues/986) | [#997](https://github.com/BradGroux/veritas-kanban/pull/997) | Desktop setup is version-neutral and the bridge consumes Electron's application version |
|
||||
|
||||
## Historical 6.0.0 Release Scope
|
||||
|
||||
| Field | Value |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release version | 6.0.0 |
|
||||
| Release tracker | [Veritas Kanban 6.0.0 harness parity and Buzz integration](https://github.com/BradGroux/veritas-kanban/issues/924) |
|
||||
| Buzz epic | [First-class Buzz integration](https://github.com/BradGroux/veritas-kanban/issues/904) |
|
||||
| Harness epic | [Equal-footing agent harness support](https://github.com/BradGroux/veritas-kanban/issues/915) |
|
||||
| Implementation baseline | `398fe7f67e94c3d6cabee0702b2331d9c873fd0f` |
|
||||
| Release branch | `release/v6.0.0` |
|
||||
| Release PR | [#985](https://github.com/BradGroux/veritas-kanban/pull/985) |
|
||||
| Release merge | [`1bd43f9279f5ab736bff03378d5d85243e16813e`](https://github.com/BradGroux/veritas-kanban/commit/1bd43f9279f5ab736bff03378d5d85243e16813e) |
|
||||
| Tag and GitHub release | [`v6.0.0`](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.0) |
|
||||
| Desktop Release workflow | [Run 30109135335](https://github.com/BradGroux/veritas-kanban/actions/runs/30109135335) |
|
||||
| Homebrew PR | [#37](https://github.com/BradGroux/homebrew-tap/pull/37), merged as `7d77106e7c526a0975c49b9b11e0c9526921ead0` |
|
||||
| Evidence host | macOS 26.5.2 arm64; Node 26.5.0; pnpm 11.1.1; Git 2.55.0 |
|
||||
|
||||
## Issue And Pull Request Traceability
|
||||
|
||||
|
|
@ -215,27 +392,127 @@ test.
|
|||
No private data, credential value, raw provider conversation, or unrestricted
|
||||
runtime profile is retained in this packet.
|
||||
|
||||
## Publication Evidence
|
||||
## 6.1.2 Publication Evidence
|
||||
|
||||
Source publication values are intentionally pending until the release PR
|
||||
merges and the annotated tag exists. Signed distribution values are
|
||||
intentionally pending until the tag-triggered workflow completes.
|
||||
Source publication, signed-macOS verification, full-width release-note
|
||||
validation, isolated installed-app readiness, Homebrew distribution, and the
|
||||
approved advisory disposition are complete.
|
||||
|
||||
| Publication item | Result |
|
||||
| ----------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| Release merge SHA | Pending |
|
||||
| Annotated `v6.0.0` tag object and peeled commit | Pending |
|
||||
| GitHub release URL | Pending |
|
||||
| Desktop Release workflow URL and duration | Pending |
|
||||
| Signed/notarized DMG | Pending name, bytes, SHA-256, GitHub digest, signature, Gatekeeper, stapling |
|
||||
| Signed/notarized ZIP | Pending name, bytes, SHA-256, GitHub digest, signature, Gatekeeper, stapling |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Pending |
|
||||
| `latest-mac.yml` | Pending version, names, sizes, SHA-512 matches |
|
||||
| Downloaded signed-app isolated launch | Pending |
|
||||
| Homebrew tap issue/PR/merge | Pending |
|
||||
| Homebrew style/audit/dry-run/livecheck | Pending |
|
||||
| Publication item | Result |
|
||||
| --- | --- |
|
||||
| Release PR and merge | [#1237](https://github.com/BradGroux/veritas-kanban/pull/1237); frozen full-matrix head `2a21178df97a00395cfc6c43774a57496a5c1f6a`; final evidence-only head `ec1d3a7e106e2dd2753d41b5e351cdf665e2cd7c`; verified squash merge `dfae7911cc282e32262a006e2171ce5fe4865714` |
|
||||
| Annotated `v6.1.2` tag object and peeled commit | `819aad9ae8eae3f2f40593d4567647963f7bfbc3`; `dfae7911cc282e32262a006e2171ce5fe4865714` |
|
||||
| GitHub release URL and body | [Veritas Kanban 6.1.2](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.1.2); stable release published 2026-08-24; the live body exactly matches `docs/releases/v6.1.2.md` |
|
||||
| Exact-main CI and security | [CI run 32734012479](https://github.com/BradGroux/veritas-kanban/actions/runs/32734012479) and [Security Gates run 32734012461](https://github.com/BradGroux/veritas-kanban/actions/runs/32734012461) passed on the exact release merge; CodeQL, Dependabot, secret scanning, push protection, gitleaks, and immutable-action gates have no unresolved release blockers |
|
||||
| Desktop Release workflow | [Run 32734749604](https://github.com/BradGroux/veritas-kanban/actions/runs/32734749604); exact release merge SHA; signed and notarized macOS job passed in 11m1s |
|
||||
| Signed/notarized DMG | `Veritas-Kanban-6.1.2-mac-arm64.dmg`; 267,430,041 bytes; SHA-256/GitHub digest and published sidecar value `1b2a75c241642a8af14e48827dfc48c5e7846f2709af2359dc1ae34ba2588baa`; Apple notarization accepted, stapling validated, and Gatekeeper accepted Notarized Developer ID `RLBHD62MPW` |
|
||||
| Signed/notarized ZIP | `Veritas-Kanban-6.1.2-mac-arm64.zip`; 271,666,610 bytes; SHA-256/GitHub digest and published sidecar value `81ea146d20d2ab279331e73c01bc3c4eafdda8a4082be3607e9ca535a7d2be85`; the independently downloaded Homebrew cache matched this digest and contained bundle version 6.1.2 |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Blockmap digests `9c99a416dbc518306456bf7025cbe478ea1242a40eb8acf349eb3dc67946ee97` and `77e55eb209f04b10bc1d38e2600032a1b2e808b4eeb11389d9e02819d306c935`; sidecar-file digests `30a6aa1831ed8f8f9c93c0e7e705c5f5e18805d092033732352f4513b6077c5a` and `9608ca6f5b365d9f74d0e6f9b3f71164dee4212af136b8cc8adf5650908b9790` |
|
||||
| `latest-mac.yml` | Version 6.1.2; 530 bytes; SHA-256 `591073ea888481ffdf1b0d2dc1d026b751422d44954ff2084bf7f8e64151992b`; ZIP and DMG names, sizes, and SHA-512 values match the published assets |
|
||||
| Installed signed-app isolated launch | Homebrew installed 6.1.2 without replacing an existing app or cask. `CFBundleShortVersionString` and `CFBundleVersion` report 6.1.2; deep strict code-signature validation, hardened runtime, Gatekeeper acceptance, and stapling validation pass. A disposable user-data root launched the installed app on isolated port 3101; exact-version readiness passed in 209ms and proved the packaged app owned the listener. `/api/health` reported 6.1.2, first-run onboarding showed all local readiness checks healthy, native menus rendered, and the task-owned process tree and listener stopped without touching the separate development build |
|
||||
| Release validator | Post-publication `pnpm validate:release -- --version 6.1.2 --github --repo BradGroux/veritas-kanban` passes and proves the live release and body match the annotated tag and canonical checked-in file |
|
||||
| Homebrew cask | [Issue #50](https://github.com/BradGroux/homebrew-tap/issues/50); [PR #51](https://github.com/BradGroux/homebrew-tap/pull/51); reviewed head `a3269d43eb657c0480e549e4d1fc15bc21baddea`; verified merge `2b29b79a26b269ed832f38b7173c7459c3db508d`; registered `bradgroux/tap/veritas-kanban` resolves 6.1.2 with the published ZIP checksum and passes Ruby syntax, cask style, strict online audit, dry-run install, livecheck, actual installation, signature/stapling/Gatekeeper verification, and exact-version packaged readiness |
|
||||
| Advisory disposition | Owner approved publication after supported artifacts were available. The [repository security advisory](https://github.com/BradGroux/veritas-kanban/security/advisories/GHSA-4r99-qpvh-wrqf) was published 2026-08-24 with the affected range and `>= 6.1.2` patched version verified |
|
||||
| Classified host limitation | The first local Docker wrapper and first Homebrew audit encountered the validation host's exhausted filesystem. No gate was weakened: clean CI proved the Docker contract, owner-approved removal of 1.67 GB of regenerable release output restored capacity, and the same live Homebrew audit/install then passed. No source, evidence, active development build, or user workspace was removed |
|
||||
|
||||
## 6.1.1 Publication Evidence
|
||||
|
||||
Source publication, signed-macOS verification, full-width release-note validation, isolated installed-app readiness, and Homebrew distribution are complete.
|
||||
|
||||
| Publication item | Result |
|
||||
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release PR and merge | [#1157](https://github.com/BradGroux/veritas-kanban/pull/1157); reviewed head `5813d0d3d5285558582cb76c97e7d824b5ad9425`; verified squash merge `2cfb89396da7e115571f3c1449449ef60bde53d7` |
|
||||
| Annotated `v6.1.1` tag object and peeled commit | `c18955bbd7cc582f7ccd41a958ec1f53b6907cc2`; `2cfb89396da7e115571f3c1449449ef60bde53d7` |
|
||||
| GitHub release URL and body | https://github.com/BradGroux/veritas-kanban/releases/tag/v6.1.1; stable release published 2026-08-22; the live body exactly matches `docs/releases/v6.1.1.md` and is the latest non-prerelease |
|
||||
| Release CI and unsigned packaging | [CI run 32608829719](https://github.com/BradGroux/veritas-kanban/actions/runs/32608829719) passed build, security audit, lint/typecheck, scope selection, 3,771 workspace tests, and the changed-test gate; [artifact run 32608829776](https://github.com/BradGroux/veritas-kanban/actions/runs/32608829776) passed macOS, Linux, and Windows unsigned packaging on the reviewed head |
|
||||
| Desktop Release workflow URL and duration | [Run 32609140955](https://github.com/BradGroux/veritas-kanban/actions/runs/32609140955); exact release merge SHA; signed and notarized job passed in 11m37s |
|
||||
| Signed/notarized DMG | `Veritas-Kanban-6.1.1-mac-arm64.dmg`; 268,632,259 bytes; SHA-256 and published sidecar value `7fa44f4129be751757a7e049cd3681266cab3215402cd2e2a5d8b0acf38c6c11`; independent download, signature, Gatekeeper, and stapling validation pass |
|
||||
| Signed/notarized ZIP | `Veritas-Kanban-6.1.1-mac-arm64.zip`; 272,805,716 bytes; SHA-256 and published sidecar value `cd9c1cc68d474d3dbea8b6aae90380f5188f041009c229756371eeba7eecd9b0`; Homebrew strict online audit independently downloaded and accepted the asset |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Blockmap digests `fe15ce89df44e720e3b1c9381b035d80a079042697a592c12b2be9cad96a949e` and `a5b9b910d458ab430daee0ae61ad962948c115bd69436fdbc4e56af861db38b8`; sidecar-file digests `7f29b0095ff92529b0c9fdcffac539fe4ae20adff772011f4ed14ca735a641c6` and `fd0cbb9aa58315f4143c1b4daf9aad158c150283086660a6be93829e42b9ccce` |
|
||||
| `latest-mac.yml` | Version 6.1.1; 530 bytes; SHA-256 `df7b4f16c3f6c21720f7446af531967854bc3901749f09aaa87e5d1d06b1eb11`; ZIP and DMG names, sizes, and SHA-512 values match the published assets |
|
||||
| Installed signed-app isolated launch | Homebrew upgraded the installed cask from 6.1.0 to 6.1.1. `CFBundleShortVersionString` reports 6.1.1; deep strict code-signature validation, Gatekeeper acceptance from notarized Developer ID `RLBHD62MPW`, and stapling validation pass. A disposable profile and workspace launched the packaged app on isolated port 3101; exact-version readiness passed in 4.289s and proved the packaged app owned the listener. `/api/health` reported 6.1.1, the process stopped cleanly, both disposable user-data directories moved to Trash, and the existing workspace was not opened. |
|
||||
| Release validator | Post-publication `pnpm validate:release -- --version 6.1.1 --skip-build-output --github --repo BradGroux/veritas-kanban` passes and proves the live release and body match the annotated tag and canonical checked-in file |
|
||||
| Homebrew cask | [Issue #48](https://github.com/BradGroux/homebrew-tap/issues/48); [PR #49](https://github.com/BradGroux/homebrew-tap/pull/49); reviewed head `4956cd0c7245ad28571f0860ce1f0d63159e4ff5`; verified merge `033983c2d76eccb6ef348e529a42f53d328ef335`; registered `bradgroux/tap/veritas-kanban` resolves 6.1.1 with the published ZIP checksum and passes Ruby syntax, cask style, strict online audit, dry-run install, livecheck, installed-cask upgrade, and exact-version packaged readiness |
|
||||
|
||||
## 6.1.0 Publication Evidence
|
||||
|
||||
Source publication, signed-macOS verification, full-width release-note validation, isolated installed-app readiness, and Homebrew distribution are complete.
|
||||
|
||||
| Publication item | Result |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release PR and merge | [#1137](https://github.com/BradGroux/veritas-kanban/pull/1137); reviewed head `ec9d7b6e86bcb5566cf04419db8817fa836508ae`; squash merge `e5aba49e61fca35c14616574a22174c0f848812f` |
|
||||
| Annotated `v6.1.0` tag object and peeled commit | `415a88e50e96a5c1ca02034e7e2392c08482d6e0`; `e5aba49e61fca35c14616574a22174c0f848812f` |
|
||||
| GitHub release URL and body | https://github.com/BradGroux/veritas-kanban/releases/tag/v6.1.0; stable release published 2026-07-26; the post-publication release validator proves the live body exactly matches `docs/releases/v6.1.0.md`, which passes the checked-in full-width format gate |
|
||||
| Release CI and unsigned packaging | [CI run 30199975494](https://github.com/BradGroux/veritas-kanban/actions/runs/30199975494) passed security audit, lint, typecheck, build, and changed tests; [artifact run 30199975488](https://github.com/BradGroux/veritas-kanban/actions/runs/30199975488) passed macOS, Linux, and Windows preview packaging |
|
||||
| Desktop Release workflow URL and duration | [Run 30200162940](https://github.com/BradGroux/veritas-kanban/actions/runs/30200162940); exact release merge SHA; signed and notarized job passed in 10m55s |
|
||||
| Signed/notarized DMG | `Veritas-Kanban-6.1.0-mac-arm64.dmg`; 277,393,979 bytes; SHA-256/GitHub digest `b7f3fb09a4b35c34bf015b3fa4f878bf6c91b126d2f5c5bad70e602839b9d599`; release workflow signature, Gatekeeper, and stapling validation passed |
|
||||
| Signed/notarized ZIP | `Veritas-Kanban-6.1.0-mac-arm64.zip`; 281,520,166 bytes; SHA-256/GitHub digest and published sidecar `6ca5f99394fb551b6b5fb60006ffaa8e5a793808be1382d49c4b3a3209e98701`; Homebrew strict online audit independently downloaded and accepted the asset |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Blockmap digests `29582df6dbdc121cbbfab4255c7ccaf5c258100456be7230b971d65a27a8eaf9` and `f9834743c9523f334c6d7113af00402f16c18c08a5f35345313ad0f644ecb886`; sidecar digests `33b2eb510b4eaa6b51082aa9dc0090b4e5e578316d51b98dcf5305016d63577e` and `bdf782510a2aebb35b22d29875d92bcaf0c75415164c0644f01d43a58914e301` |
|
||||
| `latest-mac.yml` | Version 6.1.0; 530 bytes; SHA-256 `b51be7cb54b37dc7cf49e045c276d7d09a4563e9462583a3eaec72d972c88c6d` |
|
||||
| Installed signed-app isolated launch | Homebrew upgraded the installed cask from 6.0.2 to 6.1.0. `CFBundleShortVersionString` reports 6.1.0; deep strict code-signature validation, Gatekeeper acceptance from notarized Developer ID `RLBHD62MPW`, and stapling validation pass. A disposable profile and workspace launched the packaged app on isolated port 3101; exact-version readiness passed in 6.294s and proved the packaged app owned the listener. The process stopped cleanly, the disposable user-data directory moved to Trash, and the existing workspace was not opened. |
|
||||
| Compatibility smoke | CLI/MCP version and build-output smoke passed with zero failures and two expected live read/write skips because no `VK_API_KEY` was supplied; Buzz deterministic compatibility passed 7 files and 50 tests |
|
||||
| Release validator | Post-publication `pnpm validate:release -- --version 6.1.0 --skip-build-output --github --repo BradGroux/veritas-kanban` passes and proves the published release body equals the canonical checked-in file |
|
||||
| Homebrew cask | [Issue #45](https://github.com/BradGroux/homebrew-tap/issues/45); [PR #46](https://github.com/BradGroux/homebrew-tap/pull/46); reviewed head `222c5a8`; merge `4179cce931e7a7e6401a0e3fcd5097bcc23ff830`; registered `bradgroux/tap/veritas-kanban` resolves 6.1.0 with the published ZIP checksum and passes Ruby syntax, style, strict online audit, dry-run install, livecheck, installed-cask upgrade, and exact-version verification |
|
||||
|
||||
## 6.0.2 Publication Evidence
|
||||
|
||||
Source publication, independent signed-macOS verification, release-note
|
||||
format validation, and Homebrew distribution are complete.
|
||||
|
||||
| Publication item | Result |
|
||||
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release PR and merge | [#1011](https://github.com/BradGroux/veritas-kanban/pull/1011); reviewed head `88fb70c23cf10ac58b6444b9fd08af4077956bb6`; squash merge `3a73662b9c3431115f77401e3620a6839f95b61d`; the two commits have identical Git trees |
|
||||
| Annotated `v6.0.2` tag object and peeled commit | `a56785e0f9052572982332bb974dbab871c0c971`; `3a73662b9c3431115f77401e3620a6839f95b61d` |
|
||||
| GitHub release URL and body | https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.2; stable release published 2026-07-24; live body exactly matches `docs/releases/v6.0.2.md`; no carriage returns, hard-wrapped prose, trailing-space breaks, literal escaped newlines, or HTML `<br>` |
|
||||
| Desktop Release workflow URL and duration | https://github.com/BradGroux/veritas-kanban/actions/runs/30132949865; exact release merge SHA; signed and notarized job passed in 11m52s |
|
||||
| Signed/notarized DMG | `Veritas-Kanban-6.0.2-mac-arm64.dmg`; 276,146,896 bytes; SHA-256/GitHub digest `2cf7deb9c600036468db12436988b1c23b679130f088a46a5a910366119ea52b`; Developer ID `RLBHD62MPW`, hardened runtime, Gatekeeper, and stapling pass; the mounted app independently passes the same signature, Gatekeeper, and stapling checks |
|
||||
| Signed/notarized ZIP | `Veritas-Kanban-6.0.2-mac-arm64.zip`; 279,993,375 bytes; SHA-256/GitHub digest `b6f389c67f3a99086210df104c8cdec4976b9bb56a7cea5b32e611fbc41c21cd`; extracted app Developer ID `RLBHD62MPW`, hardened runtime, Gatekeeper, and stapling pass |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Blockmap digests `02c7fe30788c92b5ec06459a157b5c4096a645c398fdda867090e5ba707e4f9e` and `08ac8ed54189981039368074c0fd085348d4cd080c9cb63ae2924dcb67776398`; sidecar digests `40e95f25cf9a2297db872d3fed33ac198bd5f40901df189069a6a5e08795266f` and `d32a9e1184ce1d8ebaa611c3c4a4fbebcc885072920963b470d37e226ffaf632`; both sidecars verify |
|
||||
| `latest-mac.yml` | Version 6.0.2; 530 bytes; SHA-256 `04797813b0aae9643295329a7ee93aa75f18746a1f4a47992cfb1432813139b3`; DMG/ZIP names, byte sizes, and independently computed SHA-512 values match |
|
||||
| Downloaded signed-app isolated launch | Pass at 1180x760; fresh disposable profile; ready local-production server; bundle, `/api/health`, updater, native About, copied support information, and desktop bridge report 6.0.2 with build `3a73662b9c3431115f77401e3620a6839f95b61d`; Right and Bottom docks remain contained and preserve the active conversation; close, Escape, Back, obsolete-state recovery, and Reset Layout restore the board and focus; bounded task creation passes; latest-version update check passes; native Quit removes app and bundled-server listeners; disposable profiles moved to Trash |
|
||||
| Release validator | Pre-publication `pnpm validate:release -- --version 6.0.2` and `--docker-build` pass; post-publication `pnpm validate:release -- --version 6.0.2 --skip-build-output --github` passes and proves the live body equals the canonical file |
|
||||
| Homebrew cask | [Issue #43](https://github.com/BradGroux/homebrew-tap/issues/43); [PR #44](https://github.com/BradGroux/homebrew-tap/pull/44); reviewed head `3bb23d5c7e7f8593ed8c4b7a294e59e4e6aa754b`; merge `03878fbbf886d24fd543422c484397747e55c80c`; registered `bradgroux/tap/veritas-kanban` resolves 6.0.2 with the published ZIP checksum and passes syntax, style, strict online audit, dry-run install, and livecheck |
|
||||
| Verification-efficiency follow-up | [Issue #1012](https://github.com/BradGroux/veritas-kanban/issues/1012) and [PR #1013](https://github.com/BradGroux/veritas-kanban/pull/1013) stop cosmetic label events from executing or canceling CI and reuse reviewed full-suite evidence only for exact-tree squash merges; two live cosmetic-label runs skipped every job while the authoritative run completed, and the post-merge workspace suite reused the exact reviewed tree |
|
||||
|
||||
## 6.0.1 Publication Evidence
|
||||
|
||||
Source publication, the independent signed-macOS gate, and Homebrew
|
||||
restoration are complete.
|
||||
|
||||
| Publication item | Result |
|
||||
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release PR and merge | [#998](https://github.com/BradGroux/veritas-kanban/pull/998); reviewed head `1c86d90386ce9ed05639c7118ab355385b88fb37`; merge `faeec2752a79ca42adef982ad169f21fd55b0711` |
|
||||
| Annotated `v6.0.1` tag object and peeled commit | `a2388528ecd80e4b4ecc5ce6e8922dcb05f23c95`; `faeec2752a79ca42adef982ad169f21fd55b0711` |
|
||||
| GitHub release URL | https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.1; stable release published 2026-07-24 |
|
||||
| Desktop Release workflow URL and duration | https://github.com/BradGroux/veritas-kanban/actions/runs/30118916300; exact merge SHA; signed and notarized job passed in 11m6s |
|
||||
| Signed/notarized DMG | `Veritas-Kanban-6.0.1-mac-arm64.dmg`; 276,120,202 bytes; SHA-256/GitHub digest `f6b811f46c92532feceb18302a7edd46ecac33580fdd3bf417bb3afd3952c22c`; Developer ID `RLBHD62MPW`, hardened runtime, Gatekeeper, and stapling pass |
|
||||
| Signed/notarized ZIP | `Veritas-Kanban-6.0.1-mac-arm64.zip`; 279,986,716 bytes; SHA-256/GitHub digest `f0f03e0210141b789daed09de34171c07260eaad2cf5f5c1d1f74fbd5296a4de`; extracted app Developer ID `RLBHD62MPW`, hardened runtime, Gatekeeper, and stapling pass |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Blockmap digests `4cc52ca62e0cd3fb44bbe230d44811e8da87d06cef52cb45ba456e4d8e69d593` and `369b0891d9d7ae0a3641ba5cc8d6dfa0a207dfd4fd8ee6b09a46f0353b4fb3db`; sidecar digests `eb2e223c0a0220e748f7b21b9c15c8c09dfc3718e9894ddfb1330ae6d702fac1` and `32ff009d56f6d18b27608ac616dc7c7a01914b5e4382ce22070b7d0bb965b6f6`; both sidecars verify |
|
||||
| `latest-mac.yml` | Version 6.0.1; 530 bytes; SHA-256 `47e6b3a2b24696873699ee11cc15de2d8b7dd580c2302ee698ae4c49ab5abbdc`; DMG/ZIP names, byte sizes, and independently computed SHA-512 values match |
|
||||
| Downloaded signed-app isolated launch | Pass; fresh disposable profile; ready local-production server; bundle, health, updater, and desktop bridge report 6.0.1; onboarding/login and bounded task creation pass; Board Chat preserves the shell; close, Escape, Back, obsolete-state recovery, and native layout reset pass; stable updater reports latest; native Quit removes both app and bundled-server listeners |
|
||||
| Release validator | `pnpm validate:release -- --version 6.0.1 --github --repo BradGroux/veritas-kanban --docker-build` passes, including a clean production image build |
|
||||
| Homebrew cask | [Issue #40](https://github.com/BradGroux/homebrew-tap/issues/40); [PR #42](https://github.com/BradGroux/homebrew-tap/pull/42); reviewed head `0a785cc1956070a8bc05533b194108010997bedb`; merge `c23cc7e1b6d1b1d05f5117b5fdfea67806d42f48`; normal registered tap resolves 6.0.1 with the published ZIP checksum and passes syntax, style, strict online audit, dry-run install, and livecheck; [verification #41](https://github.com/BradGroux/homebrew-tap/issues/41) records the exact command evidence |
|
||||
|
||||
## Historical 6.0.0 Publication Evidence
|
||||
|
||||
Source publication completed from reviewed release PR #985. The tag-triggered
|
||||
workflow, independent artifact verification, signed-app runtime check, and
|
||||
Homebrew publication are also complete.
|
||||
|
||||
| Publication item | Result |
|
||||
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Release merge SHA | `1bd43f9279f5ab736bff03378d5d85243e16813e` |
|
||||
| Annotated `v6.0.0` tag object and peeled commit | `85d0328a23b54bd7e7d578b5725d50cd2e0084f3`; `1bd43f9279f5ab736bff03378d5d85243e16813e` |
|
||||
| GitHub release URL | https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.0 |
|
||||
| Desktop Release workflow URL and duration | https://github.com/BradGroux/veritas-kanban/actions/runs/30109135335; pass in 12m7s |
|
||||
| Signed/notarized DMG | `Veritas-Kanban-6.0.0-mac-arm64.dmg`; 276,118,002 bytes; SHA-256/GitHub digest `0a2a51cb279d42089770932f760bb77abedf9db767c151cc0071d1ee3e1bfa55`; Developer ID, Gatekeeper, and stapling pass |
|
||||
| Signed/notarized ZIP | `Veritas-Kanban-6.0.0-mac-arm64.zip`; 279,976,801 bytes; SHA-256/GitHub digest `7a5947bb6abd440c4d69da36def9f891e154139547f741d2f44c4733c6719cb8`; extracted app Developer ID, hardened runtime, Gatekeeper, and stapling pass |
|
||||
| DMG/ZIP blockmaps and SHA-256 sidecars | Present; blockmap digests `b31e50258dd3744a0daeef45a1f7ae375f538b24e6c68243f13c0963661b95b5` and `af99a35b1e6380a8b9888658054690846c84b325067f7108299a4eedb6993255`; both sidecars verify |
|
||||
| `latest-mac.yml` | Version 6.0.0; 530 bytes; SHA-256 `ac2e5ee7f25d235d607409a62a047359145f80dbaf9f1f5333131dc20700df72`; DMG/ZIP names, sizes, and SHA-512 values match independent downloads |
|
||||
| Downloaded signed-app isolated launch | Pass; fresh disposable profile, ready SQLite, health/update version 6.0.0, latest-version update check, task creation, and clean quit |
|
||||
| Homebrew tap issue/PR/merge | [Issue #36](https://github.com/BradGroux/homebrew-tap/issues/36); [PR #37](https://github.com/BradGroux/homebrew-tap/pull/37); merge `7d77106e7c526a0975c49b9b11e0c9526921ead0` |
|
||||
| Homebrew style/audit/dry-run/livecheck | Pass against registered `bradgroux/tap/veritas-kanban` at 6.0.0 with the published ZIP checksum |
|
||||
|
||||
The final values are added through a focused post-publication documentation PR.
|
||||
Source CI, tag creation, asset upload, signing, notarization, downloaded runtime,
|
||||
and Homebrew availability are independent gates.
|
||||
|
||||
|
|
@ -248,6 +525,11 @@ and Homebrew availability are independent gates.
|
|||
- GitHub Copilot CLI ACP remains public preview.
|
||||
- Buzz Agent does not resume in-memory sessions; Buzz communication does not
|
||||
project files, reactions, forums, DMs, or destructive edit/delete behavior.
|
||||
- [Packaged desktop version reporting](https://github.com/BradGroux/veritas-kanban/issues/986)
|
||||
landed in 6.0.1; [native build and support information](https://github.com/BradGroux/veritas-kanban/issues/1005)
|
||||
is added in 6.0.2. Exact equality across the downloaded bundle, health
|
||||
endpoint, updater metadata, native About, copied support text, and desktop
|
||||
bridge remains a signed-publication gate.
|
||||
- Linux and Windows desktop packages remain unsigned previews.
|
||||
- Provider and Buzz Settings screenshots were captured from the isolated
|
||||
profile. No public-safe active approval existed, so approval visual evidence
|
||||
|
|
|
|||
|
|
@ -1,168 +1,90 @@
|
|||
# Veritas Kanban v6 Release Notes
|
||||
# Veritas Kanban 6.1.2 Release Notes
|
||||
|
||||
Veritas Kanban 6.0.0 makes agent runtimes explicit, evidence-backed, and
|
||||
fail-closed. It adds first-class Buzz integration and puts Grok Build, OpenAI
|
||||
Codex, Claude Code, and GitHub Copilot CLI behind the same provider-neutral run,
|
||||
approval, credential, tool, worktree, event, and completion contracts.
|
||||
Veritas Kanban 6.1.2 completes the reliability, security, persistence, provider-runtime, CI, container, and supportability audit tracked by [#1174](https://github.com/BradGroux/veritas-kanban/issues/1174). It is a backward-compatible patch release for 6.1.1.
|
||||
|
||||
- Release tracker:
|
||||
[Veritas Kanban 6.0.0 harness parity and Buzz integration](https://github.com/BradGroux/veritas-kanban/issues/924)
|
||||
- Buzz epic:
|
||||
[First-class Buzz integration](https://github.com/BradGroux/veritas-kanban/issues/904)
|
||||
- Harness epic:
|
||||
[Equal-footing agent harness support](https://github.com/BradGroux/veritas-kanban/issues/915)
|
||||
- Detailed compatibility record: [Harness Compatibility](HARNESS-COMPATIBILITY.md)
|
||||
- Versioned architecture:
|
||||
[v6 Agent Runtime Control Plane](architecture/V6-AGENT-RUNTIME-CONTROL-PLANE.md)
|
||||
- Retained validation record: [v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md)
|
||||
- Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0
|
||||
> Veritas Kanban 6.0.0 remains a quarantined prerelease. Version 6.1.2 is the supported stable v6 release; its annotated tag, signed assets, updater metadata, and Homebrew cask are published and verified.
|
||||
|
||||
## What Changed For Users
|
||||
## Audit Outcomes And Traceability
|
||||
|
||||
- Settings, `vk doctor --json`, API diagnostics, telemetry, and dispatch now
|
||||
use one `harness-support-profile/v1` state: Detected, Configured, Certified,
|
||||
Degraded, or Unsupported.
|
||||
- Buzz can connect a pinned Nostr community to Squad Chat, import signed public
|
||||
persona and team definitions, run `buzz-agent` through generic ACP, expose a
|
||||
narrow run-scoped Veritas MCP bridge, and trigger allowlisted workflows from
|
||||
root messages.
|
||||
- Grok Build, Buzz Agent, and GitHub Copilot CLI reuse the generic ACP v1 stdio
|
||||
adapter. Codex app-server uses its richer JSON-RPC v2 lifecycle adapter.
|
||||
Claude Code uses a supervised bare-mode stream-json adapter.
|
||||
- Runs persist causal events, exact launch evidence, provider conversation
|
||||
identity, completion evidence, approval decisions, credential boundaries,
|
||||
and supervised ownership so reconnect and restart do not silently duplicate
|
||||
work.
|
||||
- Provider versions, builds, capability digests, probe revisions, and fixtures
|
||||
invalidate stale certification instead of inheriting an old green state.
|
||||
- Packaged SQLite startup now defers run-supervisor repository lookup until
|
||||
storage is initialized, and the desktop updater refuses older release
|
||||
metadata instead of offering a downgrade.
|
||||
| Issue | Operational outcome | Pull requests |
|
||||
| --- | --- | --- |
|
||||
| [#1162](https://github.com/BradGroux/veritas-kanban/issues/1162) | Canonical runtime data paths, legacy discovery, and migration compatibility | #1184 |
|
||||
| [#1163](https://github.com/BradGroux/veritas-kanban/issues/1163) | Service persistence restored behind explicit file and SQLite repositories | #1190-#1220 |
|
||||
| [#1164](https://github.com/BradGroux/veritas-kanban/issues/1164) | Provider launch, runtime, event, completion, mutation, and adapter contracts decomposed | #1223-#1230 |
|
||||
| [#1165](https://github.com/BradGroux/veritas-kanban/issues/1165) | Credential-aware JSON, blob, stream, and download API helpers | #1218 |
|
||||
| [#1166](https://github.com/BradGroux/veritas-kanban/issues/1166) | Measured non-root production Docker runtime and size contract | #1222 |
|
||||
| [#1167](https://github.com/BradGroux/veritas-kanban/issues/1167) | Immutable external GitHub Actions | #1179 |
|
||||
| [#1168](https://github.com/BradGroux/veritas-kanban/issues/1168) | Continuous CodeQL, dependency, and secret scanning | #1180 |
|
||||
| [#1169](https://github.com/BradGroux/veritas-kanban/issues/1169) | Risk-weighted critical-path coverage baselines and ratchets | #1183 |
|
||||
| [#1170](https://github.com/BradGroux/veritas-kanban/issues/1170) | Four unused direct dependencies removed | #1217 |
|
||||
| [#1171](https://github.com/BradGroux/veritas-kanban/issues/1171) | Native-loader-compatible Vite and Vitest configuration | #1178 |
|
||||
| [#1172](https://github.com/BradGroux/veritas-kanban/issues/1172) | Deterministic, milestone-scoped workspace and browser gates | #1175, #1177, #1181, #1228 |
|
||||
| [#1173](https://github.com/BradGroux/veritas-kanban/issues/1173) | Server lint-warning budget reduced from 600 to 458 | #1221 |
|
||||
| [#1231](https://github.com/BradGroux/veritas-kanban/issues/1231) | Initial CodeQL baseline triaged, remediated, and dispositioned | #1232-#1235 |
|
||||
|
||||
## Tested Harness Baselines
|
||||
## Persistence And Runtime Paths
|
||||
|
||||
| Harness | Tested version/build | Transport | Source posture | Release contract |
|
||||
| ----------------------- | ------------------------------------------------------------------------------ | ----------------------- | ---------------------- | -------------------------------------------------------------------------- |
|
||||
| Buzz Agent | Buzz v0.4.24 at `710ed9fff57878a1d69f809b80a6ee0416c53fc4`; `buzz-agent 0.1.0` | ACP v1 stdio | Open source | Disabled by default; exact identity/capabilities required |
|
||||
| Grok Build | v0.2.111 build `94172f2aa4e5` | ACP v1 stdio | Partial source lineage | Disabled by default; alpha self-report and provenance limit retained |
|
||||
| OpenAI Codex app-server | `codex-cli 0.145.0`, upstream `25af12f7e61572b0bc18ddb1008be543b91519b0` | JSON-RPC v2 stdio | Open source | Exact generated schemas and disabled remote control required |
|
||||
| OpenAI Codex SDK | `@openai/codex-sdk 0.144.3` | SDK event stream | Published package | Uses the shared launch, event, tool, credential, and completion contracts |
|
||||
| Claude Code | `2.1.218 (Claude Code)` | supervised stream-json | Partial source | Bare mode, no permission bypass, exact stream fixtures |
|
||||
| GitHub Copilot CLI | v1.0.74 at public tag commit `2b809c84e87dbcc88f897cb4f3fb97c43b77af95` | ACP v1 stdio | Partial source | Public preview; source/release provenance mismatch retained |
|
||||
| Hermes Agent | v2026.7.7.2 | one-shot process | Provider project | Existing one-shot adapter; resume remains unsupported |
|
||||
| OpenClaw | v2026.6.11 | gateway tool invocation | Open source | Existing adapter; operator must allow `sessions_spawn` and `sessions_send` |
|
||||
`DATA_DIR` and `VERITAS_DATA_DIR` now resolve through one canonical path contract. Live services, health, backup, integrity, migrations, and the production container use the same root. Legacy locations remain discoverable and migrate through explicit compatibility paths rather than creating split authoritative state.
|
||||
|
||||
The table states the reviewed baseline, not the current machine's live tier.
|
||||
Only runtime evidence that matches the installed version, build, profile,
|
||||
probe revision, capability digest, and passing deterministic fixtures can
|
||||
report Certified. Credential-gated smoke is separate evidence and is recorded
|
||||
in the release packet when available.
|
||||
Service-layer filesystem access has been moved into deep repository modules across activity, progress, status history, scheduled deliverables, workflows, broadcasts, conflicts, delegation, ceremony, error analyses, permissions, lifecycle configuration, scheduler, reflection, chat, tasks, telemetry, and managed content. File and SQLite backends preserve their containment, locking, atomic-write, and parity contracts.
|
||||
|
||||
## Architecture
|
||||
## Provider Runtime And Frontend API
|
||||
|
||||
Every executable run follows the same authority chain:
|
||||
Provider work now flows through cohesive launch-compiler, runtime-resolution, event-interpreter, completion, attempt-lifecycle, and adapter-registry boundaries. Explicitly executable providers retain their supported behavior. Provider-less, unknown, or profile/adapter-mismatched records still fail before attempt creation and never route through an implicit OpenClaw fallback.
|
||||
|
||||
1. Normalize the selected profile to `harness-support-profile/v1`.
|
||||
2. Probe and persist `provider-runtime-manifest/v1`.
|
||||
3. Render one immutable `task-envelope/v1` through the provider-owned
|
||||
transport.
|
||||
4. Allocate a transactional `worktree-manifest/v1`.
|
||||
5. Compile `run-launch-manifest/v1`, including sandbox, tools, MCP, approvals,
|
||||
environment key names, credential references, and budgets.
|
||||
6. Supervise the local process or remote handle through `run-supervisor/v1`.
|
||||
7. Persist redacted causal `run-event/v1` records before projecting legacy
|
||||
output.
|
||||
8. Broker exact actions through `run-approval/v1` and one-shot credential
|
||||
leases through the system-owned `veritas-run` bridge.
|
||||
9. Finish once through the idempotent `completion-result/v1` contract.
|
||||
Frontend JSON, blob, stream, log, and download operations now share credential-aware API boundaries. Cross-origin `VITE_API_URL` cookie authentication, configured base paths, and server error envelopes remain consistent across supported workflows.
|
||||
|
||||
The generic ACP adapter and the inverse `vk acp serve --stdio` view reuse that
|
||||
chain. Buzz relay delivery remains a communication adapter and never becomes a
|
||||
task or completion authority.
|
||||
## Verification, Security, Dependencies, And Container
|
||||
|
||||
Ordinary pull requests now run source-policy, lint, typecheck, build, dependency-audit, secret-scanning, and CodeQL checks without repeatedly executing workspace tests, coverage, E2E, desktop packaging, load, or Docker contracts. Those expensive gates run at explicit `ci:full`, scheduled, manual, integration, security, and release milestones.
|
||||
|
||||
The complete final release matrix is recorded in [v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md). Historical test counts are not reused as 6.1.2 evidence.
|
||||
|
||||
The production Docker closure excludes unrelated workspace dependencies, runs as a non-root user, and has architecture-specific size ceilings. The implementation baseline measured 195,910,880 bytes on arm64 against a 200,000,000-byte ceiling. The final release candidate measured 571,628,184 bytes on amd64 against its 600,000,000-byte ceiling.
|
||||
|
||||
Four verified unused direct dependencies were removed. The server lint-warning budget dropped from 600 to 458 without weakening rules or adding broad suppressions. A coordinated security remediation is integrated through #1236 and the [repository security advisory](https://github.com/BradGroux/veritas-kanban/security/advisories/GHSA-4r99-qpvh-wrqf) was published after the supported 6.1.2 artifacts were verified. Final milestone validation also corrected recovery-key alphabet generation, WebSocket upgrade header forwarding, same-task lifecycle ordering, and sanitized URI prefix handling through #1239, #1241, and #1243.
|
||||
|
||||
The initial 195-alert CodeQL baseline was reviewed alert by alert: 67 findings were fixed and 128 non-exploitable alerts received evidence-backed dispositions. Validated request, logging, persisted-key, file-handling, and sandbox-read findings were fixed in #1232-#1235. Alerts that were limited to test fixtures or were already contained by explicit authentication, path, descriptor, ownership, or atomic-write controls received documented dispositions rather than speculative code churn. The post-merge default-branch analysis reports zero open alerts.
|
||||
|
||||
## Install Or Upgrade
|
||||
|
||||
Install or upgrade with Homebrew:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
For a first installation:
|
||||
|
||||
```bash
|
||||
brew install --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
Manual installation uses the signed and notarized macOS arm64 DMG or ZIP from the [v6.1.2 release](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.1.2). Back up the complete stopped-writer workspace before upgrading and keep the backup until the new runtime is accepted.
|
||||
|
||||
## Breaking Changes And Migration Warnings
|
||||
|
||||
- Provider-less or adapter/profile-mismatched records no longer fall through to
|
||||
OpenClaw. Only known legacy Codex, Hermes, and Claude records with matching
|
||||
built-in type and command identity may infer an adapter during normalization.
|
||||
- Claude Code no longer uses `--dangerously-skip-permissions`. Existing custom
|
||||
arguments that attempt to bypass permissions, inject configuration, inherit
|
||||
plugins, or select unrelated sessions are rejected.
|
||||
- Unknown or changed provider builds lose certification. An enabled Degraded or
|
||||
Unsupported profile blocks dispatch until the operator fixes, disables, or
|
||||
replaces it.
|
||||
- Credential-bound MCP servers are omitted from provider-native configuration.
|
||||
They run only through the one-shot Veritas bridge with an exact catalog,
|
||||
action, approval, and lease binding.
|
||||
- Generic process stdin is not treated as a successful follow-up path.
|
||||
Conversation controls are available only when the persisted adapter evidence
|
||||
says the provider supports them.
|
||||
- The public REST API remains `v1`; v6 adds contracts and endpoints without
|
||||
renaming the API mount. CLI, MCP, server, web, shared, and desktop package
|
||||
versions must all be 6.0.0.
|
||||
There is no public REST API version change, configuration breaking change, or new SQLite schema migration in 6.1.2. Migrations remain at 30 through 33. Runtime-path normalization can move legacy files into the configured canonical data directory; verify the selected data root, health, integrity, and backup evidence before resuming writers or automation.
|
||||
|
||||
Back up the current workspace before upgrading. Keep the complete v5.2.5
|
||||
backup until v6 runtime verification is accepted. App rollback is safe only
|
||||
when the older binary can open the current schema and profile records; otherwise
|
||||
restore the pre-upgrade backup. See the
|
||||
[v6 Upgrade, Install, Remote, And Admin Guide](V6-UPGRADE-INSTALL-ADMIN-GUIDE.md).
|
||||
|
||||
## Security Model
|
||||
|
||||
- Provider processes start without a shell in the exact assigned worktree.
|
||||
- Environment values are allowlisted. Manifests, APIs, logs, telemetry,
|
||||
fixtures, screenshots, and issue evidence contain key names and opaque
|
||||
references, never secret values.
|
||||
- Approval decisions bind to an exact action digest, reviewer, freshness
|
||||
requirement, expiry, attempt, and provider request. Drift, replay,
|
||||
cancellation, interruption, and duplicate terminal ownership fail closed.
|
||||
- Tool calls bind to the immutable run catalog. Credential values resolve only
|
||||
inside a one-shot downstream MCP connection and credential-bearing results
|
||||
are rejected.
|
||||
- Protocol frames, stdout, stderr, retained payloads, retries, and timeouts are
|
||||
bounded and redacted before durable storage.
|
||||
- Required network rules block when runtime evidence cannot enforce them.
|
||||
Fine-grained method/path/domain proxy enforcement remains deferred to
|
||||
[run-scoped egress gateway](https://github.com/BradGroux/veritas-kanban/issues/855).
|
||||
Rollback is restore-first. Stop every writer. Reinstall 6.1.1 only when the current data contracts remain compatible; otherwise restore the complete pre-upgrade stopped-writer workspace. Never copy an older database over a live instance.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Grok Build's released artifact is not fully traceable to the public source
|
||||
tree and self-reports alpha.
|
||||
- GitHub Copilot CLI ACP is public preview. Its public tag, commit message, and
|
||||
runtime version do not provide complete binary-to-source provenance.
|
||||
- Claude Code's complete CLI implementation is not public. Certification is
|
||||
bound to exact release behavior and checked-in stream fixtures.
|
||||
- Buzz Agent sessions are in-memory and do not support ACP session load/resume.
|
||||
Buzz files, reactions, forums, DMs, and destructive edit/delete projection
|
||||
are not bridged.
|
||||
- Hermes and OpenClaw do not gain unsupported interactive lifecycle controls.
|
||||
- Linux and Windows desktop artifacts are unsigned preview evidence. Signed,
|
||||
notarized macOS arm64 remains the supported desktop distribution.
|
||||
- A deterministic passing fixture does not prove provider authentication,
|
||||
subscription availability, quota, or live inference. Live smoke results are
|
||||
reported separately and never fabricated.
|
||||
Buzz Agent sessions remain in-memory and do not support session load/resume. Buzz files, reactions, forums, direct messages, and destructive edit/delete projection are not bridged. GitHub Copilot CLI ACP remains public preview. Grok Build's stable artifact still self-reports alpha and cannot be fully traced to the current public source tree. Claude Code's complete CLI implementation is not public, so certification remains bound to exact release behavior and checked-in fixtures.
|
||||
|
||||
Deterministic compatibility does not prove provider authentication, subscription availability, quota, or live inference. Linux and Windows desktop artifacts remain unsigned previews; signed and notarized macOS arm64 is the supported stable desktop distribution.
|
||||
|
||||
## Release Artifacts
|
||||
|
||||
The supported v6.0.0 publication set is:
|
||||
The supported stable desktop release provides signed and notarized `Veritas-Kanban-6.1.2-mac-arm64.dmg` and `Veritas-Kanban-6.1.2-mac-arm64.zip`, blockmaps, SHA-256 sidecars, and `latest-mac.yml` updater metadata from the annotated `v6.1.2` tag. Exact sizes, hashes, signing, notarization, stapling, Gatekeeper, launch, updater, workflow, release, and Homebrew evidence are recorded in [v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md).
|
||||
|
||||
- annotated source tag and GitHub release `v6.0.0`;
|
||||
- signed and notarized `Veritas-Kanban-6.0.0-mac-arm64.dmg`;
|
||||
- signed and notarized `Veritas-Kanban-6.0.0-mac-arm64.zip`;
|
||||
- DMG and ZIP blockmaps;
|
||||
- `latest-mac.yml`;
|
||||
- SHA-256 sidecars; and
|
||||
- Homebrew cask `bradgroux/tap/veritas-kanban`.
|
||||
## Documentation And Evidence
|
||||
|
||||
Final asset sizes, hashes, workflow URL, release URL, signed-runtime proof, and
|
||||
Homebrew PR are written to the
|
||||
[v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md) after
|
||||
publication. Source preparation does not count as signed distribution proof.
|
||||
|
||||
## Deferred v6.x Work
|
||||
|
||||
- [Run-scoped egress gateway](https://github.com/BradGroux/veritas-kanban/issues/855)
|
||||
remains a v6.x enhancement for fine-grained outbound HTTP enforcement.
|
||||
v6.0.0 already blocks any required rule that the selected provider cannot
|
||||
prove it enforces.
|
||||
- [Agent provider setup and operations](AGENT-PROVIDERS.md)
|
||||
- [Harness compatibility matrix](HARNESS-COMPATIBILITY.md)
|
||||
- [v6 runtime architecture](architecture/V6-AGENT-RUNTIME-CONTROL-PLANE.md)
|
||||
- [v6 compatibility and release policy](V6-COMPATIBILITY-AND-RELEASE-POLICY.md)
|
||||
- [v6 upgrade and administration guide](V6-UPGRADE-INSTALL-ADMIN-GUIDE.md)
|
||||
- [v6 release candidate evidence](V6-RC-EVIDENCE-PACKET.md)
|
||||
- [Changelog](../CHANGELOG.md)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
# Veritas Kanban v6 Upgrade, Install, Remote, And Admin Guide
|
||||
|
||||
This is the release-facing operator guide for Veritas Kanban 6.0.0. The
|
||||
This is the release-facing operator guide for Veritas Kanban 6.1.2. The
|
||||
detailed provider commands live in [Agent Providers](AGENT-PROVIDERS.md), the
|
||||
machine-readable support contract is summarized in
|
||||
[Harness Compatibility](HARNESS-COMPATIBILITY.md), and Buzz relay setup lives
|
||||
in [Buzz Integration](BUZZ-INTEGRATION.md).
|
||||
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
||||
Documentation freshness: 2026-08-24 for Veritas Kanban 6.1.2.
|
||||
|
||||
Do not install 6.0.0. It is retained as a quarantined prerelease. Version 6.1.2
|
||||
is the supported stable v6 release and supersedes 6.1.1.
|
||||
|
||||
## Fresh Mac Desktop Install
|
||||
|
||||
|
|
@ -18,8 +21,8 @@ brew install --cask veritas-kanban
|
|||
```
|
||||
|
||||
Manual installation uses
|
||||
`Veritas-Kanban-6.0.0-mac-arm64.zip` from the
|
||||
[v6.0.0 GitHub release](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.0).
|
||||
`Veritas-Kanban-6.1.2-mac-arm64.zip` from the
|
||||
[v6.1.2 GitHub release](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.1.2).
|
||||
Move `Veritas Kanban.app` into `/Applications`, launch it normally, and verify
|
||||
Settings -> Maintenance before enabling an agent or external integration.
|
||||
|
||||
|
|
@ -27,7 +30,7 @@ For a new board:
|
|||
|
||||
1. Choose Board Only unless agent execution is required immediately.
|
||||
2. Create the local admin password and retain the recovery key securely.
|
||||
3. Confirm `/api/health` reports version 6.0.0.
|
||||
3. Confirm `/api/health` reports version 6.1.2.
|
||||
4. Create a governed backup before adding external credentials or relay
|
||||
mappings.
|
||||
|
||||
|
|
@ -55,14 +58,14 @@ equivalent v5.2.5 self-hosted workspace.
|
|||
preferred port are stopped before copying data.
|
||||
5. Preserve the complete workspace, not only the SQLite file. Keep the backup
|
||||
through release acceptance.
|
||||
6. Install v6.0.0 without replacing the workspace.
|
||||
6. Install v6.1.2 without replacing the workspace.
|
||||
7. Launch with the same profile. If setup appears for a populated database,
|
||||
choose **Use Existing Data**. Do not rerun file migration or restore over the
|
||||
populated database.
|
||||
8. Wait for the exact-version readiness gate:
|
||||
|
||||
```bash
|
||||
EXPECTED_VERSION=6.0.0
|
||||
EXPECTED_VERSION=6.1.2
|
||||
pnpm desktop:wait:ready -- --expected-version "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
|
|
@ -79,6 +82,14 @@ The public API remains `v1`. v6 adds provider, approval, lifecycle, tool,
|
|||
credential, compatibility, Buzz, and conformance records without requiring a
|
||||
new API mount.
|
||||
|
||||
Veritas Kanban 6.1.2 retains the SQLite workspace migrations 30 to 33 from
|
||||
6.1.0. No new schema migration runs when upgrading from 6.1.1. Runtime-path
|
||||
normalization can move legacy files into the configured canonical data root.
|
||||
Keep the stopped-writer
|
||||
backup until collection, task, workflow, provider, and board data have been
|
||||
accepted. Rollback to an older schema requires restoring that backup; do not
|
||||
open migrated data with an older binary.
|
||||
|
||||
### Legacy provider profile normalization
|
||||
|
||||
- Known built-in Codex, Hermes, and Claude records migrate only when both type
|
||||
|
|
@ -220,3 +231,9 @@ runtime-manifest, support-profile, compatibility-matrix, launch-manifest,
|
|||
approval, run-event, and completion evidence. Never paste raw private relay
|
||||
events, provider output, credentials, or unrestricted support bundles into a
|
||||
public issue.
|
||||
|
||||
In the packaged macOS app, choose **Veritas Kanban → About Veritas Kanban** for
|
||||
the authoritative running version. **Copy Version Information** in the same
|
||||
native menu produces a redacted offline support string with the build identity,
|
||||
release channel, macOS version, and architecture even when the renderer or local
|
||||
API is unavailable.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ integration, approvals, and run evidence. The general board, desktop shell,
|
|||
Workbench, Squad Chat, Maintenance, and mobile/PWA layouts remain represented
|
||||
by the retained [v5 Visual Tour](V5-VISUAL-TOUR.md) and its dummy-data assets.
|
||||
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.2. The retained
|
||||
screenshots cover the unchanged v6 provider and Buzz surfaces; 6.0.2 Chat
|
||||
recovery and native version information are documented in the release notes
|
||||
and evidence packet.
|
||||
|
||||
## Provider Support
|
||||
|
||||
|
|
|
|||
|
|
@ -142,8 +142,20 @@ curl -X POST http://localhost:3001/api/workflows/hello-world/runs \
|
|||
|
||||
1. Open Veritas Kanban in your browser
|
||||
2. Navigate to **Workflows** tab (header navigation)
|
||||
3. Click on "Hello World Workflow"
|
||||
4. You'll see your active run with real-time step progress
|
||||
3. Select the workflow name or **View details** to inspect its definition before execution
|
||||
4. Select **Start Run**, review the optional task association and JSON run context, then confirm the run
|
||||
5. Open **View workflow runs** to see real-time step progress
|
||||
|
||||
### Browse, Edit, and Duplicate Workflows
|
||||
|
||||
The workflow browser separates inspection, authoring, and execution:
|
||||
|
||||
- A workflow name or **View details** opens a deep-linkable read-only definition at `/workflows/:id`. The definition shows provenance, variables, agents, ordered steps, phases, gate conditions, loop controls, parallel branches, step inputs, acceptance criteria, and outputs.
|
||||
- User-owned workflows with edit permission expose **Edit** and save through the Author builder at `/workflows/:id/edit`. The workflow ID remains fixed and the loaded version is sent with the update, so a stale save fails with a conflict instead of overwriting a newer definition. The draft remains in the editor after validation, permission, or conflict errors.
|
||||
- Built-in and shared read-only workflows explain why they cannot be edited. Identities with workflow write permission can choose **Duplicate to customize**, select a new ID and name in Author, and save an independent workflow.
|
||||
- **Start Run** always opens a separate configuration dialog. An optional task ID associates the run with an existing task, while the JSON object supplies initial workflow context.
|
||||
|
||||
Browser Back returns from edit or duplicate to the source definition and from the definition to the workflow browser. Direct links use the same safe fallback instead of leaving the application.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -1205,7 +1217,19 @@ If the server crashes mid-workflow, runs can be recovered:
|
|||
curl -X POST http://localhost:3001/api/workflow-runs/run_XYZ/resume
|
||||
```
|
||||
|
||||
> **📝 Note**: Automatic recovery is planned for a future release.
|
||||
Automatic transient-failure recovery is persisted on the step as
|
||||
`runRetry`. Scheduled retry and fallback timers are restored after server
|
||||
restart. Cancel an exact pending workflow recovery with:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3001/api/workflows/runs/run_XYZ/recovery/cancel \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"stepId":"implement","parentRunId":"run_XYZ:implement:0"}'
|
||||
```
|
||||
|
||||
Only explicitly transient failures retry automatically. Policy and
|
||||
configuration failures do not retry, while fallback agents must pass the same
|
||||
runtime capability and sandbox preflight as a normal workflow launch.
|
||||
|
||||
### Performance Issues
|
||||
|
||||
|
|
|
|||
|
|
@ -2450,113 +2450,35 @@ async function getDirSize(dirPath: string): Promise<number> {
|
|||
}
|
||||
```
|
||||
|
||||
**Concurrency Limits**:
|
||||
**Concurrency and Capacity Admission**:
|
||||
|
||||
```typescript
|
||||
// server/src/config/workflow-config.ts
|
||||
Workflow concurrency is not authorized by process-local sets, counters, or
|
||||
queues. `WorkflowRunService` obtains an atomic
|
||||
`admission-reservation/v1` root claim before a run becomes active. The root
|
||||
uses admission provider `workflow-control` and binds its workspace, root task,
|
||||
run ID, host, and lease.
|
||||
|
||||
export const WORKFLOW_CONCURRENCY_CONFIG = {
|
||||
// Maximum concurrent workflow runs globally
|
||||
maxConcurrentRuns: 5,
|
||||
Every provider-backed step resolves its runtime and host first, then obtains a
|
||||
child reservation before mutating the step attempt or dispatching the adapter.
|
||||
Retry and fallback attempts release the prior child reservation before
|
||||
claiming another. Global, workspace, root-task, provider, and host limits use
|
||||
the same durable policies as direct task launches.
|
||||
|
||||
// Maximum concurrent runs per workflow
|
||||
maxConcurrentRunsPerWorkflow: 2,
|
||||
The root and every executable step also persist `execution-tree-identity/v1`.
|
||||
Step, retry, and fallback edges retain the workflow root objective and exact
|
||||
parent node. The admission repository evaluates capacity and the aggregate
|
||||
workflow/root budget in the same file lock or SQLite `BEGIN IMMEDIATE`
|
||||
transaction. Each step commits only its own provider-reported usage; cumulative
|
||||
workflow totals are not copied into child records. This keeps deep, wide, and
|
||||
replayed workflows attributable without descendant double counting.
|
||||
|
||||
// Maximum concurrent steps per workflow run (for parallel steps)
|
||||
maxConcurrentStepsPerRun: 3,
|
||||
|
||||
// Queue size (pending runs waiting for a slot)
|
||||
maxQueueSize: 20,
|
||||
};
|
||||
```
|
||||
|
||||
**Concurrency Enforcement**:
|
||||
|
||||
```typescript
|
||||
// server/src/services/workflow-run-service.ts (updated)
|
||||
|
||||
import { WORKFLOW_CONCURRENCY_CONFIG } from '../config/workflow-config.js';
|
||||
|
||||
export class WorkflowRunService {
|
||||
private activeRuns: Set<string> = new Set(); // Run IDs currently executing
|
||||
private runQueue: Array<{ runId: string; workflowId: string }> = [];
|
||||
|
||||
async startRun(
|
||||
workflowId: string,
|
||||
taskId?: string,
|
||||
initialContext?: Record<string, any>
|
||||
): Promise<WorkflowRun> {
|
||||
// Check global concurrency limit
|
||||
if (this.activeRuns.size >= WORKFLOW_CONCURRENCY_CONFIG.maxConcurrentRuns) {
|
||||
// Queue the run
|
||||
if (this.runQueue.length >= WORKFLOW_CONCURRENCY_CONFIG.maxQueueSize) {
|
||||
throw new Error('Workflow run queue is full — try again later');
|
||||
}
|
||||
|
||||
const run = await this.createRun(workflowId, taskId, initialContext);
|
||||
run.status = 'pending';
|
||||
await this.saveRun(run);
|
||||
|
||||
this.runQueue.push({ runId: run.id, workflowId });
|
||||
|
||||
log.info({ runId: run.id, queuePosition: this.runQueue.length }, 'Run queued');
|
||||
return run;
|
||||
}
|
||||
|
||||
// Check per-workflow concurrency limit
|
||||
let activeRunsForWorkflow = 0;
|
||||
for (const runId of this.activeRuns) {
|
||||
const existingRun = await this.getRun(runId);
|
||||
if (existingRun?.workflowId === workflowId) {
|
||||
activeRunsForWorkflow++;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeRunsForWorkflow >= WORKFLOW_CONCURRENCY_CONFIG.maxConcurrentRunsPerWorkflow) {
|
||||
throw new Error(`Workflow ${workflowId} has too many active runs`);
|
||||
}
|
||||
|
||||
// Start the run
|
||||
const run = await this.createRun(workflowId, taskId, initialContext);
|
||||
this.activeRuns.add(run.id);
|
||||
|
||||
// Execute (async)
|
||||
this.executeRun(run, workflow).finally(() => {
|
||||
this.activeRuns.delete(run.id);
|
||||
this.processQueue(); // Start next queued run
|
||||
});
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
private async processQueue(): Promise<void> {
|
||||
if (this.runQueue.length === 0) return;
|
||||
if (this.activeRuns.size >= WORKFLOW_CONCURRENCY_CONFIG.maxConcurrentRuns) return;
|
||||
|
||||
const { runId, workflowId } = this.runQueue.shift()!;
|
||||
const run = await this.getRun(runId);
|
||||
if (!run) return;
|
||||
|
||||
// Update status and start execution
|
||||
run.status = 'running';
|
||||
await this.saveRun(run);
|
||||
broadcastWorkflowStatus(run);
|
||||
|
||||
this.activeRuns.add(run.id);
|
||||
|
||||
const workflow = await this.workflowService.loadWorkflow(workflowId);
|
||||
if (!workflow) {
|
||||
log.error({ runId, workflowId }, 'Workflow not found for queued run');
|
||||
return;
|
||||
}
|
||||
|
||||
this.executeRun(run, workflow).finally(() => {
|
||||
this.activeRuns.delete(run.id);
|
||||
this.processQueue();
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
Temporary root or step overload persists one bounded FIFO admission entry and
|
||||
leaves the run or step visibly waiting without provider dispatch. Claims
|
||||
revalidate workflow, run, provider, host, phase, budget, and execution-tree
|
||||
evidence before transferring ownership to workflow recovery. Retry and
|
||||
fallback replacements cannot queue until predecessor capacity is released. An
|
||||
impossible request or stale queue authority fails closed. Priority aging and
|
||||
cross-workspace fairness remain later scheduler concerns.
|
||||
|
||||
**OpenClaw Session Cleanup**:
|
||||
|
||||
|
|
|
|||
173
docs/architecture/FILESYSTEM-SANDBOX-BACKENDS.md
Normal file
173
docs/architecture/FILESYSTEM-SANDBOX-BACKENDS.md
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
# Run-scoped filesystem sandbox backends
|
||||
|
||||
This document defines the filesystem enforcement contract for local Veritas
|
||||
Kanban agent runs. It is the implementation contract for
|
||||
[issue #862](https://github.com/BradGroux/veritas-kanban/issues/862).
|
||||
|
||||
Documentation freshness: 2026-07-25.
|
||||
|
||||
## Decision
|
||||
|
||||
Veritas compiles every required filesystem policy before attempt state is
|
||||
mutated. Local process adapters run behind one version-bound sandbox wrapper.
|
||||
The first supported wrapper is the public `codex sandbox` command introduced
|
||||
in Codex CLI 0.145:
|
||||
|
||||
- macOS uses Seatbelt through `/usr/bin/sandbox-exec`;
|
||||
- Linux uses the Codex Linux helper with Landlock and bubblewrap where
|
||||
available; and
|
||||
- Windows uses the Codex restricted-token and filesystem capability backend.
|
||||
|
||||
Veritas does not shell-wrap providers. It invokes the wrapper with an exact
|
||||
`SandboxState` JSON document followed by the provider command and arguments.
|
||||
The wrapper remains the supervised process, so its provider descendants share
|
||||
the same process group and filesystem boundary.
|
||||
|
||||
An unavailable or non-conformant wrapper does not silently weaken a required
|
||||
policy. A provider-native sandbox can satisfy the contract only when its
|
||||
versioned runtime manifest reports every required filesystem capability as
|
||||
supported. The active read, write, deny, dotfile, descendant-process,
|
||||
run-scoped temporary-directory, and cleanup requirements must all be covered.
|
||||
Coarse provider modes such as `workspace-write` remain advisory until their
|
||||
exact-root semantics have version-bound conformance evidence.
|
||||
|
||||
Remote providers such as OpenClaw cannot use a host-local wrapper. They must
|
||||
provide equivalent version-bound native evidence or the required launch is
|
||||
blocked.
|
||||
|
||||
## Compiled policy
|
||||
|
||||
The filesystem compiler resolves these preset fields:
|
||||
|
||||
| Preset field | Compiled behavior |
|
||||
| ------------------ | ---------------------------------------------------------------------- |
|
||||
| `readPaths` | Read-only entries after absolute and symlink-aware resolution |
|
||||
| `writePaths` | Writable entries after absolute and symlink-aware resolution |
|
||||
| `deniedPaths` | Deny entries with precedence over ancestor read or write grants |
|
||||
| `dotfileMasking` | Deny globs for dotfiles below every configured root |
|
||||
| `localOnlyHandles` | Blocks remote execution unless equivalent local-handle evidence exists |
|
||||
|
||||
`<workspace>` resolves to the canonical task worktree. `~` resolves to the
|
||||
operator home directory. Other entries must be absolute. Missing leaf paths
|
||||
are resolved through their nearest existing canonical ancestor; an
|
||||
unresolvable or ambiguous path fails closed.
|
||||
|
||||
Workspace-relative and home-relative aliases must remain below their
|
||||
canonical base, so `..` traversal and symlinks cannot turn a scoped grant into
|
||||
an external grant. Existing mount points below an allowed root are denied
|
||||
unless that exact mount is explicitly granted or already covered by a deny
|
||||
rule. Veritas rechecks the relevant mount topology immediately before
|
||||
activation; a changed or uninspectable topology blocks launch. Provider-native
|
||||
enforcement blocks on an ambiguous local nested mount because Veritas cannot
|
||||
amend the provider's native boundary.
|
||||
|
||||
Before policy compilation and again immediately before activation, Veritas
|
||||
scans the bounded workspace tree for pre-existing hard links that alias an
|
||||
external denied or non-readable inode. It does not follow symlinks during the
|
||||
scan. An external alias, an unbounded tree, or an inspection failure blocks the
|
||||
launch instead of relying on the native backend to distinguish two paths to
|
||||
the same inode.
|
||||
|
||||
Required policies start from Codex's `:minimal` platform-runtime read set and
|
||||
then add only configured roots. The wrapper, provider executable package, and
|
||||
canonical PATH directories are recorded as read-only `platform-runtime`
|
||||
entries so the selected harness and normal task tools can execute without
|
||||
granting a general home-directory read.
|
||||
Node package roots and Python virtual-environment roots are resolved from the
|
||||
selected CLI launcher and added narrowly. Linked Git worktrees add only their
|
||||
canonical worktree and common metadata directories as protected read-only
|
||||
roots. Ambient system and global Git configuration is disabled inside the
|
||||
boundary; Veritas carries only the effective author name and email in the
|
||||
in-memory launch environment when they are available. Those values are not
|
||||
stored in launch evidence or logs.
|
||||
The legacy advisory preset retains its documented compatibility behavior and
|
||||
is recorded as advisory evidence rather than being represented as a required
|
||||
boundary.
|
||||
|
||||
Local provider-native and wrapper-backed runs receive dedicated temporary and
|
||||
cache directories. They are added as writable roots, supplied through
|
||||
`TMPDIR`, `TMP`, `TEMP`, and `XDG_CACHE_HOME`, and bound to the durable run
|
||||
supervisor for terminal cleanup. A remote provider-native backend must instead
|
||||
prove both run-scoped temporary storage and cleanup ownership in its exact
|
||||
runtime manifest.
|
||||
|
||||
Codex protects `.git`, `.agents`, `.codex`, and `.veritas-kanban` metadata
|
||||
directly beneath configured writable roots. Veritas records those protected
|
||||
names in the launch evidence and emits explicit read-only entries so the root
|
||||
write grant cannot make them writable. A policy cannot select a protected
|
||||
metadata path itself as a writable root, and a protected path that is or
|
||||
becomes a symlink blocks activation. Dotfile masking is stronger and denies
|
||||
reads as well as writes.
|
||||
|
||||
## Immutable evidence
|
||||
|
||||
`run-launch-manifest/v1` records a
|
||||
`filesystem-sandbox-evidence/v1` object containing:
|
||||
|
||||
- selected backend and operating-system implementation;
|
||||
- backend version, capability contract version, and executable-content digest;
|
||||
- the exact provider runtime manifest digest used for the launch decision;
|
||||
- policy hash;
|
||||
- read, write, deny, run-temporary, run-cache, and protected-root references;
|
||||
- hashes of canonical paths instead of private path contents;
|
||||
- dotfile and descendant-process enforcement state; and
|
||||
- cleanup ownership by the run supervisor.
|
||||
|
||||
The provider runtime manifest remains the evidence authority for
|
||||
provider-native enforcement. The filesystem evidence links to that manifest
|
||||
instead of duplicating unredacted provider configuration.
|
||||
|
||||
## Conformance and invalidation
|
||||
|
||||
The wrapper probe is credential-free. It checks:
|
||||
|
||||
1. exact CLI version and required `codex sandbox` flags;
|
||||
2. allowed reads and writes;
|
||||
3. denied reads and writes;
|
||||
4. symlink and hard-link escape resistance;
|
||||
5. mount-boundary compilation and pre-spawn topology drift;
|
||||
6. descendant-process inheritance;
|
||||
7. dotfile masking;
|
||||
8. protected metadata write denial;
|
||||
9. PATH tool execution; and
|
||||
10. re-execution of the selected sandbox backend inside its own boundary.
|
||||
|
||||
Probe results are cached only for the current executable byte digest, version,
|
||||
platform, and Veritas probe revision. Veritas rehashes the selected executable
|
||||
after policy evaluation and immediately before activation. A replacement,
|
||||
including a same-size binary with restored timestamps, blocks launch and must
|
||||
re-run conformance before it can satisfy a required policy.
|
||||
|
||||
Platform CI runs deterministic compiler and launch-contract tests everywhere.
|
||||
Credential-free backend smoke tests run only when the matching native backend
|
||||
is available.
|
||||
|
||||
## Failure and cleanup contract
|
||||
|
||||
Policy compilation and backend conformance happen before attempt persistence.
|
||||
Run directory activation happens after the immutable launch manifest and
|
||||
supervisor binding exist, but before provider spawn.
|
||||
|
||||
If launch fails before supervisor registration, Veritas removes the
|
||||
task-owned run directory directly. After registration, terminal cleanup is a
|
||||
durable supervisor responsibility. Cleanup state is persisted so interrupted
|
||||
or failed removal can be retried without guessing which directory belongs to
|
||||
the run. Cleanup canonicalizes the sandbox base and rejects a symlinked or
|
||||
non-directory ancestor before recursive removal.
|
||||
|
||||
Veritas exposes no per-run bypass for a required filesystem boundary.
|
||||
`overrideReason` applies only to task-readiness checks and cannot weaken a
|
||||
sandbox decision. An operator who intentionally wants advisory enforcement
|
||||
must use the separately authorized sandbox-policy API to select or maintain an
|
||||
advisory preset. Policy evaluation and launch then record the resulting
|
||||
decision in governance evidence linked to the launch manifest.
|
||||
|
||||
## Primary sources
|
||||
|
||||
- [Codex CLI sandbox command at 0.145.0](https://github.com/openai/codex/blob/25af12f7e61572b0bc18ddb1008be543b91519b0/codex-rs/cli/src/debug_sandbox.rs)
|
||||
- [Codex permission profile model at 0.145.0](https://github.com/openai/codex/blob/25af12f7e61572b0bc18ddb1008be543b91519b0/codex-rs/protocol/src/models.rs)
|
||||
- [Codex filesystem policy and protected metadata rules at 0.145.0](https://github.com/openai/codex/blob/25af12f7e61572b0bc18ddb1008be543b91519b0/codex-rs/protocol/src/permissions.rs)
|
||||
- [Linux Landlock userspace API](https://docs.kernel.org/userspace-api/landlock.html)
|
||||
- [Bubblewrap sandboxing model](https://github.com/containers/bubblewrap/blob/main/README.md)
|
||||
- [Windows `CreateRestrictedToken`](https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-createrestrictedtoken)
|
||||
- [Windows restricted tokens](https://learn.microsoft.com/en-us/windows/win32/secauthz/restricted-tokens)
|
||||
181
docs/architecture/KNOWLEDGE-COLLECTIONS-V1.md
Normal file
181
docs/architecture/KNOWLEDGE-COLLECTIONS-V1.md
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
# Knowledge Collections v1
|
||||
|
||||
`knowledge-collection/v1` is the workspace-scoped foundation for source-grounded project knowledge. It separates immutable source evidence from the derived pages, ingestion proposals, and query answers that will consume that evidence.
|
||||
|
||||
## Collection contract
|
||||
|
||||
Every collection stores:
|
||||
|
||||
- a stable operation-derived ID inside one authenticated workspace;
|
||||
- a versioned `knowledge-collection-definition/v1` schema covering page kinds, required metadata, stable naming, bidirectional links, review-required ingestion, and bounded page history;
|
||||
- an access policy with explicit read and write roles, a maximum source classification, and an export posture; and
|
||||
- content digests, actor attribution, timestamps, and a caller operation ID stored only as a digest.
|
||||
|
||||
Administrators always retain read and write access. Every configured writer must also be a reader. The creator must have write access under the new policy, preventing a non-admin caller from creating a collection it cannot subsequently maintain.
|
||||
|
||||
Creating a collection is idempotent for the same operation and exact request. Reusing that operation identity with changed input, or creating the same slug through another operation in the workspace, returns conflict.
|
||||
|
||||
## Immutable source catalog
|
||||
|
||||
`knowledge-source/v1` supports two storage modes:
|
||||
|
||||
- `content-addressed-blob` stores a bounded UTF-8 snapshot whose SHA-256 digest and byte count are computed by the server.
|
||||
- `content-addressed-reference` records an externally retained source by caller-supplied SHA-256 digest and byte count without copying the content.
|
||||
|
||||
A source key identifies the logical source. Each new registration creates the next immutable revision and points to the source revision it supersedes. A retry with the same operation and exact request returns the original revision; changed input under the same operation fails closed.
|
||||
|
||||
Inline blobs are verified before persistence and whenever file-backed content is read. The file repository rejects malformed base64, digest mismatches, unsafe store paths, oversized stores, excessive inventories, and revision races. SQLite applies the same metadata and revision contract inside a transaction.
|
||||
|
||||
Source classifications are `public`, `internal`, `confidential`, and `restricted`. Registration cannot exceed the collection policy ceiling. Collection reads and source access require a role listed by the collection, in addition to the route-level `work_product:read` or `work_product:write` permission. Cross-workspace lookup does not reveal whether an object exists.
|
||||
|
||||
Agent access is also bound to one persisted run launch manifest. Agent requests send
|
||||
`x-veritas-task-id`, `x-veritas-attempt-id`, and
|
||||
`x-veritas-launch-manifest-digest`; partial or malformed evidence is rejected.
|
||||
The server loads the exact attempt manifest and accepts access only when the
|
||||
task, attempt, digest, resource enforcement, and blocker state still match.
|
||||
`knowledge:*` allows the complete knowledge surface. Otherwise, a manifest must
|
||||
name an exact source ID, source key, source URI, page ID, or page stable key.
|
||||
|
||||
## Derived pages and citations
|
||||
|
||||
`knowledge-page/v1` stores Markdown-compatible synthesis separately from immutable source evidence. Each page has one canonical stable key, durable aliases, a typed page kind, tags, collection-required metadata, review state, confidence, outgoing page IDs, computed backlinks, and a bounded revision history.
|
||||
|
||||
Every material claim carries a stable page-local claim ID and one or more citations to immutable source revision IDs. Citations can include a line range, heading occurrence, JSON pointer, excerpt hash, time range, or an additional excerpt digest. Unknown source revisions fail closed before persistence.
|
||||
|
||||
A multi-page update resolves stable keys and aliases against the complete collection. Alias matches revise the existing page and preserve its canonical identity rather than creating a duplicate. Links can target canonical keys, aliases, or page IDs, including other pages in the same batch. The service recomputes all affected backlinks and commits every changed page through one compare-and-set repository batch. File storage uses one locked atomic replacement; SQLite uses one immediate transaction. A stale page digest aborts the whole batch.
|
||||
|
||||
Page revisions retain content hashes, claims, links, backlinks, review evidence, actor attribution, request identity, and operation identity. History is capped by the collection's `maxPageVersions` policy. Agents can prepare draft or review-required synthesis, while only administrators can mark a page approved or rejected.
|
||||
|
||||
## Reviewed ingestion transactions
|
||||
|
||||
`knowledge-ingestion-proposal/v1` is a durable dry run. Creating one resolves the exact source revisions and page candidates but does not mutate the page graph. The proposal records full before and after page snapshots, compare-and-set page digests, page create/revise/backlink changes, index upserts, source selection, extractor-supplied contradictions, stable-claim contradictions, and the activity entry that application will append.
|
||||
|
||||
Stable claim keys are compared across revisions. Changed text backed by a different source set produces a warning contradiction automatically. Extractors can add informational, warning, or blocking contradictions. Blocking contradictions cannot be applied; the operator must create a replacement proposal that resolves them.
|
||||
|
||||
Only administrators can apply or reverse a proposal. Apply requires the exact dry-run proposal digest and every expected page digest. File mode changes the page graph, proposal state, and append-only activity in one locked atomic file replacement. SQLite performs the same changes in one immediate transaction. A crash or stale page therefore cannot leave pages applied without their proposal and activity evidence.
|
||||
|
||||
Apply and reverse transitions are versioned, actor-attributed, timestamped, and bound to the complete immutable preview digest. Exact retries return the already committed state. Reverse requires every affected page to still match the proposal's applied snapshot, restores complete prior page records, removes pages created by the proposal, recomputes graph validity, and appends a separate reversal activity entry. Later edits or new links that make reversal unsafe return conflict without partial mutation.
|
||||
|
||||
## Storage and API
|
||||
|
||||
The file backend keeps collection metadata, source revisions, derived pages, ingestion proposals, activity, integrity findings, and deduplicated blobs in one lock-protected, atomically replaced `knowledge-collections.json`. SQLite migrations 30 through 33 add unique workspace, slug, source revision, operation, page identity, stable-key, proposal, activity, and integrity-finding constraints. Both implement the same repository interface.
|
||||
|
||||
The initial REST surface is mounted at both `/api/v1/knowledge/collections` and `/api/knowledge/collections`:
|
||||
|
||||
| Method | Path | Purpose |
|
||||
| ------ | -------------------------------------------------------------------------------- | --------------------------- |
|
||||
| `GET` | `/knowledge/collections` | List readable collections |
|
||||
| `POST` | `/knowledge/collections` | Create a collection |
|
||||
| `GET` | `/knowledge/collections/:collectionId` | Read collection metadata |
|
||||
| `GET` | `/knowledge/collections/:collectionId/sources` | List source revisions |
|
||||
| `POST` | `/knowledge/collections/:collectionId/sources` | Register a source revision |
|
||||
| `GET` | `/knowledge/collections/:collectionId/sources/:sourceId` | Read source metadata |
|
||||
| `GET` | `/knowledge/collections/:collectionId/pages` | List derived pages |
|
||||
| `GET` | `/knowledge/collections/:collectionId/pages/:pageId` | Read a derived page |
|
||||
| `POST` | `/knowledge/collections/:collectionId/pages/:pageId/claims/:claimId/transitions` | Transition a cited claim |
|
||||
| `POST` | `/knowledge/collections/:collectionId/integrity/lint` | Run deterministic lint |
|
||||
| `GET` | `/knowledge/collections/:collectionId/integrity/findings` | List durable findings |
|
||||
| `POST` | `/knowledge/collections/:collectionId/integrity/findings/:findingId/transitions` | Transition a finding |
|
||||
| `GET` | `/knowledge/collections/:collectionId/integrity/health` | Read integrity health |
|
||||
| `POST` | `/knowledge/collections/:collectionId/search` | Search raw and derived data |
|
||||
| `POST` | `/knowledge/collections/:collectionId/search/promotions` | Promote selected results |
|
||||
| `POST` | `/knowledge/collections/:collectionId/exports` | Create a cited work product |
|
||||
| `GET` | `/knowledge/collections/:collectionId/ingestion/proposals` | List dry runs |
|
||||
| `POST` | `/knowledge/collections/:collectionId/ingestion/proposals` | Create a dry run |
|
||||
| `GET` | `/knowledge/collections/:collectionId/ingestion/proposals/:proposalId` | Read a dry run |
|
||||
| `POST` | `/knowledge/collections/:collectionId/ingestion/proposals/:proposalId/apply` | Apply atomically |
|
||||
| `POST` | `/knowledge/collections/:collectionId/ingestion/proposals/:proposalId/reverse` | Reverse atomically |
|
||||
| `GET` | `/knowledge/collections/:collectionId/activity` | List append-only activity |
|
||||
|
||||
The REST API deliberately returns source metadata, not stored source content. Collection-scoped search reads snapshots internally through the repository, distinguishes `raw-source` from `derived-page` results, and returns source IDs and claim locators instead of uncited synthesis. Collection and workspace RBAC apply before any search data is read.
|
||||
|
||||
## Cited search
|
||||
|
||||
Bounded keyword search spans immutable raw snapshots and current derived pages. Callers can search both layers or restrict the request to one. Raw hits cite their immutable source revision. Derived hits retain the deduplicated claim citations stored on the page, so consumers can distinguish evidence from synthesis and follow each material claim back to its registered source.
|
||||
|
||||
For `auto` or `qmd`, current derived page digests produce a Markdown projection under the runtime directory. Each workspace collection and launch-manifest scope receives a hashed QMD collection name inside an isolated `QMD_CONFIG_DIR` and `INDEX_PATH`; the adapter registers it with the documented `collection add --mask "**/*.md"` command, marks it excluded from unscoped QMD queries, refreshes only when projection digests change, and queries it with an exact `-c` filter. QMD paths are accepted only when they map back to an eligible page ID, and citations come from the authoritative page record rather than QMD output. Set `VERITAS_QMD_KNOWLEDGE_EMBED=true` to refresh embeddings for changed projections.
|
||||
|
||||
The response follows the existing search degradation contract. QMD-ranked derived hits identify `backend: "qmd"` while raw-source hits identify `backend: "keyword"`. Missing QMD, refresh/query failure, or a raw-only scope returns cited keyword results with `degraded: true` and an explicit reason. Every hit also carries its effective classification. Confidential and restricted snippets are withheld from previews rather than returned to the caller.
|
||||
|
||||
## Deterministic integrity lint
|
||||
|
||||
`POST /knowledge/collections/:collectionId/integrity/lint` inspects only the
|
||||
sources and pages readable inside the caller's role and run launch manifest. It
|
||||
detects broken links, backlink drift, orphan pages, duplicate identities,
|
||||
invalid page kinds, missing required metadata, uncited claims, inaccessible
|
||||
sources, changed retained-source hashes, invalid citation locators, stale pages
|
||||
and sources, repeated terms without canonical pages, and unanswered questions.
|
||||
|
||||
Callers can supply an exact `asOf` timestamp and freshness rules for page kinds
|
||||
or source media types. Findings have stable IDs and digests, contain identifiers
|
||||
rather than source text, and are deterministically ordered. Repeating an
|
||||
unchanged request over unchanged inputs returns the same report digest.
|
||||
Research candidates are opt-in and informational.
|
||||
|
||||
Optional semantic candidate checks compare a bounded claim set and flag
|
||||
low-confidence evidence gaps, same-key disagreements, near-duplicates, and
|
||||
claims citing different revisions of one logical source. Findings link both
|
||||
page, claim, and source identities so reviewers can inspect both sides without
|
||||
copying protected text into the report.
|
||||
|
||||
Set `persistFindings: true` with a stable `runId` to synchronize findings into
|
||||
the file or SQLite repository. Exact retries of one run chunk do not increment
|
||||
occurrence counts. `pageLimit` is capped at 500 and `pageCursor` resumes the
|
||||
deterministically ordered page scan; the response returns a continuation with
|
||||
the next cursor and completion state. Each scheduled-workflow tick therefore
|
||||
performs bounded work, persists idempotently, and resumes after interruption.
|
||||
|
||||
Durable findings retain severity, status, owner, acknowledgement reason, due
|
||||
date, remediation task or proposal links, first and last observation,
|
||||
occurrence count, revision, and digest-bound transition history. Status is
|
||||
`open`, `acknowledged`, `remediating`, or `resolved`; only administrators
|
||||
resolve a finding. The findings list and integrity-health routes apply the same
|
||||
launch scope and expose open, acknowledged, remediating, resolved, overdue, and
|
||||
last-observation counts.
|
||||
|
||||
## Claim lifecycle
|
||||
|
||||
Every newly created material claim starts `active`. A compare-and-set transition
|
||||
can move it through `needs-review`, `disputed`, `superseded`, `retracted`, or
|
||||
back to an earlier state. The caller supplies the expected page digest, expected
|
||||
claim state, operation identity, reason, and optional retained evidence source
|
||||
IDs. Each transition records both states, evidence IDs, actor, timestamp,
|
||||
operation and request digests, and its own digest in a new page revision.
|
||||
|
||||
Agents can flag claims only as `needs-review` or `disputed`; an administrator
|
||||
must finalize supersession, retraction, or resolution. Exact retries are
|
||||
idempotent, changed reuse of an operation identity fails, stale page or state
|
||||
evidence fails compare-and-set, and page history makes the transition
|
||||
reversible. A disputed claim remains visible with all citations and transition
|
||||
evidence; the transition never deletes or silently replaces either side.
|
||||
|
||||
## Query promotion
|
||||
|
||||
Search responses carry an evidence digest over the exact query, bounded result array, and launch context. A caller can select one or more result IDs and propose new or revised pages without converting the answer directly into durable truth. Promotion validates the unchanged evidence digest and run binding, collection membership of raw sources, current identity and exact citations of derived pages, and the selected source set before creating a standard ingestion dry run.
|
||||
|
||||
The proposal persists the query, evidence digest, and sorted selected result IDs. Its candidate pages, source IDs, contradictions, review, atomic apply, reversal, attribution, and idempotency all use the same transaction contract as source ingestion. Promotion never adds a second mutation path.
|
||||
|
||||
## Cited work-product export
|
||||
|
||||
A validated search selection can also create a Markdown work product. The render preserves each selected result's raw-versus-derived kind, backend, score, redacted snippet, and exact source citations. Relative source links point back to immutable source metadata or the cited derived page. Work-product metadata retains the collection, query, evidence digest, export policy, selected-result count, and citation count so later Markdown or JSON export does not sever provenance.
|
||||
|
||||
The collection export policy is enforced twice. `forbidden` blocks work-product creation. `redacted-only` prevents a `none` request, defaults the product to redacted export, and causes the general work-product exporter to reject an explicit full-export override. `allowed` collections can request full output but still default to standard redaction. Confidential or restricted selected evidence independently forces redaction, and the work product retains the effective classification and launch-manifest digest for later enforcement.
|
||||
|
||||
## Current delivery boundary
|
||||
|
||||
Knowledge collections now enforce workspace RBAC, collection policy,
|
||||
classification-aware previews and exports, and exact run launch resources
|
||||
through the same immutable catalog, page graph, search, promotion, export, and
|
||||
proposal transaction. The v1 boundary does not include an extractor framework
|
||||
or an alternate source/page store; ingestion candidates still arrive through
|
||||
the reviewed proposal API.
|
||||
|
||||
## Code
|
||||
|
||||
- Shared contracts: `shared/src/types/knowledge-collection.types.ts`
|
||||
- Validation: `server/src/schemas/knowledge-collection-schemas.ts`
|
||||
- File repository: `server/src/storage/knowledge-collection-repository.ts`
|
||||
- SQLite repository and schema: `server/src/storage/sqlite/knowledge-collection-repository.ts`, `server/src/storage/sqlite/migrations.ts`
|
||||
- Service and RBAC: `server/src/services/knowledge-collection-service.ts`
|
||||
- REST routes: `server/src/routes/knowledge-collections.ts`
|
||||
- Focused verification: `server/src/__tests__/knowledge-collection-service.test.ts`, `server/src/__tests__/routes/knowledge-collections.test.ts`
|
||||
152
docs/architecture/PHASE-CAPABILITY-PROFILES.md
Normal file
152
docs/architecture/PHASE-CAPABILITY-PROFILES.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Phase Capability Profiles
|
||||
|
||||
Issue #1034 establishes the provider-neutral authority contract for execution
|
||||
phases. It defines what a phase may request and how Veritas computes the
|
||||
effective result. Issue #1035 adds durable active-run transitions and operator
|
||||
controls. Issues #1036 and #1033 bind that evidence through launch,
|
||||
continuation, tools, approvals, completion, API, CLI, and operator UI surfaces.
|
||||
|
||||
The compiler remains intentionally pure. It does not mutate an active attempt,
|
||||
persist a transition, or filter a tool catalog. The separate
|
||||
[Phase Transition Journal](PHASE-TRANSITION-JOURNAL.md) owns active state
|
||||
changes and their evidence; launch and tool services consume the compiled
|
||||
result.
|
||||
|
||||
## Contract
|
||||
|
||||
The versioned contracts are:
|
||||
|
||||
- `phase-capability-profile/v1` for built-in and workspace-defined profiles
|
||||
- `phase-transition-intent/v1` for a requested move between phase identities
|
||||
- `phase-capability-evidence/v1` for the compiled result and blockers
|
||||
|
||||
The built-in phase names are `explore`, `plan`, `implement`, `verify`, and
|
||||
`publish`. Launches without a profile compile in explicit `legacy` mode. Legacy
|
||||
mode preserves the intersection of existing policies and emits a warning; it
|
||||
does not silently invent a phase.
|
||||
|
||||
## Authority dimensions
|
||||
|
||||
The compiler keeps these dimensions independent:
|
||||
|
||||
| Dimension | Scope meaning |
|
||||
| --------------------- | --------------------------------------------------- |
|
||||
| `filesystem.read` | Exact logical paths or roots |
|
||||
| `filesystem.write` | Exact logical paths or roots |
|
||||
| `command.execute` | Trusted command classes, not arbitrary command text |
|
||||
| `network.egress` | Exact destinations or policy-owned destination IDs |
|
||||
| `credential.access` | Credential definition references, never values |
|
||||
| `external.action` | Exact external action classes |
|
||||
| `artifact.plan.write` | The narrow harness-owned plan artifact capability |
|
||||
|
||||
Scopes are exact strings. `*` means that one source does not narrow the
|
||||
dimension. It cannot be combined with exact scopes. The compiler does not infer
|
||||
path ancestry, destination patterns, credential aliases, or command safety.
|
||||
|
||||
In particular, an `inspect` command class is only a policy identifier for a
|
||||
trusted, enforceable tool mapping. It does not make arbitrary shell commands
|
||||
read-only.
|
||||
|
||||
## Built-in profiles
|
||||
|
||||
| Phase | General workspace write | Task credentials | External mutation | Plan artifact |
|
||||
| ----------- | ----------------------- | ------------------ | ------------------ | ------------------- |
|
||||
| `explore` | No | No | No | No |
|
||||
| `plan` | No | No | No | Optional exact path |
|
||||
| `implement` | Yes | Separately bounded | No | No |
|
||||
| `verify` | Yes | No | No | No |
|
||||
| `publish` | Yes | Separately bounded | Separately bounded | No |
|
||||
|
||||
Profiles are ceilings, not grants by themselves. Agent, sandbox, tool, and
|
||||
launch policy sources can always narrow them.
|
||||
|
||||
## Deterministic intersection
|
||||
|
||||
Effective authority is the exact intersection of:
|
||||
|
||||
1. Parent authority
|
||||
2. The selected phase profile
|
||||
3. Agent profile authority
|
||||
4. Sandbox capability
|
||||
5. Tool catalog capability
|
||||
6. Launch policy
|
||||
|
||||
The compiler never unions scopes. A descendant therefore cannot exceed its
|
||||
parent. Every dimension records requested scopes, effective scopes, and the
|
||||
sources that narrowed it.
|
||||
|
||||
Each non-phase source also reports whether it can enforce every dimension:
|
||||
|
||||
- `enforced` allows its exact scopes to participate.
|
||||
- `unsupported` removes the dimension and creates a typed blocker when the
|
||||
profile requires it.
|
||||
- `unenforceable` also removes the dimension and creates a distinct typed
|
||||
blocker when required.
|
||||
|
||||
An enforced source with no matching requested scope produces
|
||||
`required-authority-denied` for a required dimension. Optional authority can be
|
||||
narrowed away with a warning. Unknown dimensions and malformed source records
|
||||
are rejected by strict Zod schemas.
|
||||
|
||||
## Plan artifact exception
|
||||
|
||||
The plan profile may request one plan artifact through:
|
||||
|
||||
```json
|
||||
{
|
||||
"exactPath": ".veritas-kanban/plans/task-1034.md",
|
||||
"owner": "veritas-kanban",
|
||||
"transport": "harness-api"
|
||||
}
|
||||
```
|
||||
|
||||
The effective evidence binds that exact normalized repository-relative path.
|
||||
The contract records `shellRedirection: false` and `indirectWrites: false`.
|
||||
Absolute paths, traversal, backslashes, control characters, and shell syntax
|
||||
fail closed. The exception never adds `filesystem.write` authority and cannot
|
||||
be requested by another built-in phase.
|
||||
|
||||
Only the harness API may perform this write. A provider shell, hook, MCP tool,
|
||||
or redirection must not translate the exception into a general filesystem
|
||||
grant.
|
||||
|
||||
## Legacy migration
|
||||
|
||||
Existing attempts and workflow history are not rewritten. A launch with no
|
||||
explicit phase and no profile-authoritative parent remains in `legacy` mode;
|
||||
its existing sandbox, provider, profile, and tool policies still apply.
|
||||
Readers expose that identity without inventing a transition journal.
|
||||
|
||||
Migrate one execution path at a time:
|
||||
|
||||
1. Add `phase` to the API or CLI launch, or to an agent workflow step.
|
||||
2. Run launch preview against the exact provider, agent profile, sandbox, and
|
||||
tool selection.
|
||||
3. Resolve typed enforcement blockers instead of weakening the phase.
|
||||
4. Start the run only after preview is enforceable, then use `agent:phase` to
|
||||
inspect the server-owned evidence.
|
||||
|
||||
Agent profile packages remain independent narrowing sources. They do not
|
||||
silently select or widen a phase, so existing packages need no schema rewrite.
|
||||
ACP stdio is the current adapter for explicit phase execution. Keep other
|
||||
adapters in legacy mode until their runtime exposes equivalent pre-execution
|
||||
command and external-action mediation.
|
||||
|
||||
## Delivery boundary
|
||||
|
||||
The delivered phase control plane now includes:
|
||||
|
||||
- Shared types, strict schemas, built-in profiles, and the pure compiler from
|
||||
#1034
|
||||
- Durable transition state, approvals, emergency override expiry, restart
|
||||
recovery, REST, and CLI controls from #1035
|
||||
- Launch, descendant, retry, fallback, resume, fork, and handoff propagation
|
||||
from #1036
|
||||
- Phase-filtered tool catalogs, stale-call rejection, phase-bound approvals,
|
||||
completion evidence, and shared REST, CLI, and UI projections from #1033
|
||||
|
||||
Provider enforcement remains capability-bound. ACP stdio exposes a
|
||||
pre-execution permission path for command and external actions. Adapters that
|
||||
cannot prove equivalent mediation return typed blockers for explicit phases;
|
||||
Veritas does not substitute prompt instructions or post-execution events for
|
||||
enforcement.
|
||||
133
docs/architecture/PHASE-TRANSITION-JOURNAL.md
Normal file
133
docs/architecture/PHASE-TRANSITION-JOURNAL.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# Phase Transition Journal
|
||||
|
||||
Issue #1035 adds the durable state machine that moves one active run between
|
||||
compiled phase capability profiles. It builds on the
|
||||
[Phase Capability Profiles](PHASE-CAPABILITY-PROFILES.md) contract without
|
||||
duplicating the launch propagation and tool enforcement delivered by #1036 and
|
||||
#1033.
|
||||
|
||||
## Durable record
|
||||
|
||||
Each applied change appends one immutable `phase-transition-record/v1` record.
|
||||
The record binds:
|
||||
|
||||
- Workspace, task, active attempt, sequence, and idempotent operation ID
|
||||
- Prior and effective `phase-capability-evidence/v1` documents
|
||||
- Added and removed scopes for every changed authority dimension
|
||||
- Verified actor, reason, and policy decision
|
||||
- Exact approval or emergency-override evidence when required
|
||||
- Active `run-launch-manifest/v1` digest
|
||||
- Deterministic run-event projection reference and timestamp
|
||||
|
||||
File storage uses a locked, bounded JSONL journal. SQLite uses an append-only
|
||||
table with unique run sequence and operation constraints. Both repositories
|
||||
implement the same compare-and-set contract and recover the current phase as
|
||||
the highest sequence for the exact workspace, task, and attempt.
|
||||
|
||||
## Compare-and-set rules
|
||||
|
||||
A request supplies the expected sequence, prior phase-evidence digest, and
|
||||
launch-manifest digest. The server also verifies that the task still has the
|
||||
same running attempt, executable provider, and manifest before appending.
|
||||
|
||||
The first transition additionally supplies the exact initial compiled
|
||||
evidence. Later transitions use the journal's current evidence. Stale attempt,
|
||||
sequence, evidence, manifest, or changed reuse of an operation ID fails closed.
|
||||
An exact duplicate operation returns the original record without replaying the
|
||||
transition.
|
||||
|
||||
Evidence is validated against its content digest. A blocked result or legacy
|
||||
identity cannot become the target of an operator transition.
|
||||
|
||||
## Policy and approvals
|
||||
|
||||
The authority delta is calculated independently for filesystem read and write,
|
||||
command execution, network egress, credentials, external actions, and the plan
|
||||
artifact capability.
|
||||
|
||||
- Same-authority and narrowing transitions apply immediately.
|
||||
- Any added scope creates or reuses an exact-action request in the existing run
|
||||
approval broker.
|
||||
- Approval binds the operation, prior and target evidence digests, manifest,
|
||||
and complete authority delta.
|
||||
- Pending approval returns `approval-required`; rejected or expired approval
|
||||
fails closed.
|
||||
- Credential or external-action expansion is classified as critical risk.
|
||||
|
||||
An emergency expansion requires verified `admin:manage` authority, a reason,
|
||||
and an expiry no more than 24 hours in the future. The first read after expiry
|
||||
appends one system-attributed `override-expired` transition that restores the
|
||||
prior evidence. Concurrent readers use compare-and-set behavior, so only one
|
||||
expiry record wins.
|
||||
|
||||
## REST controls
|
||||
|
||||
Read the active phase and bounded history:
|
||||
|
||||
```http
|
||||
GET /api/agents/:taskId/phase?attemptId=attempt_123&limit=100
|
||||
```
|
||||
|
||||
Request or apply a transition:
|
||||
|
||||
```http
|
||||
POST /api/agents/:taskId/phase/transitions
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"attemptId": "attempt_123",
|
||||
"operationId": "move-to-implement",
|
||||
"expectedSequence": 1,
|
||||
"expectedPhaseEvidenceDigest": "sha256:...",
|
||||
"expectedManifestDigest": "sha256:...",
|
||||
"reason": "The approved plan is ready to implement.",
|
||||
"targetEvidence": {
|
||||
"schemaVersion": "phase-capability-evidence/v1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The abbreviated `targetEvidence` above represents the complete compiled
|
||||
evidence document. Reads require `agent:read`; transition requests require
|
||||
`task:write`. Expansion is not applied until an administrator resolves its
|
||||
approval. Emergency override authority is checked independently by the service.
|
||||
|
||||
## CLI controls
|
||||
|
||||
```bash
|
||||
vk agent:phase TASK-001 --attempt attempt_123 --json
|
||||
|
||||
vk agent:transition-phase TASK-001 \
|
||||
--attempt attempt_123 \
|
||||
--operation move-to-implement \
|
||||
--target-evidence ./implement-evidence.json \
|
||||
--reason "The approved plan is ready to implement." \
|
||||
--json
|
||||
|
||||
vk agent:decide-phase-approval runapproval_123 \
|
||||
--decision approve \
|
||||
--note "Reviewed the exact authority delta." \
|
||||
--json
|
||||
```
|
||||
|
||||
For the first transition, add `--from-evidence` and `--manifest`. The CLI reads
|
||||
the current durable record for later transitions and supplies its sequence,
|
||||
evidence digest, and manifest automatically. Retry an approved expansion with
|
||||
the same `--operation` value and the returned `--approval-id`.
|
||||
|
||||
Emergency override uses `--override-until` and `--override-reason` together.
|
||||
The server remains authoritative for administrator permission and maximum
|
||||
expiry.
|
||||
|
||||
## Delivery boundary
|
||||
|
||||
The journal makes phase state, approval, expiry, restart recovery, and operator
|
||||
control durable. Launch propagation binds it into descendants, retries,
|
||||
continuations, and handoffs. Tool catalogs, mediated invocation, approvals,
|
||||
completion results, and the run timeline consume the same server-owned active
|
||||
projection.
|
||||
|
||||
The journal still does not prove provider enforcement by itself. ACP stdio
|
||||
supplies the current pre-execution mediation contract. An adapter without
|
||||
equivalent command and external-action controls fails an explicit phase launch
|
||||
closed.
|
||||
52
docs/architecture/RUN-TERMINAL-V1.md
Normal file
52
docs/architecture/RUN-TERMINAL-V1.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Run Terminal v1
|
||||
|
||||
`run-terminal-handle/v1` is the provider-neutral ownership contract for commands executed on behalf of one Veritas run. It is distinct from the provider process, workflow scheduler, MCP server runtime, and any operator shell.
|
||||
|
||||
## Current scope
|
||||
|
||||
The first implementation supports background pipe-mode commands with:
|
||||
|
||||
- one opaque handle bound to workspace, task, attempt, and launch-manifest digest;
|
||||
- one stable request identity and digest for exact approval binding and retry deduplication;
|
||||
- a manifest-approved executable, relative cwd, and environment-key subset;
|
||||
- immediate background return, bounded single/any/all waits, foreground detachment without changing ownership, status inspection, and attempt cleanup;
|
||||
- active run status includes only handles owned by that workspace, task, and attempt;
|
||||
- run completion and cancellation terminate all still-active attempt handles, including detached jobs, before the terminal run result is committed;
|
||||
- fail-closed ownership persistence before a new handle is returned;
|
||||
- redacted byte-bounded stdout/stderr chunks with monotonic cursors, explicit gap metadata, and a total-volume circuit that terminates noisy jobs before they flood the journal;
|
||||
- graceful process-group termination followed by bounded forced termination;
|
||||
- causal `command.started`, `command.detached`, `stream.stdout`, `stream.stderr`, and `command.completed` journal events; and
|
||||
- bounded handle and retained-output reconstruction from the durable causal journal.
|
||||
|
||||
Externally initiated pipe execution is available only through the active-run API and an exact high-risk shell approval. PTY mode, interactive stdin, and restart reattachment remain explicitly unsupported. Callers receive typed blockers instead of an implicit downgrade.
|
||||
|
||||
## Authority boundary
|
||||
|
||||
The service accepts a server-owned launch context and an untrusted command request. The launch context contains the exact workspace, task, attempt, launch-manifest digest, worktree root, approved environment values, approved executable list, and optional server-owned launch wrapper. The request may select only an approved command, arguments without credential material, a canonical cwd within the worktree, and environment keys already present in that context. Lexical traversal and symlink escapes fail closed before spawn.
|
||||
|
||||
The active-run layer first reconciles durable handles, verifies the immutable task envelope still authorizes execution in the exact worktree, confirms the persisted launch manifest and phase authority, and rejects required filesystem posture that the terminal child cannot inherit. It then creates an exact-action `run-approval/v1` request. Only an approved identical retry reaches the terminal service. The server-owned wrapper applies filesystem enforcement and mandatory egress variables after caller-selectable environment values, so omitting a proxy name cannot bypass the run's network posture.
|
||||
|
||||
The child is launched without a shell. On Unix-like systems it owns a detached process group; on Windows it remains an exact child until the platform-specific tree supervisor is added. The service never exposes a writable stdin stream.
|
||||
|
||||
Terminal children are subordinate to their owning run. Detaching changes foreground coordination only; it does not outlive the attempt. Attempt cleanup terminates matching live handles in parallel and verifies that already-terminal handles have complete journal evidence before allowing the run completion to commit.
|
||||
|
||||
When a provider completion arrives after a server restart, Veritas reconciles the attempt's durable terminal journal before cleanup. Handles that cannot be safely reattached are recorded as interrupted before the provider's terminal result is committed.
|
||||
|
||||
## Control API
|
||||
|
||||
Authenticated clients with `agent:read` can list active attempt handles and retrieve cursor-addressed output. Clients with `agent:write` can request exact execution, perform bounded single/any/all waits, detach a foreground handle, and terminate a handle. Every operation resolves the active task attempt first and then verifies the opaque handle's workspace, task, and attempt identity. Scope mismatches return not found rather than leaking another run's handle.
|
||||
|
||||
The canonical routes live under `/api/v1/run-terminals/runs/:taskId/:attemptId`. The `/api` compatibility mount exposes the same routes. `POST .../execute` returns `202 approval-required` until the exact request is approved, then returns `201 started` with the opaque handle. A stable `requestId` deduplicates concurrent and post-restart retries; changing any approved field under that identity returns conflict.
|
||||
|
||||
## Output and replay
|
||||
|
||||
Each redacted chunk receives a handle-local cursor. Chunks are capped below the journal spill threshold so the cursor, stream, and content remain directly replayable. Retention is byte-bounded; when older chunks are dropped, `retainedFromCursor`, `droppedBytes`, `truncated`, and the query page's `gap` flag make the loss explicit. Completion waits for successful terminal journal persistence, so callers never receive a durable-completion claim when causal evidence is incomplete.
|
||||
|
||||
`reconcileAttempt(workspaceId, taskId, attemptId)` replays at most 20,000 system-authored run-terminal events and validates each persisted handle before making it visible. Terminal handles and retained output remain queryable after a service restart. A handle without durable completion evidence is marked `interrupted` and receives a deduplicated `command.completed` reconciliation event. The runtime does not claim that it can inherit stdout/stderr pipes from the prior server process, so `restartReattachment` remains `unsupported` rather than pretending the process is safely controlled.
|
||||
|
||||
## Code
|
||||
|
||||
- Shared contract: `shared/src/types/run-terminal.types.ts`
|
||||
- Request validation: `server/src/schemas/run-terminal-schemas.ts`
|
||||
- Runtime: `server/src/services/run-terminal-service.ts`
|
||||
- Focused verification: `server/src/__tests__/run-terminal-service.test.ts`
|
||||
29
docs/architecture/SERVICE-FILESYSTEM-BOUNDARY.md
Normal file
29
docs/architecture/SERVICE-FILESYSTEM-BOUNDARY.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Service Filesystem Boundary
|
||||
|
||||
Service modules must use the storage abstraction instead of importing Node's
|
||||
filesystem APIs directly. The current exceptions are tracked in
|
||||
[`service-filesystem-boundary.json`](service-filesystem-boundary.json) so the
|
||||
existing migration debt is explicit without allowing it to spread.
|
||||
|
||||
Run the boundary gate with:
|
||||
|
||||
```bash
|
||||
pnpm check:service-filesystem-boundary
|
||||
```
|
||||
|
||||
The gate recursively scans `server/src/services/**/*.ts` and recognizes static
|
||||
imports, dynamic imports, and `require()` calls for `fs`, `node:fs`, and their
|
||||
`/promises` variants. Filesystem text embedded in strings and comments is
|
||||
ignored. The command exits nonzero and names the file when it finds:
|
||||
|
||||
- a direct import without a classified inventory entry;
|
||||
- an invalid category, owner, or rationale;
|
||||
- a duplicate entry; or
|
||||
- a stale entry after an import has been removed.
|
||||
|
||||
`maximumEntries` must equal the number of classified exceptions. Any increase
|
||||
therefore requires a visible inventory and ratchet change in the same review.
|
||||
The remaining #1163 child issues own the reductions: #1187 covers operational
|
||||
evidence, #1188 managed content, and #1189 final process I/O plus removal of the
|
||||
last compatibility exceptions. Each migration must delete its stale inventory
|
||||
entries and lower `maximumEntries` in the same change.
|
||||
|
|
@ -44,6 +44,7 @@ The catalog binds:
|
|||
|
||||
- task and attempt IDs;
|
||||
- provider, provider-runtime digest, and task-envelope digest;
|
||||
- active launch phase evidence when the run is phase-controlled;
|
||||
- definition and discovery digests;
|
||||
- required or optional readiness; and
|
||||
- each tool's `allow`, `deny`, or `approval` decision.
|
||||
|
|
@ -86,6 +87,25 @@ Approval-required tools are deliberately disabled there because native calls
|
|||
would bypass the Veritas approval broker. Those tools use the mediated
|
||||
`call_run_tool` path.
|
||||
|
||||
## Phase Authority
|
||||
|
||||
MCP discovery maps the standard `annotations.readOnlyHint: true` value to an
|
||||
external read. Missing or false annotations classify the tool as an external
|
||||
mutation. A phase-controlled catalog includes only tools and credential
|
||||
bindings allowed by the launch evidence. Approval-required phase dimensions
|
||||
also stay out of native provider configuration so they cannot bypass Veritas.
|
||||
|
||||
Mediated invocation resolves the current server-owned phase again before
|
||||
dispatch. It rejects a hidden tool call, a credential reference outside the
|
||||
active phase, or a catalog compiled from different phase evidence. A
|
||||
transition can therefore narrow a running attempt immediately without relying
|
||||
on the provider to refresh a cached tool list.
|
||||
|
||||
Every phase-bound approval records the exact manifest digest, phase evidence
|
||||
digest, identity, transition sequence, dimension, and requested scopes. The
|
||||
broker re-checks those values at decision time, so an older approval cannot
|
||||
authorize an action after narrowing or authorize a wider scope.
|
||||
|
||||
## Mediated Invocation
|
||||
|
||||
A call must provide the exact task, active attempt, server, tool, arguments,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
# Veritas Kanban v6 Agent Runtime Control Plane
|
||||
|
||||
This document defines the shipped v6.0.0 architecture for executable agent
|
||||
This document defines the supported v6.1.2 architecture for executable agent
|
||||
harnesses and Buzz integration. It is the version-level composition of the
|
||||
individual contract documents for
|
||||
[ACP](ACP-PROVIDER-V1.md),
|
||||
[harness conformance](HARNESS-CONFORMANCE-V1.md),
|
||||
[filesystem sandbox backends](FILESYSTEM-SANDBOX-BACKENDS.md),
|
||||
[tool control](TOOL-CONTROL-PLANE-V1.md), and
|
||||
[runtime hooks](RUNTIME-HOOK-V1.md).
|
||||
|
||||
Documentation freshness: 2026-07-24 for Veritas Kanban 6.0.0.
|
||||
Documentation freshness: 2026-08-24 for Veritas Kanban 6.1.2.
|
||||
|
||||
## Authority Model
|
||||
|
||||
|
|
@ -42,6 +43,12 @@ owns signed delivery, not Veritas task or completion state.
|
|||
Provider profiles select an adapter. No unknown executable, provider-less
|
||||
record, or unsupported profile can route through an implicit fallback.
|
||||
|
||||
The server resolves these contracts through a provider adapter registry. The
|
||||
registry owns the task-envelope renderer, runtime probe, event mapper, start
|
||||
dispatch, and stop semantics for each exact executable provider. Attempt state
|
||||
transitions remain centralized in the lifecycle coordinator, while terminal
|
||||
completion and recovery consume the same persisted provider evidence.
|
||||
|
||||
## Run Lifecycle
|
||||
|
||||
```text
|
||||
|
|
@ -128,6 +135,7 @@ evidence and never hides a failure.
|
|||
artifacts are bounded.
|
||||
- Required unsupported filesystem, network, environment, credential, MCP,
|
||||
tool, approval, lifecycle, and budget controls block before attempt mutation.
|
||||
- Fine-grained HTTP method/path/domain proxy enforcement remains deferred to
|
||||
issue 855. v6.0.0 claims only the network controls proven in current provider
|
||||
runtime evidence.
|
||||
- Required network policy resolves and pins destinations, routes governed
|
||||
traffic through the run-scoped egress gateway, enforces protocol, host, port,
|
||||
HTTP method, and normalized path rules, and rejects direct or unverifiable
|
||||
bypass paths.
|
||||
|
|
|
|||
65
docs/architecture/WORKSPACE-CHECKPOINTS-V1.md
Normal file
65
docs/architecture/WORKSPACE-CHECKPOINTS-V1.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Workspace Checkpoints v1
|
||||
|
||||
`workspace-checkpoint/v1` is the immutable storage contract for a run-owned worktree at a safe execution boundary. It is not the existing task-resume checkpoint and it is not permission to mutate or rewind a workspace.
|
||||
|
||||
## Capture foundation
|
||||
|
||||
The first slice captures:
|
||||
|
||||
- exact workspace, task, attempt, boundary, parent, turn, and conversation-cursor identity;
|
||||
- Git HEAD, branch, porcelain status digest, and a content-addressed copy of the exact Git index;
|
||||
- tracked worktree files, untracked non-ignored files, and explicit tracked-file absence;
|
||||
- content-addressed regular text blobs with mode, size, and SHA-256 evidence;
|
||||
- deterministic exclusion evidence for sensitive files, binary files, symlinks, unsupported entries, file-size limits, aggregate byte limits, and inventory limits; and
|
||||
- a digest over the complete immutable metadata document.
|
||||
|
||||
Ignored files are excluded by Git policy. Sensitive files, binary files, symlinks, `.git`, and `.veritas-kanban` content are excluded before blob persistence. Defaults cap one checkpoint at 10,000 files, 64 MiB total content, 8 MiB per file, and 2,000 retained exclusion records.
|
||||
|
||||
## Consistency and atomicity
|
||||
|
||||
Capture resolves the canonical Git worktree root and refuses a subdirectory or different repository. It records Git state before scanning, validates each file did not change across no-follow open/read/file-handle-stat checks, and compares HEAD, branch, index, and status again before publishing. Concurrent workspace mutation therefore aborts the capture instead of exposing a mixed-time snapshot.
|
||||
|
||||
Blobs are written under a SHA-256 content address and verified on reuse. Metadata is written into a private temporary directory and the directory is renamed into the exact run scope only after every blob and integrity check succeeds. Interrupted captures may leave unreachable deduplicated blobs, but list/get cannot expose a partial checkpoint as valid.
|
||||
|
||||
Caller operation IDs are persisted only as digests. Repeating the same exact capture operation returns the original checkpoint. Reusing that operation identity with changed boundary, scope, cursor, parent, worktree, or policy evidence returns conflict.
|
||||
|
||||
## Runtime boundaries and ownership
|
||||
|
||||
The workspace checkpoint coordinator captures immediately before the first provider turn, a native steered operator turn, and provider-native compaction. Recovery launches are labeled `before-retry`; cross-provider execution-tree edges are labeled `before-provider-handoff`; other launches and native steering are labeled `before-user-turn`.
|
||||
|
||||
Only worktrees with a complete Veritas manifest and lease are eligible. The coordinator re-reads the durable manifest and requires exact task, attempt, path, branch, lease, lifecycle, rebase, and unexpired ownership evidence before each capture. Unmanaged worktrees are skipped, while partial, stale, expired, or conflicting ownership fails closed.
|
||||
|
||||
Captures are serialized per attempt and chained through `parentCheckpointId`. Every published boundary emits a deduplicated `workspace.checkpoint.created` run event containing checkpoint identity, digest, boundary, counts, and a digest of any provider conversation cursor.
|
||||
|
||||
## Deliberate next boundaries
|
||||
|
||||
Direct parent-to-child checkpoints can be compared without touching the worktree. The bounded comparison reports affected captured files, line-numbered unified hunks, content digests, mode changes, and whether HEAD, branch, index, or Git status changed. Comparisons fail closed if either checkpoint is missing, the checkpoints are not directly chained, or their worktree ownership evidence differs.
|
||||
|
||||
Provider event mappers normalize bounded relative file paths and tool names into the causal journal. The attribution service considers only evidence between the two checkpoint-created events. Explicit provider file events and known path-bearing write tools are agent evidence; operator file events are operator evidence; system file events are external evidence. When every write event for a file carries bounded unified-diff hunk ranges, each checkpoint hunk is attributed only from overlapping old and new line ranges. This can distinguish agent and operator changes in different hunks of the same file. Any unscoped write evidence falls back to conservative file-window attribution; missing, non-overlapping, or mixed exact evidence remains `unknown`. Missing checkpoint event boundaries mark the complete evidence window unavailable.
|
||||
|
||||
Rewind preview revalidates the durable worktree lease before and after a no-follow current-state inspection. It compares the current worktree root, HEAD, branch, index, status, affected file hashes, modes, exclusions, and attribution against the expected descendant checkpoint. The result lists reverse file actions, Git and conversation-cursor changes, estimated discarded bytes, and explicit blockers. Automatic rewind is safe only when every current-state and ownership check matches and every changed file is exclusively supported by high-confidence agent evidence.
|
||||
|
||||
Every preview carries both a full observation digest and a stable evidence digest over ownership, current state, diff, conflicts, resolutions, selected paths, and loss estimates. The stable digest excludes only observation timestamps and their derived digests, so an unchanged preview can survive an asynchronous approval round trip while any material evidence change invalidates the approval. A conflict-free preview, or one whose attribution conflicts have an explicit per-path `accept`, `reject`, or `leave-untouched` decision, can drive a private `workspace-checkpoint-rewind-transaction/v1` record. `accept` selects the path for rewind; `reject` and `leave-untouched` preserve the descendant path. Other conflict classes remain unresolved and fail closed. The storage transaction rechecks the complete descendant diff immediately before mutation, restores only the selected paths through no-follow parent validation and atomic file replacement, verifies the exact hybrid target/descendant file posture, and commits the canonical decisions as durable evidence. Any ordinary failure rolls the affected paths back to the descendant checkpoint. An interrupted transaction remains recoverable; recovery accepts only files that still match the recorded target or descendant states and refuses unknown external edits. Attempt-local mutation serialization prevents competing rewind operations inside the owning server process, while the higher service layer must revalidate the authoritative worktree lease before invoking storage.
|
||||
|
||||
The rewind coordinator requests a critical, non-mobile approval whose exact action binds the stable preview evidence, runtime state, provider evidence revision, checkpoints, and estimated loss. It leaves the provider running while approval is pending. Once approved, the provider runtime port quiesces the exact runtime state, after which the coordinator regenerates the preview and requires the same stable evidence before starting storage mutation. Runtime cursor recovery occurs only after the storage transaction commits. If runtime recovery fails, the coordinator rolls the committed storage transaction back before allowing runtime recovery from the descendant anchor; a failed storage rollback deliberately leaves the provider quiesced for recovery.
|
||||
|
||||
The production runtime port currently supports only an active Codex app-server attempt. It interrupts the exact live turn, consumes that terminal notification as quiescence instead of finalizing the attempt, and forks provider history from the approved target turn. The resulting thread receives a new provider ID, so the runtime records both the new live cursor and the checkpoint cursor used as its rewind anchor. A later operator message starts a new native turn on the recovered thread. The adapter refuses item-level cursors, a target in another thread, the currently interrupted turn, providers without native fork, and any runtime whose evidence changed during approval.
|
||||
|
||||
Operators call `POST /api/agents/:taskId/workspace/checkpoints/rewind` with the exact active `attemptId`, target and descendant checkpoint IDs, an idempotent `requestId` (or `X-Idempotency-Key`), and optional path resolutions. The first conflict-free or fully resolved request returns `202` with the critical approval. Repeating the same request after approval revalidates the evidence and returns `200` only after storage and runtime recovery commit. The route requires `agent:write` plus local run-control capability.
|
||||
|
||||
Retention pruning accepts explicit checkpoint-count, logical-byte, age, and protected-checkpoint limits. An active run always preserves every discovered complete chain tip even when configured limits are zero, including conservative preservation of concurrent branches. Cleanup reports the exact metadata bytes removed and logical content bytes dereferenced. Content-addressed blob garbage collection is deliberately deferred until it can coordinate safely with concurrent captures, so retention never claims those shared blob bytes as reclaimed.
|
||||
|
||||
This foundation does not yet claim partial-hunk resolution inside one file, provider-runtime rewind outside the exact Codex app-server turn-fork case, or shared blob garbage collection. Those layers must consume the immutable repository and remain preview-first.
|
||||
|
||||
## Code
|
||||
|
||||
- Shared contract: `shared/src/types/workspace-checkpoint.types.ts`
|
||||
- Validation: `server/src/schemas/workspace-checkpoint-schemas.ts`
|
||||
- File repository: `server/src/storage/workspace-checkpoint-repository.ts`
|
||||
- Ownership and boundary coordination: `server/src/services/workspace-checkpoint-service.ts`
|
||||
- Read-only comparison: `server/src/services/workspace-checkpoint-diff-service.ts`
|
||||
- Conservative causal attribution: `server/src/services/workspace-checkpoint-attribution-service.ts`
|
||||
- Conflict-aware rewind preview: `server/src/services/workspace-checkpoint-rewind-preview-service.ts`
|
||||
- Approval, storage, and runtime coordination: `server/src/services/workspace-checkpoint-rewind-service.ts`
|
||||
- Production Codex app-server port and operator route: `server/src/services/clawdbot-agent-service.ts`, `server/src/routes/agents.ts`
|
||||
- Focused verification: `server/src/__tests__/workspace-checkpoint-repository.test.ts`, `server/src/__tests__/workspace-checkpoint-diff-service.test.ts`, `server/src/__tests__/workspace-checkpoint-attribution-service.test.ts`, and `server/src/__tests__/workspace-checkpoint-rewind-preview-service.test.ts`
|
||||
126
docs/architecture/WORKSPACE-EXECUTION-TRUST.md
Normal file
126
docs/architecture/WORKSPACE-EXECUTION-TRUST.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Workspace Execution Trust
|
||||
|
||||
Veritas treats repository-controlled agent instructions and executable
|
||||
configuration as an execution boundary. A task worktree is scanned before
|
||||
launch, the exact inventory is evaluated against an operator decision, and the
|
||||
result is bound into the immutable run launch manifest.
|
||||
|
||||
This prevents a cloned, moved, nested, sibling, or modified repository from
|
||||
silently inheriting authorization that was granted to different content.
|
||||
|
||||
## Launch flow
|
||||
|
||||
Every executable task launch follows the same sequence:
|
||||
|
||||
1. Resolve the task's registered Git worktree and canonical repository identity.
|
||||
2. Inventory recognized repository-controlled instructions and executable
|
||||
configuration without following configuration symlinks.
|
||||
3. Evaluate the inventory against the latest operator decision, project maximum,
|
||||
and effective launch restrictions.
|
||||
4. Block untrusted execution before reading repository instructions or creating
|
||||
an attempt.
|
||||
5. Record the redacted identity, exact inventory digest, decision evidence, and
|
||||
requested capabilities in `run-launch-manifest/v1`.
|
||||
6. Rescan immediately before sandbox activation and provider creation. Any
|
||||
identity, inventory, or decision drift aborts the launch.
|
||||
|
||||
The no-configuration result is provisional. Veritas rescans it for every
|
||||
launch, so adding an instruction, hook, MCP server, workflow, extension, or
|
||||
provider configuration cannot inherit the earlier result.
|
||||
|
||||
## Workspace identity
|
||||
|
||||
`workspace-execution-trust/v1` derives identity from the canonical worktree,
|
||||
repository root, Git common directory, and credential-redacted remote identity.
|
||||
The identity survives a symlink alias or directory rename while remaining
|
||||
distinct for sibling clones and linked worktrees. Changing the remote identity
|
||||
also changes the trust identity.
|
||||
|
||||
Authorization never flows from a parent, child, sibling, or different remote
|
||||
repository based on path proximity.
|
||||
|
||||
## Inventory
|
||||
|
||||
The scanner classifies entries as:
|
||||
|
||||
- `declarative-only`: project policy that can only narrow trust.
|
||||
- `model-influencing`: agent instructions, provider instructions, agent
|
||||
definitions, and skills.
|
||||
- `executable`: MCP/tool server configuration, provider overrides, runtime
|
||||
hooks, language-server settings, workflows, extension configuration, and
|
||||
custom Git hooks.
|
||||
|
||||
Recognized sources include root harness instructions; GitHub Copilot
|
||||
instructions, workflows, and MCP configuration; Claude settings, agents,
|
||||
commands, skills, hooks, and MCP configuration; Codex configuration, rules, and
|
||||
skills; Cursor rules; VS Code tasks, settings, extensions, and MCP
|
||||
configuration; development-container configuration; `.envrc`; and supported
|
||||
Buzz, Grok Build, and generic agent definition directories.
|
||||
|
||||
Each inventory entry stores only its relative path, classification, requested
|
||||
capabilities, byte length, symlink state, canonical path digest, and content
|
||||
fingerprint. File contents and local absolute paths are not copied into the
|
||||
launch manifest.
|
||||
|
||||
The scanner fails closed when a recognized file exceeds 2 MiB, the inventory
|
||||
exceeds 2,000 entries, recursive discovery exceeds its bounded depth, or the
|
||||
worktree cannot be resolved as a valid Git repository.
|
||||
|
||||
## Decisions and effective modes
|
||||
|
||||
Operator decisions are append-only and bound to both the workspace identity and
|
||||
exact inventory digest.
|
||||
|
||||
| Mode | Effect |
|
||||
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `trusted` | Allows the exact reviewed inventory under the normal launch policy. |
|
||||
| `restricted` | Denies inventoried executable configuration and allows only an enforced read-only, no-network launch without task credentials, project tool servers, or external mutation. |
|
||||
| `denied` | Blocks the workspace until an operator records a later decision. |
|
||||
| `revoked` | Withdraws the latest active authorization without deleting its audit history. |
|
||||
|
||||
Executable configuration always requires an explicit decision. Model-only
|
||||
instructions may run provisionally in restricted mode when every restricted
|
||||
boundary is enforceable. Expired, revoked, stale, or inventory-mismatched
|
||||
authorization cannot permit a launch.
|
||||
|
||||
Decision creation and revocation require `admin:manage`. Every mutation records
|
||||
the authenticated actor, reason, exact inventory digest, timestamp, and
|
||||
superseded decision where applicable.
|
||||
|
||||
## Project maximum
|
||||
|
||||
A repository may add `.veritas-kanban/workspace-trust.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": "workspace-trust-policy/v1",
|
||||
"maximumTrust": "restricted"
|
||||
}
|
||||
```
|
||||
|
||||
`maximumTrust` accepts `trusted`, `restricted`, or `denied`. The file can only
|
||||
narrow an operator decision. An invalid or symlinked project policy is treated
|
||||
as `denied`.
|
||||
|
||||
## Operator workflow
|
||||
|
||||
```bash
|
||||
vk workspace-trust scan TASK-001
|
||||
vk workspace-trust decide TASK-001 \
|
||||
--mode trusted \
|
||||
--inventory sha256:... \
|
||||
--reason "Reviewed repository execution configuration"
|
||||
vk launch-preview TASK-001 --json
|
||||
```
|
||||
|
||||
To withdraw an authorization:
|
||||
|
||||
```bash
|
||||
vk workspace-trust revoke TASK-001 \
|
||||
--inventory sha256:... \
|
||||
--reason "Repository ownership changed"
|
||||
```
|
||||
|
||||
Use `--json` on any workspace-trust command for the complete versioned record.
|
||||
See [API Reference](../API-REFERENCE.md#workspace-execution-trust) for the REST
|
||||
surface.
|
||||
5
docs/architecture/service-filesystem-boundary.json
Normal file
5
docs/architecture/service-filesystem-boundary.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"maximumEntries": 0,
|
||||
"entries": []
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Veritas Kanban v4.1 adds QMD-backed retrieval for task/docs search, duplicate detection, and VERITAS chat context. QMD is optional; if it is unavailable, VK falls back to built-in keyword search across markdown files.
|
||||
|
||||
Workspace knowledge collections also use QMD when `backend` is `auto` or `qmd`. Veritas materializes current derived pages into hashed, excluded QMD collections under its runtime directory, scopes every query with the documented `-c` flag, and falls back to cited keyword results on any setup, refresh, or query failure. Raw source snapshots are not copied into the QMD projection. Set `VERITAS_QMD_KNOWLEDGE_EMBED=true` to embed changed knowledge projections.
|
||||
|
||||
## Collections
|
||||
|
||||
Retrieval searches these collections:
|
||||
|
|
|
|||
|
|
@ -32,15 +32,15 @@ This guide walks you through every self-hosting scenario — from running locall
|
|||
|
||||
| Requirement | Version | Install |
|
||||
| ----------- | ------- | ------------------------------------------------------------ |
|
||||
| Node.js | 22.0.0+ | https://nodejs.org or `nvm install 22` |
|
||||
| pnpm | 11.1.1+ | `corepack enable && corepack prepare pnpm@11.1.1 --activate` |
|
||||
| Git | any | https://git-scm.com |
|
||||
| Node.js | 22.22.1+ | https://nodejs.org or `nvm install 22` |
|
||||
| pnpm | 11.1.1 | `corepack enable && corepack prepare pnpm@11.1.1 --activate` |
|
||||
| Git | 2.38+ | https://git-scm.com |
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
node --version # v22.x.x
|
||||
pnpm --version # 11.x.x
|
||||
node --version # v22.22.1 or newer
|
||||
pnpm --version # 11.1.1
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -468,6 +468,10 @@ The `DATA_DIR=/app/data` volume holds all persistent data:
|
|||
└── logs/ # Application logs
|
||||
```
|
||||
|
||||
This tree is illustrative, not exhaustive. Veritas also stores workflows,
|
||||
runtime evidence, telemetry, provider records, and other governed domains under
|
||||
the same canonical root. Back up the entire stopped-writer volume.
|
||||
|
||||
**Without a named volume, data is lost on every `docker compose down`.** Always use a volume or bind mount.
|
||||
|
||||
For `VERITAS_STORAGE=sqlite`, persistence is not enough: the authoritative
|
||||
|
|
@ -614,8 +618,8 @@ Set `PROMETHEUS_METRICS_TOKEN` on the Veritas server to the same secret, or use
|
|||
|
||||
| Variable | Default | Description |
|
||||
| -------------------------- | -------------------- | ------------------------------------------------------- |
|
||||
| `VERITAS_DATA_DIR` | `.veritas-kanban` | Config, logs, internal state (relative to project root) |
|
||||
| `DATA_DIR` | `/app/data` (Docker) | Mapped data dir inside Docker container |
|
||||
| `VERITAS_DATA_DIR` | Project root | Storage root used when `DATA_DIR` is unset |
|
||||
| `DATA_DIR` | `/app/data` (Docker) | Preferred storage root; takes precedence |
|
||||
| `TELEMETRY_RETENTION_DAYS` | `30` | Days to keep telemetry event files |
|
||||
| `TELEMETRY_COMPRESS_DAYS` | `7` | Days after which telemetry files are gzip-compressed |
|
||||
|
||||
|
|
|
|||
|
|
@ -1239,7 +1239,7 @@
|
|||
<div class="hero-content">
|
||||
<div class="hero-badge fade-in">
|
||||
<span class="pulse"></span>
|
||||
v6.0.0 — Buzz and evidence-backed agent harnesses
|
||||
v6.0.2: Desktop recovery hotfix
|
||||
</div>
|
||||
<h1 class="fade-in fade-in-delay-1">Veritas Kanban</h1>
|
||||
<p class="tagline fade-in fade-in-delay-2">Task management built for AI agents — not against them</p>
|
||||
|
|
@ -1556,36 +1556,36 @@ curl -X POST .../tasks/<id>/comments \
|
|||
<div class="container">
|
||||
<div class="fade-in">
|
||||
<span class="section-label">What's New</span>
|
||||
<h2 class="section-title">v6.0.0 Harness Control Plane</h2>
|
||||
<h2 class="section-title">v6.0.2 Desktop Recovery And Support</h2>
|
||||
<span class="version-tag">🚀 Latest Release</span>
|
||||
</div>
|
||||
<div class="version-grid">
|
||||
<div class="version-item fade-in">
|
||||
<span class="version-item-icon">🧭</span>
|
||||
<span class="version-item-icon">💬</span>
|
||||
<div>
|
||||
<h3>Evidence-Backed Harness Support</h3>
|
||||
<p>Buzz, Grok Build, OpenAI Codex, Claude Code, and GitHub Copilot CLI use one reviewed support matrix, exact build evidence, and fail-closed dispatch.</p>
|
||||
<h3>Bounded Chat Workbench</h3>
|
||||
<p>Board Chat and Squad Chat default to a right dock, switch to Bottom without remounting, clamp to the viewport, and always preserve visible recovery controls.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version-item fade-in fade-in-delay-1">
|
||||
<span class="version-item-icon">🛡️</span>
|
||||
<span class="version-item-icon">ℹ️</span>
|
||||
<div>
|
||||
<h3>Run Authority And Safety</h3>
|
||||
<p>Immutable launch manifests, causal events, supervised runs, exact-action approvals, brokered credentials, and authoritative completion evidence stay provider-neutral.</p>
|
||||
<h3>Exact Native Version Information</h3>
|
||||
<p>About and Copy Version Information expose one authoritative version, release commit, channel, operating system, architecture, and packaged-state record.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version-item fade-in fade-in-delay-2">
|
||||
<span class="version-item-icon">📡</span>
|
||||
<span class="version-item-icon">🧭</span>
|
||||
<div>
|
||||
<h3>Native Buzz Integration</h3>
|
||||
<p>Signed Squad Chat bridging, safe persona and team import, generic ACP task execution, run-scoped MCP tools, and replay-safe workflow triggers.</p>
|
||||
<h3>Evidence-Backed Harness Support</h3>
|
||||
<p>Buzz, Grok Build, OpenAI Codex, Claude Code, and GitHub Copilot CLI retain one reviewed support matrix, exact build evidence, and fail-closed dispatch.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="version-item fade-in fade-in-delay-3">
|
||||
<span class="version-item-icon">🔌</span>
|
||||
<span class="version-item-icon">⚡</span>
|
||||
<div>
|
||||
<h3>ACP And Interactive Lifecycles</h3>
|
||||
<p>Generic ACP stdio support sits beside Codex app-server and Claude stream adapters, with capability-gated resume, fork, steer, interrupt, and close.</p>
|
||||
<h3>Proportional Verification</h3>
|
||||
<p>Ordinary changes run focused coverage, while shared, storage, manifest, desktop, high-risk, and milestone release changes retain the full gate.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1669,7 +1669,7 @@ curl -X POST .../tasks/<id>/comments \
|
|||
</div>
|
||||
<div class="proof-stats fade-in">
|
||||
<div class="proof-stat">
|
||||
<div class="proof-stat-number">v6.0.0</div>
|
||||
<div class="proof-stat-number">v6.0.2</div>
|
||||
<div class="proof-stat-label">Current source line</div>
|
||||
</div>
|
||||
<div class="proof-stat">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# MCP Server — Veritas Kanban
|
||||
|
||||
> **41 tools · 9 categories · stdio transport · zero external dependencies**
|
||||
> **42 tools · 9 categories · stdio transport · zero external dependencies**
|
||||
|
||||
The Veritas Kanban MCP server lets any [Model Context Protocol](https://modelcontextprotocol.io/) client — Claude Desktop, OpenClaw, Cursor, Cline, Codex, or your own tooling — manage tasks, sprints, projects, comments, agents, automation, notifications, and summaries through a single stdio process.
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ MCP is optional. The board, REST API, and CLI do not require MCP or OpenClaw. Us
|
|||
- [Configuration Reference](#configuration-reference)
|
||||
- [Tool Catalog](#tool-catalog)
|
||||
- [Task Management (6 tools)](#task-management-6-tools)
|
||||
- [Agent Control (2 tools)](#agent-control-2-tools)
|
||||
- [Agent Control (4 tools)](#agent-control-4-tools)
|
||||
- [Automation (4 tools)](#automation-4-tools)
|
||||
- [Notifications (3 tools)](#notifications-3-tools)
|
||||
- [Summaries (2 tools)](#summaries-2-tools)
|
||||
|
|
@ -45,7 +45,7 @@ MCP is optional. The board, REST API, and CLI do not require MCP or OpenClaw. Us
|
|||
Use the MCP server when:
|
||||
|
||||
- Your AI assistant (Claude Desktop, Cursor, etc.) needs **structured tool access** to VK — not raw HTTP calls.
|
||||
- You want **one process** that exposes all 36 VK operations with typed inputs and validated outputs.
|
||||
- You want **one process** that exposes all 42 VK operations with typed inputs and validated outputs.
|
||||
- You're building **agent orchestration** and need task/sprint/automation lifecycle management over MCP.
|
||||
|
||||
Don't use it when:
|
||||
|
|
@ -71,7 +71,7 @@ Don't use it when:
|
|||
│ ┌────────────┐ ┌────────────┐ ┌───────────┐ │
|
||||
│ │ Tool │ │ Resource │ │ Transport │ │
|
||||
│ │ Registry │ │ Provider │ │ (stdio) │ │
|
||||
│ │ (41 tools) │ │ (kanban:// │ │ │ │
|
||||
│ │ (42 tools) │ │ (kanban:// │ │ │ │
|
||||
│ │ │ │ URIs) │ │ │ │
|
||||
│ └──────┬─────┘ └──────┬─────┘ └───────────┘ │
|
||||
│ │ │ │
|
||||
|
|
@ -103,7 +103,7 @@ Don't use it when:
|
|||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js ≥ 22
|
||||
- Node.js ≥ 22.22.1
|
||||
- The Veritas Kanban server running (`pnpm dev` or production)
|
||||
- pnpm (for building from source)
|
||||
- No OpenClaw requirement unless OpenClaw is the MCP client or agent runner you choose
|
||||
|
|
@ -356,12 +356,13 @@ Task write tools return concise confirmations. Use `get_task`, `list_tasks`, or
|
|||
|
||||
---
|
||||
|
||||
### Agent Control (3 tools)
|
||||
### Agent Control (4 tools)
|
||||
|
||||
| Tool | Description | Required Inputs | Key Options |
|
||||
| ---------------------------- | ----------------------------------------- | --------------------------- | --------------------------------------------------------------------- |
|
||||
| `start_agent` | Start a coding agent on a task | `id` | `agent`; `requiredRuntimeCapabilities`; `commitPolicy` |
|
||||
| `stop_agent` | Stop a running agent | `id` | Resolves status and binds the stop to that exact attempt and manifest |
|
||||
| `cancel_agent_recovery` | Cancel a pending retry or fallback | `id`, `attemptId` | Requires the exact persisted parent attempt |
|
||||
| `control_agent_conversation` | Invoke a supported conversation lifecycle | `id`, `attemptId`, `action` | `message`, `forkTurnId`, `commitPolicy` |
|
||||
|
||||
> **Constraints:** Only works on tasks with `type: "code"` that already have a git worktree attached.
|
||||
|
|
@ -843,10 +844,10 @@ Configure telemetry retention in `server/.env`:
|
|||
|
||||
| Component | Version | Notes |
|
||||
| ------------------ | ------------ | --------------------------- |
|
||||
| MCP server package | `6.0.0` | Matches VK server version |
|
||||
| MCP server package | `6.1.2` | Matches VK server version |
|
||||
| MCP SDK | `1.29.0` | `@modelcontextprotocol/sdk` |
|
||||
| MCP protocol | `2025-11-25` | Latest stable spec |
|
||||
| Node.js | `≥ 22` | Matches the repo runtime |
|
||||
| Node.js | `≥ 22.22.1` | Matches the repo runtime |
|
||||
| TypeScript | `6.0+` | Build dependency only |
|
||||
|
||||
**Breaking change policy:**
|
||||
|
|
@ -887,4 +888,4 @@ The `findTask` utility matches the last N characters of a task ID (minimum 6). I
|
|||
|
||||
---
|
||||
|
||||
_Last updated: 2026-07-24 · VK v6.0.0 · 41 tools / 9 categories_
|
||||
_Last updated: 2026-08-24 · VK v6.1.2 · 42 tools / 9 categories_
|
||||
|
|
|
|||
102
docs/releases/v6.0.1.md
Normal file
102
docs/releases/v6.0.1.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
Veritas Kanban 6.0.1 is the first supported stable v6 release.
|
||||
|
||||
It adds first-class Buzz integration and gives Grok Build, OpenAI Codex, Claude Code, and GitHub Copilot CLI the same evidence-backed task, worktree, tool, approval, credential, event, and completion boundaries.
|
||||
|
||||
**Release status:** Veritas Kanban 6.0.0 remains available as a quarantined prerelease. Install 6.0.1 or newer.
|
||||
|
||||
## Highlights
|
||||
|
||||
### One control plane for agent harnesses
|
||||
|
||||
- Buzz Agent, Grok Build, and GitHub Copilot CLI run through the shared ACP v1 adapter.
|
||||
- OpenAI Codex supports CLI, SDK, and the richer app-server JSON-RPC lifecycle.
|
||||
- Claude Code runs through a supervised bare-mode stream.
|
||||
- Hermes and OpenClaw retain their supported one-shot and gateway transports.
|
||||
- Settings, API diagnostics, telemetry, dispatch, and `vk doctor --json` now report the same support tier and redacted readiness evidence.
|
||||
|
||||
Equal footing does not mean pretending every provider has the same features. Veritas probes the installed version and capabilities, then persists that evidence.
|
||||
|
||||
Unsupported resume, tool, approval, sandbox, or completion behavior is blocked before an attempt starts.
|
||||
|
||||
### First-class Buzz support
|
||||
|
||||
- Connect a pinned Buzz community channel to Squad Chat.
|
||||
- Import signed public persona and team definitions as disabled profiles.
|
||||
- Run `buzz-agent` through ACP with exact version and capability checks.
|
||||
- Expose only the selected run-scoped Veritas tools.
|
||||
- Trigger allowlisted Veritas workflows from Buzz root messages with durable replay protection.
|
||||
|
||||
Buzz relay delivery remains a communication path. Veritas remains the authority for tasks, attempts, tools, approvals, and completion.
|
||||
|
||||
### Safer, recoverable runs
|
||||
|
||||
- Every run records its task envelope, launch policy, worktree baseline, provider runtime evidence, causal events, approvals, tool catalog, and completion evidence.
|
||||
- Restart and reconnect logic verifies ownership before resuming work, avoiding silent duplicate runs.
|
||||
- Provider build or capability drift invalidates old certification.
|
||||
- Credential-bearing tools use one-shot, exact-action leases instead of exposing raw secrets to provider configuration or logs.
|
||||
- The desktop updater rejects older release metadata instead of offering a downgrade.
|
||||
|
||||
## Stabilization fixes in 6.0.1
|
||||
|
||||
- Chat is a reversible desktop panel with visible close, Escape, browser Back, startup-state recovery, and native layout reset ([#945](https://github.com/BradGroux/veritas-kanban/issues/945)).
|
||||
- Task drawers, shared overlays, Archive cards, scoring pages, and the template editor have reachable scrolling and resizing ([#935](https://github.com/BradGroux/veritas-kanban/issues/935), [#938](https://github.com/BradGroux/veritas-kanban/issues/938), [#939](https://github.com/BradGroux/veritas-kanban/issues/939), [#941](https://github.com/BradGroux/veritas-kanban/issues/941)).
|
||||
- Workflow actions handle omitted collections and recoverable load failures instead of crashing task detail ([#936](https://github.com/BradGroux/veritas-kanban/issues/936)).
|
||||
- Full-page views, task detail, and nested Workflow overlays preserve their actual route origin, browser history, keyboard Back behavior, and scroll position ([#937](https://github.com/BradGroux/veritas-kanban/issues/937)).
|
||||
- New scoring profiles open as visible, validated drafts ([#943](https://github.com/BradGroux/veritas-kanban/issues/943)).
|
||||
- Operations Digest reconciles board inventory with windowed events and exposes source IDs and metadata-quality findings ([#944](https://github.com/BradGroux/veritas-kanban/issues/944)).
|
||||
- The packaged desktop consistently reports its real application version ([#986](https://github.com/BradGroux/veritas-kanban/issues/986)).
|
||||
|
||||
## Install or upgrade
|
||||
|
||||
### Homebrew
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
For a first install:
|
||||
|
||||
```bash
|
||||
brew install --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
### Direct download
|
||||
|
||||
Download the signed and notarized macOS arm64 DMG or ZIP from the [v6.0.1 release](https://github.com/BradGroux/veritas-kanban/releases/tag/v6.0.1).
|
||||
|
||||
Back up the current workspace before upgrading. Keep the v5.2.5 backup until the v6 runtime is accepted.
|
||||
|
||||
If rollback requires an older schema, restore the pre-upgrade backup instead of opening newer data with an incompatible binary.
|
||||
|
||||
Follow the [v6 upgrade and administration guide](https://github.com/BradGroux/veritas-kanban/blob/v6.0.1/docs/V6-UPGRADE-INSTALL-ADMIN-GUIDE.md) for migration, remote access, validation, and rollback.
|
||||
|
||||
## Important behavior changes
|
||||
|
||||
- Provider-less or adapter/profile-mismatched records no longer fall through to OpenClaw.
|
||||
- Unknown or changed provider builds lose certification until current probes and deterministic fixtures pass.
|
||||
- Claude Code no longer launches with `--dangerously-skip-permissions`.
|
||||
- Credential-bound MCP servers are not copied into provider-native configuration. They are available only through the mediated run bridge.
|
||||
- Conversation controls appear only when the selected provider proves it supports them.
|
||||
- The public REST API remains mounted at `v1`; the v6 product version does not rename API routes.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- Buzz Agent sessions are in-memory and do not support session load/resume. Buzz files, reactions, forums, DMs, and destructive edit/delete projection are not bridged.
|
||||
- GitHub Copilot CLI ACP is public preview.
|
||||
- Grok Build's stable artifact self-reports alpha and cannot be fully traced to the current public source tree.
|
||||
- Claude Code's complete CLI implementation is not public, so certification is bound to exact release behavior and checked-in fixtures.
|
||||
- Linux and Windows desktop artifacts remain unsigned previews. Signed and notarized macOS arm64 is the supported desktop distribution.
|
||||
- Deterministic compatibility does not prove provider authentication, subscription availability, quota, or live inference.
|
||||
|
||||
## Documentation and evidence
|
||||
|
||||
- [Agent guide and reusable `AGENTS.md` template](https://github.com/BradGroux/veritas-kanban/blob/main/docs/AGENTS-TEMPLATE.md)
|
||||
- [Agent provider setup and operations](https://github.com/BradGroux/veritas-kanban/blob/v6.0.1/docs/AGENT-PROVIDERS.md)
|
||||
- [Harness compatibility matrix](https://github.com/BradGroux/veritas-kanban/blob/v6.0.1/docs/HARNESS-COMPATIBILITY.md)
|
||||
- [Buzz integration guide](https://github.com/BradGroux/veritas-kanban/blob/v6.0.1/docs/BUZZ-INTEGRATION.md)
|
||||
- [v6 runtime architecture](https://github.com/BradGroux/veritas-kanban/blob/v6.0.1/docs/architecture/V6-AGENT-RUNTIME-CONTROL-PLANE.md)
|
||||
- [Release evidence packet](https://github.com/BradGroux/veritas-kanban/blob/main/docs/V6-RC-EVIDENCE-PACKET.md)
|
||||
- [Release tracker #924](https://github.com/BradGroux/veritas-kanban/issues/924)
|
||||
- [Buzz epic #904](https://github.com/BradGroux/veritas-kanban/issues/904)
|
||||
- [Equal-footing harness epic #915](https://github.com/BradGroux/veritas-kanban/issues/915)
|
||||
30
docs/releases/v6.0.2.md
Normal file
30
docs/releases/v6.0.2.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
Veritas Kanban 6.0.2 is the supported stable v6 release. It fixes the critical desktop regressions tracked in [#1004](https://github.com/BradGroux/veritas-kanban/issues/1004) and [#1005](https://github.com/BradGroux/veritas-kanban/issues/1005), adds the proportional verification policy from [#1000](https://github.com/BradGroux/veritas-kanban/issues/1000), and supersedes v6.0.1. Version 6.0.0 remains a quarantined prerelease.
|
||||
|
||||
## What changed
|
||||
|
||||
Board Chat and Squad Chat now stay mounted in a reversible Workbench dock, preserve the conversation when switching between Right and Bottom, keep dock dimensions and scrolling bounded, and return to a visible board after Close, Escape, browser Back, or Reset Layout. About Veritas Kanban and Copy Version Information now share one authoritative offline support record for the app version, release commit, channel, operating system, architecture, and packaged state. CI verification now scales with the change: documentation-only changes skip workspace suites, ordinary code changes run affected Vitest coverage, and shared, storage, manifest, desktop, high-risk, or release changes retain the full gate. Full-suite evidence is reused only when its exact tested head is an ancestor of the resulting commit.
|
||||
|
||||
## Install or upgrade
|
||||
|
||||
Upgrade an existing Homebrew installation:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
Install for the first time:
|
||||
|
||||
```bash
|
||||
brew install --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
The Assets section provides the signed and notarized macOS arm64 DMG and ZIP. Back up the current workspace before upgrading and retain the backup until the new runtime is accepted.
|
||||
|
||||
## Compatibility and support
|
||||
|
||||
No data-schema migration is required when upgrading from 6.0.1, and the public REST API remains mounted at `v1`. Buzz, Grok Build, OpenAI Codex, Claude Code, GitHub Copilot CLI, Hermes, and OpenClaw support contracts are unchanged. Signed and notarized macOS arm64 is the supported desktop distribution; Linux and Windows artifacts remain unsigned verification previews.
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [release notes](https://github.com/BradGroux/veritas-kanban/blob/v6.0.2/docs/V6-RELEASE-NOTES.md), [upgrade guide](https://github.com/BradGroux/veritas-kanban/blob/v6.0.2/docs/V6-UPGRADE-INSTALL-ADMIN-GUIDE.md), [harness matrix](https://github.com/BradGroux/veritas-kanban/blob/v6.0.2/docs/HARNESS-COMPATIBILITY.md), [Buzz guide](https://github.com/BradGroux/veritas-kanban/blob/v6.0.2/docs/BUZZ-INTEGRATION.md), [evidence packet](https://github.com/BradGroux/veritas-kanban/blob/v6.0.2/docs/V6-RC-EVIDENCE-PACKET.md), and [release issue #1010](https://github.com/BradGroux/veritas-kanban/issues/1010).
|
||||
44
docs/releases/v6.1.0.md
Normal file
44
docs/releases/v6.1.0.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
Veritas Kanban 6.1.0 completes the agentic-control roadmap that followed the first stable v6 release. It gives Buzz, Grok Build, OpenAI Codex, Claude Code, GitHub Copilot CLI, Hermes, and OpenClaw one provider-neutral control plane while preserving explicit transport and capability differences. It also adds governed knowledge collections, durable execution supervision, run-scoped network enforcement, safe workspace rewind, and resilient output handling.
|
||||
|
||||
## Agent harness control plane
|
||||
|
||||
Every supported harness is discovered, diagnosed, dispatched, observed, and completed through the same support-profile, runtime-manifest, launch-manifest, approval, tool, credential, sandbox, phase-authority, and completion contracts. Veritas probes exact provider builds and capabilities rather than treating provider names as proof. Unsupported controls fail before attempt creation, and unknown or changed builds invalidate cached conformance instead of silently falling through to another adapter.
|
||||
|
||||
Buzz Agent and Grok Build use ACP transports with exact handshake and capability evidence. Codex CLI, SDK, and app-server retain their distinct supervised lifecycles. Claude Code runs through its strict bare-mode stream, GitHub Copilot CLI remains bounded to its public-preview ACP contract, Hermes retains one-shot execution, and OpenClaw retains explicit gateway policy. Buzz relay communication remains separate from execution authority: Buzz transports signed messages while Veritas owns tasks, attempts, tools, approvals, and completion.
|
||||
|
||||
## Governed execution
|
||||
|
||||
Run-scoped egress enforcement now resolves and pins allowed destinations, applies protocol, host, port, method, and normalized path rules, and records redacted decision evidence. Durable admission control applies capacity, budgets, fairness, cancellation, and circuit breaking to direct tasks, workflows, retries, fallbacks, continuations, child agents, and provider handoffs through one queue and execution-tree contract.
|
||||
|
||||
Append-only admission records now complete each serialized write before syncing, preventing short filesystem writes from truncating durable reservation state. Knowledge-collection routes also use the exact shared permission prefix so client discovery and server enforcement remain in fail-closed parity.
|
||||
|
||||
Durable goals survive turns, restarts, and provider continuations without inventing completion. Background commands and monitors are supervisor-owned, repetitive or stalled runs receive bounded recovery, oversized output spills into governed artifacts, and dependency-aware load shedding prevents unhealthy agent trees from amplifying pressure.
|
||||
|
||||
## Knowledge and workspace safety
|
||||
|
||||
Workspace knowledge collections now support classified immutable sources, cited derived pages, reviewed ingestion, atomic apply and reversal, scoped search, query promotion, and cited export with file and SQLite parity. Deterministic integrity linting finds graph, schema, provenance, freshness, citation, canonical-term, contradiction, duplication, supersession, and evidence gaps. Material claims have attributable, evidence-linked, reversible lifecycle controls so conflicts remain visible and reviewable.
|
||||
|
||||
Turn-boundary checkpoints capture run-owned Git, index, file, exclusion, ownership, conversation, and attributable provider-diff state. Rewind is preview-first, digest-bound, conflict-aware, and limited to selected paths; ambiguous attribution, external edits, unsupported providers, and stale evidence fail closed.
|
||||
|
||||
## Install or upgrade
|
||||
|
||||
Back up the complete stopped-writer workspace before upgrading and keep the backup until the new runtime is accepted. Version 6.1.0 advances SQLite through migrations 30 to 33 for knowledge collections and integrity operations. The public REST API remains mounted at `v1`.
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
For a first installation:
|
||||
|
||||
```bash
|
||||
brew install --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
The Assets section provides the signed and notarized macOS arm64 DMG and ZIP after the release workflow completes. Linux and Windows artifacts remain unsigned verification previews.
|
||||
|
||||
## Compatibility and documentation
|
||||
|
||||
Provider support is evidence-bound, not feature-parity theater. Authentication, subscription availability, quota, and live inference remain external runtime facts even when deterministic compatibility passes. GitHub Copilot CLI ACP remains public preview; Buzz session resume and several relay projections remain unsupported; Grok Build and Claude Code certification remains pinned to exact reviewed release behavior.
|
||||
|
||||
See the [full release notes](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/docs/V6-RELEASE-NOTES.md), [upgrade guide](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/docs/V6-UPGRADE-INSTALL-ADMIN-GUIDE.md), [harness matrix](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/docs/HARNESS-COMPATIBILITY.md), [provider guide](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/docs/AGENT-PROVIDERS.md), [agent template](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/docs/AGENTS-TEMPLATE.md), [Buzz guide](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/docs/BUZZ-INTEGRATION.md), and [changelog](https://github.com/BradGroux/veritas-kanban/blob/v6.1.0/CHANGELOG.md).
|
||||
40
docs/releases/v6.1.1.md
Normal file
40
docs/releases/v6.1.1.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
Veritas Kanban 6.1.1 restores reliable scrolling in long Task Detail drawers after the Mantine tabs migration and completes a security-audited dependency maintenance pass. It is a drop-in patch release for 6.1.0 with no schema, API, configuration, or migration changes.
|
||||
|
||||
## Task Detail scrolling
|
||||
|
||||
Long Task Detail content is constrained to the available drawer height again, and the shared overlay scroll region receives wheel input normally. A Chromium regression now verifies the Mantine tabs root layout, real content overflow, and an actual scroll-position change so this contract is covered beyond component-level rendering.
|
||||
|
||||
Interactive controls inside task cards no longer activate the card itself. This restores touch status selection in WebKit after the Mantine 9.5 update. Status-move browser checks now wait for the visible save contract before closing Task Detail, matching the user-facing lifecycle instead of racing the completed network response.
|
||||
|
||||
File-backed workflow operations now wait for their storage directory to be ready before reading or writing. This removes a startup race that could make an immediate first workflow request fail with `ENOENT`.
|
||||
|
||||
Thanks to Matt Ezell for reporting the regression and contributing the focused CSS correction in #1153 and #1154.
|
||||
|
||||
## Dependency maintenance and security
|
||||
|
||||
The workspace minor and patch dependency set is current as of this release, with Chalk 6 adopted separately after runtime compatibility review. Stale transitive override floors for body-parser, fast-uri, js-yaml, nanoid, tar, undici, and related packages were refreshed. Both production-only and full `pnpm audit` checks complete with no known vulnerabilities.
|
||||
|
||||
The jsdom 30 proposal was intentionally not included because it requires Node.js 22.22.2 while this release retains the documented Node.js 22.22.1 floor, and it caused 65 changed-test failures. Dependabot will continue offering jsdom 29 patches but defer major updates until the runtime floor and UI environment are deliberately migrated. The floating `pnpm/action-setup@v6` workflow reference already resolves to 6.0.9, so no workflow edit was necessary.
|
||||
|
||||
## Install or upgrade
|
||||
|
||||
Back up the complete stopped-writer workspace before upgrading and keep the backup until the new runtime is accepted.
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
For a first installation:
|
||||
|
||||
```bash
|
||||
brew install --cask bradgroux/tap/veritas-kanban
|
||||
```
|
||||
|
||||
The Assets section provides the signed and notarized macOS arm64 DMG and ZIP after the release workflow completes. Linux and Windows artifacts remain unsigned verification previews.
|
||||
|
||||
## Compatibility and documentation
|
||||
|
||||
Version 6.1.1 retains the 6.1.0 storage schema, public REST API `v1`, provider contracts, configuration, and supported runtime policy. No data migration is required when upgrading from 6.1.0.
|
||||
|
||||
See the [full release notes](https://github.com/BradGroux/veritas-kanban/blob/v6.1.1/docs/V6-RELEASE-NOTES.md), [upgrade guide](https://github.com/BradGroux/veritas-kanban/blob/v6.1.1/docs/V6-UPGRADE-INSTALL-ADMIN-GUIDE.md), [compatibility policy](https://github.com/BradGroux/veritas-kanban/blob/v6.1.1/docs/V6-COMPATIBILITY-AND-RELEASE-POLICY.md), and [changelog](https://github.com/BradGroux/veritas-kanban/blob/v6.1.1/CHANGELOG.md).
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue