mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-08-28 10:54:59 +00:00
Compare commits
No commits in common. "main" and "v5.2.5" have entirely different histories.
856 changed files with 9734 additions and 186386 deletions
|
|
@ -7,11 +7,6 @@ node_modules
|
|||
server/dist
|
||||
web/dist
|
||||
shared/dist
|
||||
desktop/.desktop-release
|
||||
desktop/release
|
||||
.veritas-desktop-dev
|
||||
playwright-report
|
||||
test-results
|
||||
|
||||
# Git
|
||||
.git
|
||||
|
|
|
|||
|
|
@ -62,11 +62,6 @@ 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,16 +2,6 @@
|
|||
|
||||
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)
|
||||
|
|
@ -25,15 +15,6 @@ work that was intentionally kept out of this PR, or write `None`.
|
|||
**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
|
||||
|
|
@ -42,13 +23,9 @@ modes, and why broader gates are or are not required.
|
|||
|
||||
## 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 or updated coverage for the next declared test milestone
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] 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,12 +12,6 @@ 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:
|
||||
|
|
|
|||
450
.github/workflows/ci.yml
vendored
450
.github/workflows/ci.yml
vendored
|
|
@ -5,157 +5,25 @@ on:
|
|||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
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: >-
|
||||
${{
|
||||
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'
|
||||
)
|
||||
}}
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@v6
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -166,9 +34,6 @@ 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
|
||||
|
||||
|
|
@ -184,320 +49,42 @@ jobs:
|
|||
- name: Type check all packages
|
||||
run: pnpm typecheck
|
||||
|
||||
# ─── Focused Related Tests ──────────────────────────────────────
|
||||
test-changed:
|
||||
name: Changed Tests
|
||||
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 }}
|
||||
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@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
|
||||
- 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 related tests in affected workspaces
|
||||
if: needs.select-tests.outputs.scope == 'focused'
|
||||
env:
|
||||
DIFF_RANGE: ${{ needs.select-tests.outputs.diff_range }}
|
||||
SELECTED_PACKAGES: ${{ needs.select-tests.outputs.packages }}
|
||||
VERITAS_DISABLE_WATCHERS: '1'
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
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:
|
||||
SELECTED_SCOPE: ${{ needs.select-tests.outputs.scope }}
|
||||
SELECTION_REASON: ${{ needs.select-tests.outputs.reason }}
|
||||
run: |
|
||||
{
|
||||
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 ───────────────────────────────────
|
||||
# ─── Workspace Unit Tests ────────────────────────────────────────
|
||||
test-workspace:
|
||||
name: Workspace Unit Tests
|
||||
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 }}
|
||||
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: actions/checkout@v7
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
if: needs.select-tests.outputs.scope == 'full'
|
||||
- uses: pnpm/action-setup@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'
|
||||
- uses: actions/setup-node@v6
|
||||
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
|
||||
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"
|
||||
run: pnpm --filter @veritas-kanban/server test -- src/__tests__/storage/dual-storage-parity.test.ts
|
||||
|
||||
# ─── 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@v6
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -508,9 +95,6 @@ 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
|
||||
|
||||
|
|
@ -545,17 +129,13 @@ 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@v6
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
|
|||
74
.github/workflows/desktop-artifacts.yml
vendored
74
.github/workflows/desktop-artifacts.yml
vendored
|
|
@ -3,7 +3,18 @@ name: Desktop Artifacts
|
|||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
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'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
|
|
@ -16,25 +27,13 @@ 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:
|
||||
- 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: 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@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -55,7 +54,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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: veritas-kanban-mac-unsigned
|
||||
path: |
|
||||
|
|
@ -68,25 +67,13 @@ 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:
|
||||
- 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: 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@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -110,7 +97,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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: veritas-kanban-linux-unsigned
|
||||
path: |
|
||||
|
|
@ -124,26 +111,13 @@ 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:
|
||||
- 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: 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@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -164,7 +138,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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: veritas-kanban-windows-unsigned
|
||||
path: |
|
||||
|
|
|
|||
15
.github/workflows/desktop-release.yml
vendored
15
.github/workflows/desktop-release.yml
vendored
|
|
@ -24,7 +24,6 @@ concurrency:
|
|||
|
||||
env:
|
||||
NODE_VERSION: '22'
|
||||
VERITAS_BUILD_SHA: ${{ github.sha }}
|
||||
VERITAS_UPDATE_CHANNEL: ${{ github.event.inputs.channel || 'stable' }}
|
||||
|
||||
jobs:
|
||||
|
|
@ -32,23 +31,15 @@ jobs:
|
|||
name: Signed and Notarized macOS Artifact
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||||
- uses: pnpm/action-setup@v6
|
||||
|
||||
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
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
39
.github/workflows/docker-image.yml
vendored
|
|
@ -1,39 +0,0 @@
|
|||
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,9 +1,6 @@
|
|||
name: Scheduled QA
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
types: [opened, synchronize, reopened, labeled, unlabeled]
|
||||
schedule:
|
||||
- cron: '17 8 * * 1'
|
||||
workflow_dispatch:
|
||||
|
|
@ -39,26 +36,14 @@ 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:
|
||||
- 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: 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@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -85,7 +70,7 @@ jobs:
|
|||
|
||||
- name: Upload Playwright artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: playwright-artifacts
|
||||
path: |
|
||||
|
|
@ -96,29 +81,16 @@ 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:
|
||||
- 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: 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@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: pnpm
|
||||
|
|
@ -193,7 +165,7 @@ jobs:
|
|||
|
||||
- name: Upload k6 artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: k6-artifacts
|
||||
path: |
|
||||
|
|
|
|||
65
.github/workflows/security.yml
vendored
65
.github/workflows/security.yml
vendored
|
|
@ -1,65 +0,0 @@
|
|||
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
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -44,8 +44,10 @@ tasks/archive/*.md
|
|||
tasks/backlog/*.md
|
||||
tasks/attachments/
|
||||
tasks/archive-attachments/
|
||||
/storage/
|
||||
/server/storage/
|
||||
storage/
|
||||
server/storage/
|
||||
!server/src/storage/
|
||||
!server/src/storage/**
|
||||
.veritas-kanban/*
|
||||
!.veritas-kanban/.gitkeep
|
||||
.veritas-desktop-dev/
|
||||
|
|
@ -66,7 +68,6 @@ coverage/
|
|||
/blob-report/
|
||||
/playwright/.cache/
|
||||
.veritas-kanban/worktrees/
|
||||
.veritas-kanban/worktree-manifests/
|
||||
|
||||
# TypeScript build info
|
||||
*.tsbuildinfo
|
||||
|
|
@ -110,7 +111,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,53 +1,22 @@
|
|||
# CLI snapshot uses an intentionally synthetic API key in serialized output.
|
||||
cli/src/__tests__/snapshot.test.ts:generic-api-key:220
|
||||
# Gitleaks False Positives
|
||||
# Updated: 2026-01-29 (post-history-rewrite)
|
||||
# All entries below are placeholder/example/test values, NOT real secrets.
|
||||
|
||||
# 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: "your-admin-key" placeholder in deployment guide
|
||||
39423f74cf3849684e8de4ebf746156a6be0ea00:docs/DEPLOYMENT.md:curl-auth-header:557
|
||||
|
||||
# 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
|
||||
# Documentation example: "dev-admin-key" placeholder in security audit
|
||||
f01a0157f1d1612ab7bea646cf7265ec018e1d9a:docs/SECURITY_AUDIT_2026-01-28.md:curl-auth-header:135
|
||||
|
||||
# Demo seeding passes the operator-provided key variable to curl.
|
||||
seed-demo-data.sh:curl-auth-header:45
|
||||
# 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
|
||||
|
||||
# Environment template documents shell-variable authentication examples.
|
||||
server/.env.example:curl-auth-header:127
|
||||
server/.env.example:curl-auth-header:130
|
||||
# .env.example placeholder values ("your-api-key")
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:server/.env.example:curl-auth-header:43
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:server/.env.example:curl-auth-header:46
|
||||
|
||||
# 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
|
||||
# Documentation placeholder values ("your-api-key")
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:docs/security.md:curl-auth-header:40
|
||||
55c742c2dfd731069505e1baab16bb4c328234bd:docs/security.md:curl-auth-header:47
|
||||
|
|
|
|||
|
|
@ -1,5 +1,2 @@
|
|||
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 scan the reviewed tree without the pre-commit framework:
|
||||
# gitleaks dir . --redact=100
|
||||
# Or standalone gitleaks hook (no pre-commit framework needed):
|
||||
# gitleaks protect --staged --verbose
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: 83d9cd684c87d95d656c1458ef04895a7f1cbd8e # v8.30.1
|
||||
rev: v8.21.2
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
|
|
|
|||
376
AGENTS.md
376
AGENTS.md
|
|
@ -1,12 +1,10 @@
|
|||
# AGENTS.md — Canonical Agent Instructions for Veritas Kanban
|
||||
|
||||
> **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.
|
||||
> **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.
|
||||
>
|
||||
> **Version:** 6.1.2
|
||||
> **Version:** 5.2.5
|
||||
> **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.
|
||||
|
|
@ -37,7 +35,7 @@ veritas-kanban/
|
|||
├── mcp/ MCP server
|
||||
├── desktop/ Electron desktop wrapper
|
||||
├── docs/ Operator and developer documentation
|
||||
├── prompt-registry/ Prompt templates and optional review workflows
|
||||
├── prompt-registry/ Prompt templates and cross-model review SOPs
|
||||
└── .veritas-kanban/ Runtime data: agent-registry, logs, telemetry
|
||||
```
|
||||
|
||||
|
|
@ -58,10 +56,9 @@ pnpm build
|
|||
pnpm dev
|
||||
|
||||
# Tests
|
||||
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
|
||||
pnpm test # Vitest across server, web, mcp, cli
|
||||
pnpm test:unit # Per-workspace tests sequentially
|
||||
pnpm test:e2e # Playwright end-to-end
|
||||
|
||||
# Type check (builds shared first)
|
||||
pnpm typecheck
|
||||
|
|
@ -71,18 +68,8 @@ 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
|
||||
```
|
||||
|
||||
Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise, resolve with
|
||||
|
|
@ -90,67 +77,6 @@ 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)
|
||||
|
|
@ -158,9 +84,6 @@ 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,
|
||||
|
|
@ -190,116 +113,16 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
- All cross-package types live in `shared/src/types/`.
|
||||
- `AgentProvider` union is the single definition consumed by both server and web.
|
||||
**Currently supported providers:**
|
||||
`openclaw` | `codex-cli` | `codex-sdk` | `codex-app-server` | `codex-cloud` |
|
||||
`claude-code` | `acp-stdio` | `hermes-cli` | `ollama-local` | `ollama-cloud` |
|
||||
`lm-studio-local` | `custom`
|
||||
`openclaw` | `codex-cli` | `codex-sdk` | `codex-cloud` | `hermes-cli` |
|
||||
`ollama-local` | `ollama-cloud` | `lm-studio-local` | `custom`
|
||||
- Executable task adapters are currently `openclaw`, `codex-cli`, `codex-sdk`,
|
||||
`codex-app-server`, `claude-code`, `acp-stdio`, and `hermes-cli`. Explicitly
|
||||
configured providers outside that set must fail closed; never route them
|
||||
through an implicit OpenClaw fallback.
|
||||
and `hermes-cli`. Explicitly configured providers outside that set must fail
|
||||
closed; never route them through an implicit OpenClaw fallback.
|
||||
- Probe and persist `provider-runtime-manifest/v1` before mutating attempt state.
|
||||
New runtime controls must use the persisted evidence instead of provider-name
|
||||
checks, and provider version/build changes must invalidate cached conformance.
|
||||
Increment `PROVIDER_RUNTIME_PROBE_REVISION` whenever probe semantics or the
|
||||
built-in adapter capability evidence changes.
|
||||
- Normalize every configured harness through `harness-support-profile/v1`.
|
||||
Settings, API diagnostics, `vk doctor`, dispatch, and telemetry must use the
|
||||
same support tier and redacted readiness evidence. Only known legacy records
|
||||
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
|
||||
mediated invocation issues exact-action leases using the server-owned launch
|
||||
manifest digest. Credential-bound sessions are one-shot and raw values may
|
||||
exist only inside the controlled downstream dispatch callback.
|
||||
- Providers access credential-bound tools only through the system-owned
|
||||
`veritas-run` MCP bridge and an opaque in-memory run handle. Codex CLI/SDK,
|
||||
Codex app-server, Claude Code, and ACP stdio inject this shared contract;
|
||||
Hermes and OpenClaw fail closed until their certified transports can enforce
|
||||
it.
|
||||
- Classify launch credentials through `run-launch-credential-plan/v1`.
|
||||
Provider boot authentication, task integration definition IDs, and explicit
|
||||
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
|
||||
cursor; and record a typed recovery action instead of starting duplicate work
|
||||
or signaling an unverified process.
|
||||
- Resolve selected MCP servers through `tool-server-definition/v1` and persist
|
||||
an immutable `run-tool-catalog/v1` before provider dispatch. Required
|
||||
discovery failures block launch; optional failures remain visible and
|
||||
audited.
|
||||
- Native provider configuration may expose only tools with an `allow`
|
||||
decision. Approval-required tools must use the Veritas-mediated
|
||||
`call_run_tool` path so the exact action hash is approved before dispatch.
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -332,90 +155,8 @@ Do not run `npm install`, `yarn`, or `bun install`. If lockfile conflicts arise,
|
|||
|
||||
- `codex-cli`: `codex exec --sandbox workspace-write --json`
|
||||
- `codex-sdk`: programmatic SDK, requires `@openai/codex-sdk`
|
||||
- `codex-app-server`: pinned to `codex-cli 0.145.0`; supervised JSON-RPC v2 over
|
||||
strict stdio for one task-bound thread and turn.
|
||||
- App-server launch arguments are system-owned. Inherited MCP servers, hooks,
|
||||
plugins, apps, browser/computer tools, and remote control remain disabled.
|
||||
Selected run-scoped MCP servers are injected only through the immutable
|
||||
catalog's thread configuration.
|
||||
- App-server consumes only the checked-in v0.145.0 schemas and exposes
|
||||
`initialize`, thread start/resume/fork/compact/archive, and turn
|
||||
start/steer/interrupt. `thread/shellCommand` is never reachable.
|
||||
- `conversation-lifecycle/v1` persists opaque thread, turn, item, parent, and
|
||||
fork identities. Resume and fork validate the source launch manifest,
|
||||
provider/model/policy, base revision, and worktree compatibility before a new
|
||||
attempt is created.
|
||||
- App-server command, file, permission, tool-question, and elicitation requests
|
||||
use `run-approval/v1`. Decisions must preserve the persisted revision and
|
||||
exact action hash; interruption and cancellation invalidate pending requests.
|
||||
- Auth: `codex login status` / `OPENAI_API_KEY`
|
||||
|
||||
### Claude Code (v2.1.218)
|
||||
|
||||
- Provider ID: `claude-code`. Default command: `claude`.
|
||||
- Veritas launches `claude --bare --print --output-format stream-json` with
|
||||
static sandbox-derived permissions and no shell.
|
||||
- Bare mode requires explicit environment authentication. OAuth/keychain state
|
||||
reported by `claude auth status` does not prove bare-mode readiness.
|
||||
- The terminal `result` record is authoritative. Veritas drains stdout after
|
||||
process close, persists `session_id`, and maps partial, hook, tool, subagent,
|
||||
usage, cost, and result records into `run-event/v1`.
|
||||
- Resume uses the exact persisted session through system-owned `--resume`.
|
||||
Native history fork adds `--fork-session`; caller-supplied lifecycle flags
|
||||
remain prohibited. Run-scoped MCP uses a system-owned strict config and
|
||||
exposes only catalog tools with an `allow` decision.
|
||||
- The shared approval broker is available, but Claude stays on static
|
||||
`dontAsk` permissions until its adapter exposes a pinned interactive
|
||||
request/response contract.
|
||||
|
||||
### Agent Client Protocol (ACP v1)
|
||||
|
||||
- Provider ID: `acp-stdio`. Configure the exact ACP agent command and arguments.
|
||||
- Veritas launches the agent without a shell in the task worktree and negotiates
|
||||
stable ACP protocol version 1 before attempt mutation.
|
||||
- Capability evidence comes from `initialize`; resume/load, fork, and close fail
|
||||
closed when the runtime does not advertise them.
|
||||
- `session/update` records enter the causal run journal.
|
||||
`session/request_permission` uses the durable approval broker.
|
||||
- Only immutable all-allow MCP server catalogs can be passed natively because
|
||||
ACP v1 has no per-tool allowlist. Profiles may explicitly require the
|
||||
system-owned `veritas-run` bridge for mediated catalogs; otherwise partial
|
||||
native catalogs fail closed.
|
||||
- The built-in `buzz-agent` profile remains provider `acp-stdio`, pins Buzz
|
||||
`v0.4.24` at commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`, and rejects
|
||||
`buzz-acp`, version drift, session loading, and network MCP claims. Selected
|
||||
run tools are delivered only through the opaque, attempt-bound
|
||||
`veritas-run` bridge.
|
||||
- The built-in `copilot` profile remains provider `acp-stdio`, pins Copilot CLI
|
||||
`v1.0.74`, owns the stdio safety argv, rejects broad allow/remote/TCP/config
|
||||
injection, and records public-preview plus incomplete-source limitations.
|
||||
- The built-in `grok-build` profile remains provider `acp-stdio`, pins Grok
|
||||
Build `v0.2.111` build `94172f2aa4e5`, launches `grok agent --no-leader
|
||||
stdio`, and rejects approval bypass, reauthentication, leader, plugin,
|
||||
endpoint, prompt, and resume argument injection.
|
||||
- Harness certification uses `harness-conformance-suite/v1`; run the committed
|
||||
mock lane with `pnpm --filter @veritas-kanban/server exec tsx
|
||||
src/scripts/run-harness-conformance.ts -- --suite <suite.json>
|
||||
--observations <observations.json>`. Credential-gated lanes require explicit
|
||||
opt-in and never commit raw provider output or secrets.
|
||||
- Cross-harness compatibility is published as
|
||||
`harness-compatibility-matrix/v1`. API, `vk doctor`, Settings, telemetry, and
|
||||
`docs/HARNESS-COMPATIBILITY.md` must use the reviewed profile capability
|
||||
digest, fixture revision, invalidation policy, and source caveats rather than
|
||||
defining provider-specific tiers.
|
||||
- Runtime extensions use the in-process `runtime-hook/v1` bus. Only documented
|
||||
pre-events may deny, post-events remain passive, and arbitrary executable or
|
||||
HTTP handlers stay unsupported until their filesystem and egress boundaries
|
||||
are enforceable. See `docs/architecture/RUNTIME-HOOK-V1.md`.
|
||||
- `vk acp serve --stdio` exposes one Veritas-managed task as an ACP v1 server
|
||||
view for editors and other ACP clients. Bind with `--task` or require
|
||||
`_meta["veritas/taskId"]` on `session/new`; client-owned MCP catalogs fail
|
||||
closed.
|
||||
- ACP client disconnect never stops the durable Veritas run. Reconnect with
|
||||
`session/load` and `_meta["veritas/afterSequence"]`; cancellation uses the
|
||||
conversation interrupt path, not task termination.
|
||||
- See `docs/AGENT-PROVIDERS.md` § ACP stdio agent provider.
|
||||
|
||||
---
|
||||
|
||||
## Security boundaries
|
||||
|
|
@ -424,15 +165,7 @@ src/scripts/run-harness-conformance.ts -- --suite <suite.json>
|
|||
- **Input validation.** All user input is validated with Zod schemas before processing.
|
||||
- **Path traversal.** `validatePathSegment()` + `ensureWithinBase()` on every user-supplied path.
|
||||
- **Env passthrough.** Agents receive only the keys in the configured safe allowlist; see
|
||||
`server/src/utils/codex-env.ts`, `server/src/utils/hermes-env.ts`, and
|
||||
`server/src/services/claude-code-adapter.ts` plus
|
||||
`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.
|
||||
`server/src/utils/codex-env.ts` and `server/src/utils/hermes-env.ts`.
|
||||
- **Log redaction.** Trace logs and telemetry run through `TRACE_SECRET_PATTERNS` before storage.
|
||||
- **No credentials in PR descriptions, test fixtures, or log snippets.**
|
||||
|
||||
|
|
@ -446,9 +179,6 @@ 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.
|
||||
|
||||
|
|
@ -466,61 +196,55 @@ 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/...`, or `docs/...` |
|
||||
| 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` |
|
||||
|
||||
---
|
||||
|
||||
## Code quality gates
|
||||
|
||||
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
|
||||
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
|
||||
or provider `--help` output.
|
||||
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.
|
||||
4. **pnpm-lock.yaml** is generated by pnpm; do not reformat or hand-edit 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/` |
|
||||
| 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/` |
|
||||
| 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/` |
|
||||
|
||||
---
|
||||
|
||||
## Harness instruction sources
|
||||
## Harness-specific supplements
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
|
|
|||
688
CHANGELOG.md
688
CHANGELOG.md
|
|
@ -7,686 +7,6 @@ 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).
|
||||
|
||||
### Changed
|
||||
|
||||
- Tiered GitHub Actions so pull requests run changed test files with
|
||||
lint/typecheck, security, and build gates, while the complete workspace suite
|
||||
runs on `main`, nightly, by manual dispatch, or with the `ci:full` label.
|
||||
Cross-platform unsigned desktop packaging now runs for desktop/package PRs,
|
||||
after relevant changes merge to `main`, or by manual dispatch (#955).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Prevented the packaged desktop updater from treating an older stable release
|
||||
as downloadable. Stable, beta, and dev channel assignment now explicitly
|
||||
restores `allowDowngrade=false` after the `electron-updater` channel setter
|
||||
changes it internally (#983).
|
||||
- Deferred the default run-supervisor SQLite repository lookup until the first
|
||||
supervisor operation. Packaged SQLite startup can now import and initialize
|
||||
routes before storage bootstrap without crashing (#981).
|
||||
- Added bounded macOS desktop startup verification that retries through the
|
||||
LaunchServices readiness window, requires the installed app and health
|
||||
endpoint versions to agree, and reports process, port, and log diagnostics on
|
||||
a real timeout. Updated web-to-desktop migration, routine Homebrew upgrade,
|
||||
automation, troubleshooting, and release guidance to use the readiness gate
|
||||
instead of an immediate health request (#927).
|
||||
|
||||
### Added
|
||||
|
||||
- Added a composed credential-free Buzz compatibility gate and complete
|
||||
machine-readable seam evidence for the pinned Buzz `0.4.24` /
|
||||
`710ed9fff57878a1d69f809b80a6ee0416c53fc4` baseline, plus trigger setup,
|
||||
support dispositions, optional live-smoke boundaries, upgrade, and rollback
|
||||
guidance (#912).
|
||||
- Added allowlisted Buzz root-message workflow triggers with bounded predicates,
|
||||
provider-neutral pre-dispatch hooks, durable causal keys, replay and echo
|
||||
suppression, restart reconciliation, API configuration, and audit history
|
||||
(#911).
|
||||
- Added the provider-neutral `runtime-hook/v1` in-process hook bus with bounded
|
||||
envelopes, deterministic scope ordering, explicit blocking pre-events,
|
||||
passive post-events, timeout/reentrancy protection, side-effect-free dry-run,
|
||||
and causal run-journal evidence. External executable and HTTP handlers remain
|
||||
fail closed pending their sandbox and egress boundaries (#874).
|
||||
- Added `harness-compatibility-matrix/v1` for Buzz, Grok Build, OpenAI Codex
|
||||
app-server, Claude Code, and GitHub Copilot CLI. One reviewed record now
|
||||
drives exact builds, source caveats, capability and fixture digests,
|
||||
invalidation policy, live support tiers, API, `vk doctor`, Settings, telemetry,
|
||||
and operator guidance (#918).
|
||||
- Added `harness-conformance-suite/v1` and a deterministic runner for seeded,
|
||||
repeated provider/model/profile/policy/sandbox comparisons. Explicit
|
||||
assertions cover outcomes, files, tools, approvals, network, policy,
|
||||
completion, and capability evidence; normalized results aggregate pass rate,
|
||||
variance, latency, tokens, cost, retries, failure classes, immutable evidence
|
||||
references, and versioned baseline regressions. A credential-free recorded
|
||||
mock command provides the CI/local foundation while credential-gated lanes
|
||||
require explicit opt-in (#859).
|
||||
- Composed the pinned `buzz-agent` ACP profile with the system-owned
|
||||
`veritas-run` bridge. Buzz sessions now receive only that bridge for selected
|
||||
run tools, so catalog allow, deny, approval, attribution, and cleanup remain
|
||||
under the provider-neutral control plane without native server credentials
|
||||
or a Buzz-specific MCP implementation. Provider capability evidence advances
|
||||
to probe revision 14 (#909).
|
||||
- Added the system-owned `veritas-run` MCP bridge for credential-bound tools.
|
||||
Opaque, in-memory authority binds the exact task, attempt, catalog, launch
|
||||
manifest, and two allowed bridge methods. Codex CLI/SDK, Codex app-server,
|
||||
Claude Code, and ACP stdio inject the same narrow bridge; Hermes and OpenClaw
|
||||
fail closed before dispatch when a credential-bound catalog is selected
|
||||
(#970).
|
||||
- Added exact-action credential lease consumption to mediated run-tool calls.
|
||||
The server binds each operation to the active launch manifest, catalog,
|
||||
server, tool, arguments digest, approval, and operation ID; resolves values
|
||||
only inside a one-shot downstream MCP session; rejects replay, drift,
|
||||
approval mismatch, unavailable sources, and credential-bearing results; and
|
||||
relies on the existing terminal lifecycle reconciliation to revoke leases
|
||||
(#969).
|
||||
- Added value-free credential boundary evidence to `run-tool-catalog/v1`.
|
||||
Credential-bound definitions now require enabled MCP-scoped broker
|
||||
definitions, exact source targets, and immutable definition/scope digests.
|
||||
Discovery strips source environment and header values, native provider
|
||||
configuration omits credential-bound servers, covered launch references
|
||||
report a tool-control-plane boundary, and uncovered or drifted references
|
||||
remain fail closed (#968).
|
||||
- Added `vk acp serve --stdio` and `vk acp status --json` to expose a
|
||||
task-bound, provider-neutral ACP v1 server view over Veritas-managed
|
||||
conversations. Fresh prompts retain the immutable task envelope, reconnect
|
||||
replays the causal run journal by cursor, permission requests use the durable
|
||||
approval broker, cancellation interrupts instead of stopping the task, and
|
||||
disconnect leaves the supervised run active. Client-owned MCP catalogs and
|
||||
mismatched worktrees fail closed (#960).
|
||||
- Added a disabled-by-default Grok Build profile under the generic ACP
|
||||
provider, pinned to released `v0.2.111` build `94172f2aa4e5`. Veritas now
|
||||
compiles a dedicated no-leader stdio launch, accepts only bounded model,
|
||||
effort, deny, tool, sandbox, and feature-disable controls, classifies Grok
|
||||
boot credentials separately from task credentials, and validates the exact
|
||||
version, capability, and xAI extension handshake. Unknown builds, approval
|
||||
bypass, reauthentication, leader/plugin/endpoint injection, and source
|
||||
provenance claims fail closed. Provider probe evidence advances to revision
|
||||
13 (#920).
|
||||
- Added a disabled-by-default GitHub Copilot CLI profile under the generic ACP
|
||||
provider, pinned to the `Copilot 1.0.74` public-preview handshake. Veritas
|
||||
now owns a safe stdio baseline, compiles bounded restrictive model/effort/
|
||||
tool/URL/context/credit settings into immutable launch argv, classifies
|
||||
Copilot boot credentials without probing authentication or inference, and
|
||||
reports the incomplete-source and release-tag provenance limits truthfully.
|
||||
Provider probe evidence advances to revision 12 (#917).
|
||||
- Added a disabled-by-default `buzz-agent` conformance profile under the generic
|
||||
ACP provider, pinned to Buzz v0.4.24 commit
|
||||
`710ed9fff57878a1d69f809b80a6ee0416c53fc4`. The profile validates exact ACP
|
||||
identity, version, capabilities, known limitations, safe configuration and
|
||||
boot-authentication environment keys, rejects `buzz-acp` and incompatible
|
||||
capability drift,
|
||||
and advances provider probe evidence to revision 11 (#907).
|
||||
- Added `run-launch-credential-plan/v1` to classify provider boot
|
||||
authentication, broker-targeted task credentials, and explicit high-risk
|
||||
environment passthrough in immutable launch evidence. Plans bind to provider
|
||||
runtime evidence, remain value-free, ignore timestamp-only probe refreshes,
|
||||
and block task credential launches until an accepted tool or egress boundary
|
||||
proves brokered delivery (#932).
|
||||
- Added a generic stable ACP v1 stdio provider adapter with no-shell supervised
|
||||
launch, initialize-time identity and capability evidence, fresh/resume/fork/
|
||||
close session lifecycle, bounded bidirectional JSON-RPC, causal message,
|
||||
thought, plan, and tool events, durable permission brokering, capability
|
||||
drift rejection, explicit environment filtering, and immutable all-allow
|
||||
run-scoped MCP session configuration. Provider capability evidence advances
|
||||
to probe revision 10; the client-facing ACP server view is tracked separately
|
||||
by #960 (#870).
|
||||
- Added the `tool-server-definition/v1`, `tool-server-discovery/v1`, and
|
||||
`run-tool-catalog/v1` control-plane contracts with file and SQLite
|
||||
persistence, version-bound discovery caching, required and optional server
|
||||
posture, per-run allow/deny/approval decisions, bounded supervised stdio and
|
||||
Streamable HTTP invocation, causal replay-safe events, REST/CLI/MCP operator
|
||||
surfaces, and native run-scoped MCP injection for Codex app-server and
|
||||
Claude Code. Credential-bound definitions remain fail-closed until the
|
||||
provider launch credential broker lands (#857).
|
||||
- Added `conversation-lifecycle/v1` with durable provider conversation, turn,
|
||||
item, parent, and fork identities; launch-manifest and worktree compatibility
|
||||
checks; context-window posture; causal lifecycle events; attributed REST,
|
||||
CLI, MCP, and web controls; and explicit unsupported delivery results.
|
||||
Codex CLI and SDK can resume exact persisted sessions, Claude Code can resume
|
||||
or fork with system-owned native flags, and Codex app-server can resume,
|
||||
follow up, steer, fork, compact, archive, interrupt, and close through
|
||||
schema-validated JSON-RPC methods. Generic process stdin is no longer treated
|
||||
as a successful provider follow-up path, and capability evidence advances to
|
||||
probe revision 8 (#856).
|
||||
- Added `run-supervisor/v1`, a durable provider-run ownership record with exact
|
||||
provider, task-envelope, launch-manifest, and worktree bindings; persisted
|
||||
process groups or remote-session handles; budget and event cursors; expiring
|
||||
single-owner leases; typed recovery actions; and idempotent terminal
|
||||
ownership. Startup now reattaches verified local provider processes, resumes
|
||||
journal reconciliation strictly after the durable cursor, restores exact
|
||||
completion results across crash races, and blocks unsafe recovery without
|
||||
signaling reused PIDs or starting duplicate work. File and SQLite
|
||||
repositories share the same compare-and-set contract, and provider capability
|
||||
evidence advances to probe revision 7 (#853).
|
||||
- Added a provider-native `run-approval/v1` broker with file and SQLite
|
||||
persistence, exact action hashes, atomic compare-and-set decisions,
|
||||
auth-derived reviewer identity, critical-action authentication freshness,
|
||||
expiry/cancellation, mobile two-layer safety, causal audit correlation, REST
|
||||
and WebSocket delivery, task and shared-run review surfaces, and
|
||||
non-persisted elicitation response values. Codex app-server now pauses and resumes its exact
|
||||
command, file, permission, tool-question, legacy exec/patch, and MCP
|
||||
elicitation request through pinned v0.145.0 response schemas; stale, changed,
|
||||
duplicate, interrupted, unsupported, and failed requests deny safely (#852).
|
||||
- Added a first-class Codex app-server v2 task adapter pinned to
|
||||
`codex-cli 0.145.0` and upstream commit
|
||||
`25af12f7e61572b0bc18ddb1008be543b91519b0`. Veritas now owns a strict-stdio,
|
||||
no-shell launch, validates checked-in exact-version JSON Schemas, persists
|
||||
one task-bound thread and turn, streams item/usage/completion events into the
|
||||
causal journal, supports cooperative `turn/interrupt`, and retries only
|
||||
bounded overload `-32001` responses. Inherited MCP, hooks, plugins, apps,
|
||||
browser/computer tools, elicitation, remote control, and unsandboxed
|
||||
`thread/shellCommand` remain structurally disabled; approval, lifecycle, and
|
||||
reattachment controls fail closed until their provider-neutral brokers land
|
||||
(#921).
|
||||
- Added a first-class Claude Code v2.1.218 task adapter with a reproducible
|
||||
no-shell bare-mode launch, static sandbox-derived permissions, explicit
|
||||
environment authentication with provider credentials scrubbed from tool
|
||||
subprocesses, bounded health and agent-discovery diagnostics,
|
||||
immutable task/run manifests, drain-safe stream-json ingestion, causal event
|
||||
mapping for partial text, thinking, tools, hooks, subagents, usage, cost, and
|
||||
results, separate session identity persistence, artifact discovery, and
|
||||
fail-closed capability evidence for lifecycle and broker controls that have
|
||||
not landed yet. Legacy provider-less Claude records migrate only when command
|
||||
identity matches, and the old permission-bypass default is removed (#916).
|
||||
- Added `run-event/v1`, a provider-neutral causal event journal for OpenClaw,
|
||||
Codex CLI, Codex SDK, Codex app-server, Claude Code, and Hermes. Provider
|
||||
mappers now durably append bounded,
|
||||
redacted, deduplicated lifecycle, message, reasoning, command, file, tool,
|
||||
approval, artifact, usage, error, and unknown events before projecting them
|
||||
into legacy logs, traces, telemetry, budgets, and live output. File and SQLite
|
||||
repositories allocate per-attempt monotonic cursors, REST and WebSocket
|
||||
clients can replay after a cursor, and reconnect subscriptions merge replay
|
||||
with concurrent live events without gaps. The published JSON Schema,
|
||||
symlink-safe file storage, SQLite migration, provider fixtures, replay,
|
||||
migration, redaction, and interleaved operator/provider tests define the v1
|
||||
compatibility boundary (#850).
|
||||
- Added one-way Buzz persona/team definition import with signed bounded NIP-33
|
||||
head queries, exact v0.4.24 field policy, preview/diff/collision actions,
|
||||
same-author team resolution, disabled profile and roster materialization,
|
||||
immutable provenance, source-status reporting, optimistic JSON/SQLite
|
||||
mutations, audit history, Settings controls, and malicious-source coverage.
|
||||
Runtime/model/provider preferences remain source-only, and import never
|
||||
launches, enables, routes, fetches, installs, or writes back (#910).
|
||||
- Added a reference-only Buzz communication adapter with typed configuration,
|
||||
host-preserving HTTP/WS normalization, SSRF-aware bounded relay probes,
|
||||
NIP-11 software/version/community checks, NIP-98 identity and membership/read
|
||||
diagnostics with built-in hexadecimal/`nsec` signing, stable machine-readable
|
||||
reason codes, evidence invalidation, shell-free optional command discovery,
|
||||
Settings controls, `vk doctor` reporting, and operator/API/security
|
||||
documentation. The resulting compatibility evidence is the fail-closed gate
|
||||
consumed by Buzz communication delivery (#906).
|
||||
- Added a native bidirectional Buzz channel bridge for Squad Chat with signed
|
||||
Nostr root/reply events, channel mapping APIs and Settings controls, NIP-42
|
||||
WebSocket subscriptions, source author/timestamp/deep-link metadata,
|
||||
`(created_at,event_id)` cursors, overlap dedupe, out-of-order reply recovery,
|
||||
loop prevention, non-destructive edit/delete auditing, delivery-unknown
|
||||
reconciliation, runtime health facets, safe disable/rollback, fake-relay
|
||||
contract coverage, and operator/API/security documentation (#906).
|
||||
- Added transactional, remote-safe worktree lifecycle management with
|
||||
versioned manifests, exact fetched base commits, reasoned offline fallback,
|
||||
task/attempt ownership leases, active-run locks, recovery states, and
|
||||
preview-first stale cleanup. Dirty, untracked, unpushed, unmerged, and
|
||||
externally held worktrees now require an admin-audited override. Attempt
|
||||
leases use exclusive claims, legacy worktrees have a validated adoption path,
|
||||
and restart recovery reconciles partial rebase, integration, push, and
|
||||
cleanup states. Integration uses a dedicated temporary worktree and
|
||||
non-force push without mutating the primary checkout. Task envelopes and run
|
||||
launch manifests bind the exact worktree allocation and base evidence (#858).
|
||||
- Added the versioned credential-definition and run-bound lease core with
|
||||
metadata-only admin APIs, canonical definition/scope/action fingerprints,
|
||||
opaque hashed handles, exact launch-manifest and action binding, atomic TTL
|
||||
and use-count enforcement, operation-level replay rejection,
|
||||
refresh/revoke/expiry/reconciliation state, callback-only secret resolution,
|
||||
terminal-run invalidation with revocation retry, live-owner file locking, and
|
||||
bounded metadata audit records. Required brokered presets now reject advisory
|
||||
or externally delegated credential handling until a controlled provider
|
||||
boundary proves end-to-end support (#931).
|
||||
- Added the immutable `run-launch-manifest/v1` contract, a preflight preview API
|
||||
and CLI command, per-field configuration provenance, fail-closed profile
|
||||
tool/MCP/permission/health enforcement, parent-attempt material drift,
|
||||
readiness/override evidence, governance traces, attempt/history/log
|
||||
persistence, and completion-packet links to the exact launch and provider
|
||||
capability evidence (#854).
|
||||
- Added immutable provider-owned task-envelope transports for OpenClaw, Codex
|
||||
CLI, Codex SDK, Codex app-server, Claude Code, and Hermes. Each renderer
|
||||
binds the exact envelope and runtime identity, commit policy, bounded
|
||||
attributed profile/checkpoint context, workspace baseline, verification
|
||||
gates, and completion evidence to the launched request. OpenClaw alone
|
||||
receives a completion callback; process and stream providers return terminal
|
||||
output through harness-owned capture, and mismatched provider/adapter
|
||||
identities fail closed (#892).
|
||||
- Added digest-bound, idempotent `completion-result/v1` persistence for process,
|
||||
stream, callback, remote-session, and operator-interruption terminal paths.
|
||||
Provider claims are bounded, redacted, and untrusted while harness evidence
|
||||
attributes post-launch files, commits, artifacts, verification, and side
|
||||
effects without crediting pre-existing off-HEAD history.
|
||||
Commit/output/evidence violations downgrade success to recoverable
|
||||
partial completion; exact duplicate callbacks survive retry or restart and
|
||||
conflicting terminal owners fail closed. Provider-owned transports cannot be
|
||||
replaced by callbacks, completion writes use compare-and-swap retries, and
|
||||
harness-owned restart reconciliation remains inside the same result contract
|
||||
while OpenClaw stays eligible for its callback (#893).
|
||||
|
||||
### Security
|
||||
|
||||
- Removed the legacy Claude Code permission-bypass default. Every executable
|
||||
provider now launches through an explicit adapter, immutable launch manifest,
|
||||
bounded environment allowlist, capability evidence, exact-action approval
|
||||
broker, and credential plan. Unsupported controls, adapter/profile drift,
|
||||
stale evidence, and unknown provider-less records fail closed (#919, #916,
|
||||
#852, #932).
|
||||
- Added value-free credential definitions and one-shot run-bound leases.
|
||||
Credential values are resolved only inside the mediated downstream call and
|
||||
are excluded from provider-native configuration, manifests, logs, telemetry,
|
||||
fixtures, and API responses (#931, #968, #969, #970).
|
||||
- Added bounded, redacted causal protocol storage and supervised no-shell
|
||||
process lifecycles for ACP, Codex app-server, Claude Code, and Buzz relay
|
||||
integration. The deferred fine-grained HTTP egress proxy remains tracked in
|
||||
#855; any required network rule that current runtime evidence cannot enforce
|
||||
blocks launch.
|
||||
|
||||
### Migration
|
||||
|
||||
- Existing provider configurations are normalized to
|
||||
`harness-support-profile/v1`. Only known legacy records whose command and
|
||||
built-in type both identify Codex, Hermes, or Claude are migrated to their
|
||||
matching adapter. Provider-less, ambiguous, disabled, or mismatched records
|
||||
remain disabled or fail closed; no migration starts a provider.
|
||||
- The public API remains `v1`. New run, approval, lifecycle, tool, credential,
|
||||
compatibility, Buzz, and conformance contracts are additive. Existing v5
|
||||
board and desktop SQLite data remains in place; operators must back up before
|
||||
upgrade and keep the v5.2.5 backup for rollback.
|
||||
- Buzz, Grok Build, GitHub Copilot CLI, Claude Code, and Codex app-server
|
||||
support is exact-version and evidence-bound. Unknown builds do not inherit
|
||||
certification. Grok Build and Copilot CLI retain source-provenance limits,
|
||||
ACP lifecycle support varies by runtime, and credential-gated live smoke
|
||||
remains separate from deterministic certification.
|
||||
|
||||
## [5.2.5] - 2026-07-23
|
||||
|
||||
### Added
|
||||
|
|
@ -2547,13 +1867,7 @@ 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.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
|
||||
[unreleased]: https://github.com/BradGroux/veritas-kanban/compare/v5.2.5...HEAD
|
||||
[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
|
||||
[5.2.3]: https://github.com/BradGroux/veritas-kanban/compare/v5.2.2...v5.2.3
|
||||
|
|
|
|||
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 and common mistakes caught by previous Claude runs. Do not duplicate
|
||||
> `AGENTS.md` content here.
|
||||
> Claude-specific lessons, cross-model review workflow, and common mistakes caught by previous
|
||||
> Claude runs. Do not duplicate `AGENTS.md` content here.
|
||||
>
|
||||
> **Last updated:** 2026-08-24 (v6.1.2 release freshness)
|
||||
> **Last updated:** 2026-07-10 (v2.1.0 — converted to supplement)
|
||||
> **Freshness check:** Update after mistakes; review monthly.
|
||||
|
||||
---
|
||||
|
|
@ -16,8 +16,14 @@ 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:** managed Buzz, Grok Build, Codex, Claude Code, Copilot CLI,
|
||||
Hermes, and OpenClaw contracts are documented in `AGENTS.md`
|
||||
- **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.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -56,7 +62,7 @@ that was previously embedded here. The fields updated from their stale v2.0 valu
|
|||
## When to update this file
|
||||
|
||||
- After a mistake that a rule would have prevented.
|
||||
- After any review catches a systemic pattern worth preserving.
|
||||
- After a cross-model review catches something systemic.
|
||||
- Monthly freshness review.
|
||||
|
||||
---
|
||||
|
|
|
|||
181
CONTRIBUTING.md
181
CONTRIBUTING.md
|
|
@ -5,7 +5,7 @@ Thanks for your interest in contributing! This guide will help you get started.
|
|||
## Prerequisites
|
||||
|
||||
- **Node.js** 22 or later
|
||||
- **pnpm** 11+ (package manager)
|
||||
- **pnpm** 9+ (package manager)
|
||||
|
||||
## Development Setup
|
||||
|
||||
|
|
@ -73,75 +73,31 @@ veritas-kanban/
|
|||
|
||||
2. Make your changes — write code, add tests, update docs.
|
||||
|
||||
3. Run touched-package type checking and changed-file linting before
|
||||
committing. Ordinary implementation pull requests do not run workspace
|
||||
tests:
|
||||
3. Run type checking, linting, and tests before committing:
|
||||
|
||||
```bash
|
||||
pnpm --filter @veritas-kanban/server typecheck
|
||||
pnpm exec eslint server/src/path/to/changed.ts
|
||||
pnpm typecheck
|
||||
pnpm lint
|
||||
pnpm test
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
When merging multiple feature branches, merge one at a time so the next branch
|
||||
can rebase on the exact result.
|
||||
**Critical:** When merging multiple feature branches, merge **one at a time**. Never batch-merge parallel branches.
|
||||
|
||||
**Process:**
|
||||
|
||||
1. Merge first branch to `main`
|
||||
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
|
||||
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
|
||||
|
||||
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.
|
||||
**Why:** Parallel branches often introduce integration issues that are hidden when batch-merging. Sequential merges with testing between each merge catch these immediately.
|
||||
|
||||
### One Agent Per File Rule
|
||||
|
||||
|
|
@ -180,28 +136,37 @@ 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.
|
||||
|
||||
### Risk-Proportional Review
|
||||
### Pre-Commit Review Protocol (Mandatory)
|
||||
|
||||
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.
|
||||
Before every commit, run these 4 reviews:
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Pre-Merge Checklist
|
||||
|
||||
Before merging, verify the checks selected for the changed product boundary:
|
||||
Before merging any branch, verify:
|
||||
|
||||
- [ ] **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.
|
||||
- [ ] **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
|
||||
|
||||
### Environment Rules
|
||||
|
||||
|
|
@ -216,19 +181,17 @@ Before merging, verify the checks selected for the changed product boundary:
|
|||
|
||||
### Testing Requirements
|
||||
|
||||
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:
|
||||
**"Builds clean" is necessary but NOT sufficient.**
|
||||
|
||||
- **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.
|
||||
Before declaring a branch ready to merge, verify **runtime behavior:**
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### Common Integration Failures
|
||||
|
||||
|
|
@ -284,11 +247,7 @@ 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 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.
|
||||
5. **Ensure CI passes** — all checks must be green.
|
||||
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.
|
||||
|
|
@ -311,19 +270,12 @@ 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
|
||||
|
|
@ -340,47 +292,6 @@ Follow the existing conventions in `.eslintrc.*`, `.prettierrc`, and `tsconfig.j
|
|||
- Write tests for new features and bug fixes.
|
||||
- Ensure existing tests pass before submitting.
|
||||
|
||||
### CI tiers
|
||||
|
||||
| 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 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?
|
||||
|
||||
Open a [GitHub Discussion](https://github.com/BradGroux/veritas-kanban/discussions) or reach out to the maintainers.
|
||||
|
|
|
|||
79
Dockerfile
79
Dockerfile
|
|
@ -5,17 +5,16 @@
|
|||
# 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 the Express server TypeScript
|
||||
# 5. production-deps — Install the server-only runtime closure
|
||||
# 6. production — Minimal runtime image
|
||||
# 4. build-server — Compile Express server TypeScript
|
||||
# 5. production — Minimal runtime image
|
||||
#
|
||||
# Target image size: < 200,000,000 bytes on arm64; < 600,000,000 bytes on amd64
|
||||
# Target image size: < 200MB
|
||||
# =============================================================================
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 1: Install dependencies (shared across build stages)
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM node:22-alpine3.24 AS deps
|
||||
FROM node:22-alpine AS deps
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@11.1.1 --activate
|
||||
|
||||
|
|
@ -64,54 +63,45 @@ COPY server/ ./server/
|
|||
RUN pnpm --filter @veritas-kanban/server build
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 5: Install the server-only production dependency closure
|
||||
# Stage 5: Production runtime
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM node:22-alpine3.24 AS production-deps
|
||||
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
|
||||
|
||||
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 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
|
||||
COPY web/package.json ./web/
|
||||
COPY cli/package.json ./cli/
|
||||
COPY mcp/package.json ./mcp/
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 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
|
||||
# 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
|
||||
|
||||
RUN apk add --no-cache ca-certificates libstdc++ && \
|
||||
addgroup -g 1001 -S nodejs && \
|
||||
adduser -S veritas -u 1001 -G nodejs
|
||||
# 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
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
# Switch to non-root user
|
||||
USER veritas
|
||||
|
|
@ -127,7 +117,8 @@ 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
|
||||
|
||||
# The runtime path contract is independent of cwd when DATA_DIR is set.
|
||||
# Set working directory to server/ so path.resolve(cwd, '..') resolves to /app
|
||||
# (Services use process.cwd()/.. to find .veritas-kanban and tasks directories)
|
||||
WORKDIR /app/server
|
||||
|
||||
# Start server
|
||||
|
|
|
|||
185
README.md
185
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? Choose the packaged Mac app or a local source checkout:
|
||||
Want to do it yourself? Get up and running in under 5 minutes:
|
||||
|
||||
For the packaged Mac desktop app:
|
||||
|
||||
|
|
@ -49,11 +49,6 @@ brew tap BradGroux/tap
|
|||
brew install --cask veritas-kanban
|
||||
```
|
||||
|
||||
Existing desktop users should follow the
|
||||
[routine Mac upgrade](docs/V6-UPGRADE-INSTALL-ADMIN-GUIDE.md#routine-mac-desktop-upgrade)
|
||||
path so backup, heartbeat pause, app replacement, launch, and exact-version
|
||||
server readiness happen in the right order.
|
||||
|
||||
For local source development:
|
||||
|
||||
```bash
|
||||
|
|
@ -92,39 +87,29 @@ 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, 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.
|
||||
- [MCP Server Guide](docs/mcp/README.md) — optional MCP setup, 36 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.
|
||||
- [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.
|
||||
- [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) — 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.
|
||||
- [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.
|
||||
- [v5 GA Checklist](docs/V5-GA-CHECKLIST.md) — release-gate reference, follow-up evidence tracking, Mantine visual/accessibility cleanup evidence, and bundle checks.
|
||||
- [v5 Visual Tour](docs/V5-VISUAL-TOUR.md) — release-safe dummy screenshots and GIFs for the v5 desktop shell, resizable workbench, Squad Chat coordination, agent providers, task work view, Maintenance Center, and mobile/PWA shell.
|
||||
- [v5 Upgrade, Install, Remote, And Admin Guide](docs/V5-UPGRADE-INSTALL-ADMIN-GUIDE.md) — fresh install, v4-to-v5 upgrade, desktop setup, remote/server, mobile/PWA, admin, backup, and diagnostics paths.
|
||||
- [v5 Compatibility And Release Policy](docs/V5-COMPATIBILITY-AND-RELEASE-POLICY.md) — supported version combinations, update channels, stale-client behavior, rollback limits, and release validation.
|
||||
- [v5 Release Notes](docs/V5-RELEASE-NOTES.md) — current source release notes, breaking changes, migration warnings, published v5 artifacts, and deferred post-GA backlog.
|
||||
- [v5 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.
|
||||
- [Self-Hosting Guide](docs/guides/SELF_HOST.md) — production deployment, reverse proxy, auth hardening, Docker, and backups.
|
||||
- [Agent Task Workflow SOP](docs/SOP-agent-task-workflow.md) — lifecycle, API/CLI snippets, prompts.
|
||||
- [Squad Chat Protocol](docs/SQUAD-CHAT-PROTOCOL.md) — agent messaging, system events (spawned/completed/failed), model attribution, and helper scripts.
|
||||
- [Buzz Integration](docs/BUZZ-INTEGRATION.md) — signed Squad Chat bridging,
|
||||
explicit persona/team import, and a separate disabled-by-default
|
||||
`buzz-agent` profile under the generic ACP provider.
|
||||
- [Agent Providers](docs/AGENT-PROVIDERS.md#grok-build-acp) — exact-version
|
||||
Grok Build, GitHub Copilot CLI, Buzz Agent, Claude Code, and Codex runtime
|
||||
setup, safety policy, and known limitations.
|
||||
- [Sprint Planning SOP](docs/SOP-sprint-planning.md) — epic → sprint → task breakdown.
|
||||
- [Multi-Agent Orchestration](docs/SOP-multi-agent-orchestration.md) — PM + worker handoffs.
|
||||
- [Optional Cross-Model Code Review](docs/SOP-cross-model-code-review.md) — add an independent model only when the issue or release owner requires it.
|
||||
- [Cross-Model Code Review](docs/SOP-cross-model-code-review.md) — enforce Claude ↔ GPT reviews.
|
||||
- [Agent Governance SOPs](docs/) — [Policy engine](docs/SOP-agent-policy-engine.md), [drift detection](docs/SOP-behavioral-drift-detection.md), [decision audit](docs/SOP-decision-audit-trail.md), [output evaluation](docs/SOP-output-evaluation.md), [user feedback](docs/SOP-user-feedback.md).
|
||||
- [Operational SOPs](docs/) — [Broadcasts](docs/SOP-broadcasts.md), [delegation](docs/SOP-delegation.md), [deliverables](docs/SOP-deliverables.md), [work products](docs/features/work-products.md), [prompt registry](docs/SOP-prompt-registry.md), [squad chat](docs/SOP-squad-chat.md), [system health](docs/SOP-system-health-monitoring.md).
|
||||
- [Best Practices](docs/BEST-PRACTICES.md) & [Tips + Tricks](docs/TIPS-AND-TRICKS.md) — patterns, shortcuts, integrations.
|
||||
|
|
@ -154,7 +139,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:** Agents amplify both useful work and mistakes. Start locally, keep permissions narrow, and add autonomy only after the smaller setup is understood and verified.
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -162,7 +147,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 bounded criteria profiles (regex, keyword, numeric range, occurrence ratio).
|
||||
**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).
|
||||
|
||||
### 🤖 Agent Orchestration
|
||||
|
||||
|
|
@ -172,15 +157,11 @@ 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.
|
||||
|
||||

|
||||
|
||||
### 🧭 Provider And Cutover Operations
|
||||
### 🧭 Veritas Cutover + Hermes Support
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### 🧠 OpenAI Codex Integration
|
||||
|
||||
|
|
@ -210,17 +191,13 @@ 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.
|
||||
|
||||
### 📁 Local-First Storage
|
||||
### 📁 Zero Infrastructure
|
||||
|
||||
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.
|
||||
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`.
|
||||
|
||||
### 🔌 Optional Integration Surfaces
|
||||
|
||||
- **MCP Server** — 42 tools across 9 categories via Model Context Protocol
|
||||
- **MCP Server** — 36 tools across 8 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.
|
||||
|
||||
|
|
@ -254,12 +231,6 @@ complete configured storage root, not only the Git-tracked board files.
|
|||
- **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
|
||||
- **Sandbox policy presets** — Built-in and custom presets for filesystem scope, network egress, environment passthrough, and credential brokering, with Settings dry-runs before agent launch
|
||||
|
|
@ -269,13 +240,6 @@ complete configured storage root, not only the Git-tracked board files.
|
|||
- **@Mention notifications** — @agent-name parsing in comments, thread subscriptions
|
||||
- **Broadcast Notifications** — Priority-based persistent notifications with read receipts and agent-specific delivery
|
||||
- **Squad Chat Webhook** — Configurable webhooks (generic HTTP or OpenClaw Direct) for external agent integration
|
||||
- **Buzz Communication Adapter** — Native signed root/reply bridge between one mapped Buzz community channel and Squad Chat, with durable replay, ambiguous-send reconciliation, and operator-confirmed persona/team definition materialization
|
||||
- **Buzz Agent ACP profile** — Generic ACP execution with pinned Buzz v0.4.24
|
||||
identity/capability evidence, safe environment allowlists, and honest
|
||||
no-resume and stdio-only MCP posture
|
||||
- **GitHub Copilot CLI ACP profile** — Generic ACP execution with a system-owned
|
||||
stdio/public-preview launch baseline, exact v1.0.74 compatibility evidence,
|
||||
bounded restrictive process policy, and honest source/authentication limits
|
||||
- **Agent registry** — Service discovery with heartbeat tracking, capabilities, and live status
|
||||
- **Multi-agent dashboard** — Real-time sidebar with expandable agent cards, status indicators
|
||||
- **Multi-agent task assignment** — Assign multiple agents per task with color-coded chips
|
||||
|
|
@ -327,7 +291,7 @@ complete configured storage root, not only the Git-tracked board files.
|
|||
- **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** — Registry-backed review dates, thresholds, scores, and staleness alerts
|
||||
- **Documentation freshness** — Steward workflow with freshness headers and automated staleness detection
|
||||
- **Cost prediction** — Multi-factor cost estimation for tasks
|
||||
|
||||
#### Dashboard
|
||||
|
|
@ -364,7 +328,7 @@ complete configured storage root, not only the Git-tracked board files.
|
|||
#### Integration
|
||||
|
||||
- **CLI** — `vk` command for terminal workflows
|
||||
- **MCP Server** — 42 tools across 9 categories via Model Context Protocol
|
||||
- **MCP Server** — 36 tools across 8 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
|
||||
|
||||
|
|
@ -376,14 +340,14 @@ complete configured storage root, not only the Git-tracked board files.
|
|||
|
||||
| Layer | Technology | Version |
|
||||
| ------------------- | ------------------------------------- | ------------------------------------------- |
|
||||
| **Frontend** | React, Vite, Tailwind CSS, Mantine UI | React 19, Vite 8, Tailwind 4.3, Mantine 9.5 |
|
||||
| **Frontend** | React, Vite, Tailwind CSS, Mantine UI | React 19, Vite 8, Tailwind 4.3, Mantine 9.3 |
|
||||
| **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.62, Vitest 4.1 |
|
||||
| **Runtime** | Node.js | 22.22.1+ |
|
||||
| **Package Manager** | pnpm | 11.1.1 (pinned) |
|
||||
| **Testing** | Playwright (E2E), Vitest (unit) | Playwright 1.61, Vitest 4.1 |
|
||||
| **Runtime** | Node.js | 22+ |
|
||||
| **Package Manager** | pnpm | 11.1.1+ |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -402,7 +366,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** | ✅ 42 tools | ❌ | ❌ |
|
||||
| **MCP server** | ✅ 36 tools | ❌ | ❌ |
|
||||
| **CLI** | ✅ Full lifecycle | ❌ | ⚠️ Limited |
|
||||
| **Git worktrees + code review** | ✅ Built-in | ❌ | ❌ |
|
||||
| **Task persistence** | ✅ Markdown files | ❌ In-memory | ✅ Database |
|
||||
|
|
@ -486,7 +450,7 @@ veritas-kanban/ ← pnpm monorepo
|
|||
└── agent-requests/
|
||||
```
|
||||
|
||||
**Data flow:** Web ↔ REST API / WebSocket ↔ Server ↔ configured file or SQLite storage
|
||||
**Data flow:** Web ↔ REST API / WebSocket ↔ Server ↔ Markdown/YAML files on disk
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -515,7 +479,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.
|
||||
|
||||
Handle the common start-and-complete task lifecycle with two commands.
|
||||
Manage your entire task lifecycle with two commands.
|
||||
|
||||
```bash
|
||||
# Install globally
|
||||
|
|
@ -538,8 +502,6 @@ vk doctor --json # Support-safe JSON report
|
|||
vk snapshot --format markdown # Redacted runtime support snapshot
|
||||
vk prompts import prompt-registry --dry-run
|
||||
vk sqlite journal status --json # Journal maintenance and override posture
|
||||
vk tool-servers list --json # Registered run-scoped MCP servers
|
||||
vk tool-servers discover <id> # Refresh version-bound tool discovery
|
||||
```
|
||||
|
||||
Validates Node version, server health, API auth, and optionally creates a welcome task to get you started.
|
||||
|
|
@ -648,15 +610,10 @@ vk github mappings # List issue↔task mappings
|
|||
vk agents:pending # List pending agent requests
|
||||
vk agents:status <id> # Check if agent running
|
||||
vk agents:complete <id> -s --attempt-id <id> --manifest-digest <sha256:...>
|
||||
vk launch-preview <id> --json # Inspect effective launch evidence without dispatch
|
||||
vk profiles list # List reusable agent profile packages
|
||||
vk profiles validate ./agent.yml # Validate a package before import
|
||||
vk profiles import ./agent.yml # Import or replace a package
|
||||
vk start <task> --profile <id> # Launch a task with a profile package
|
||||
vk agent:resume <task> --source-attempt <id> -m "Continue the work"
|
||||
vk agent:fork <task> --source-attempt <id> --fork-turn <id> -m "Try another path"
|
||||
vk agent:steer <task> --attempt <id> -m "Use the smaller fix"
|
||||
vk agent:compact <task> --attempt <id>
|
||||
```
|
||||
|
||||
### Utilities
|
||||
|
|
@ -740,31 +697,18 @@ vk agents:pending
|
|||
# then call the completion endpoint automatically.
|
||||
```
|
||||
|
||||
### Managed agent harnesses and external clients
|
||||
### Codex + HermesAgent
|
||||
|
||||
- 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.
|
||||
- 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, Ollama, LM Studio, provider-specific routing, or sandbox presets in the web app or macOS app.
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## 🔗 MCP Server
|
||||
|
||||
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.
|
||||
Optional. The MCP server exposes 36 tools across 8 categories (tasks, agents, automation, notifications, summaries, sprints, comments, projects) 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.
|
||||
|
||||
|
|
@ -798,7 +742,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 42 Veritas Kanban tools appear
|
||||
- **Verify tools are available:** Run `openclaw mcp list` to confirm 36 Veritas Kanban tools appear
|
||||
- **When reporting issues, provide:**
|
||||
- OpenClaw version (`openclaw --version`)
|
||||
- VK version and health (`curl http://localhost:3001/api/health`)
|
||||
|
|
@ -840,7 +784,7 @@ pnpm build # Production build
|
|||
pnpm typecheck # TypeScript strict check
|
||||
pnpm lint # ESLint
|
||||
pnpm lint:budget # ESLint with current warning budget
|
||||
pnpm test # Canonical unit gate (server, web, CLI, MCP)
|
||||
pnpm test # Unit tests (Vitest)
|
||||
pnpm test:e2e # E2E tests (Playwright)
|
||||
pnpm test:load:smoke # k6 API smoke test
|
||||
pnpm validate:release # Release readiness checks
|
||||
|
|
@ -850,29 +794,29 @@ 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 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 |
|
||||
| Document | Description |
|
||||
| ---------------------------------------------- | -------------------------------------------- |
|
||||
| [Features](docs/FEATURES.md) | Complete feature reference |
|
||||
| [v5 Visual Tour](docs/V5-VISUAL-TOUR.md) | Release-safe dummy screenshots and GIFs |
|
||||
| [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 |
|
||||
|
||||
---
|
||||
|
||||
## 📸 Visuals
|
||||
## 📸 v5 Visuals
|
||||
|
||||
<details>
|
||||
<summary><strong>Click to expand screenshots and GIFs</strong></summary>
|
||||
<summary><strong>Click to expand v5 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.
|
||||
These captures use release-safe dummy content against the current v5 app surfaces. See the [v5 Visual Tour](docs/V5-VISUAL-TOUR.md) for the full set and capture notes.
|
||||
|
||||
### Desktop
|
||||
|
||||
|
|
@ -904,22 +848,19 @@ These captures use release-safe dummy content against the current app surfaces.
|
|||
|
||||
## 🗺️ Roadmap
|
||||
|
||||
Current work and priorities live in GitHub, not in a version-specific README checklist:
|
||||
Current planning lives in GitHub, not in a stale README checklist:
|
||||
|
||||
- [Open issues](https://github.com/BradGroux/veritas-kanban/issues)
|
||||
- [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)
|
||||
- [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)
|
||||
- [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, architecture records for durable direction, and the changelog and releases for shipped work.
|
||||
Use issues for current work and the changelog for shipped work.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
44
SECURITY.md
44
SECURITY.md
|
|
@ -53,50 +53,6 @@ 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.1.2",
|
||||
"version": "5.2.5",
|
||||
"description": "CLI for Veritas Kanban task management",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
@ -9,18 +9,17 @@
|
|||
"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": "^6.0.0"
|
||||
"chalk": "^5.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.2.0",
|
||||
"@types/node": "^26.1.1",
|
||||
"typescript": "^6.0.3",
|
||||
"tsx": "^4.23.12"
|
||||
"tsx": "^4.23.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "Brad Groux <brad@digitalmeld.io>",
|
||||
|
|
|
|||
|
|
@ -1,400 +0,0 @@
|
|||
import { Buffer } from 'node:buffer';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type {
|
||||
RunApprovalRequest,
|
||||
RunEventEnvelope,
|
||||
RunEventPage,
|
||||
Task,
|
||||
} from '@veritas-kanban/shared';
|
||||
import { AcpServerView, readAcpStatus, type AcpApiClient } from '../commands/acp.js';
|
||||
|
||||
function request(id: number, method: string, params: unknown): string {
|
||||
return JSON.stringify({ jsonrpc: '2.0', id, method, params });
|
||||
}
|
||||
|
||||
function event(
|
||||
sequence: number,
|
||||
kind: string,
|
||||
payload: Record<string, unknown> = {}
|
||||
): RunEventEnvelope {
|
||||
return {
|
||||
schemaVersion: 'run-event/v1',
|
||||
eventId: `event_${sequence}`,
|
||||
taskId: 'task_1',
|
||||
runId: 'attempt_1',
|
||||
attemptId: 'attempt_1',
|
||||
sequence,
|
||||
receivedAt: '2026-07-24T12:00:00.000Z',
|
||||
kind,
|
||||
source: { provider: 'codex-cli', adapter: 'codex-cli', agent: 'codex' },
|
||||
redaction: { status: 'none', fields: [], originalBytes: 1, persistedBytes: 1 },
|
||||
payload,
|
||||
payloadHash: `sha256:${'a'.repeat(64)}`,
|
||||
} as RunEventEnvelope;
|
||||
}
|
||||
|
||||
function task(attempt?: Task['attempt']): Task {
|
||||
return {
|
||||
id: 'task_1',
|
||||
title: 'ACP task',
|
||||
description: 'Use the ACP view',
|
||||
type: 'code',
|
||||
status: 'in-progress',
|
||||
priority: 'high',
|
||||
project: 'veritas-kanban',
|
||||
created: '2026-07-24T12:00:00.000Z',
|
||||
updated: '2026-07-24T12:00:00.000Z',
|
||||
git: {
|
||||
repo: 'veritas-kanban',
|
||||
branch: 'feat/acp',
|
||||
baseBranch: 'main',
|
||||
worktreePath: '/tmp/task_1',
|
||||
},
|
||||
...(attempt ? { attempt, attempts: [attempt] } : {}),
|
||||
} as Task;
|
||||
}
|
||||
|
||||
const approval: RunApprovalRequest = {
|
||||
schemaVersion: 'run-approval/v1',
|
||||
id: 'runapproval_123456789012',
|
||||
workspaceId: 'local',
|
||||
taskId: 'task_1',
|
||||
attemptId: 'attempt_1',
|
||||
provider: 'codex-cli',
|
||||
agentId: 'codex',
|
||||
requestKind: 'approval',
|
||||
actionClass: 'shell',
|
||||
action: 'Run tests',
|
||||
actionHash: `sha256:${'b'.repeat(64)}`,
|
||||
details: 'pnpm test',
|
||||
resourceScope: ['/tmp/task_1'],
|
||||
riskClass: 'medium',
|
||||
evidenceRevision: `sha256:${'c'.repeat(64)}`,
|
||||
providerRequestId: 'provider_approval_1',
|
||||
mobileSafe: true,
|
||||
status: 'pending',
|
||||
revision: 1,
|
||||
createdAt: '2026-07-24T12:00:00.000Z',
|
||||
updatedAt: '2026-07-24T12:00:00.000Z',
|
||||
expiresAt: '2026-07-24T12:30:00.000Z',
|
||||
};
|
||||
|
||||
describe('vk ACP server view', () => {
|
||||
beforeEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('streams one provider-neutral turn through events and the durable approval broker', async () => {
|
||||
const writes: Record<string, unknown>[] = [];
|
||||
const decisions: unknown[] = [];
|
||||
let eventRead = 0;
|
||||
const fakeApi = vi.fn(async (requestPath: string, options?: RequestInit) => {
|
||||
if (requestPath === '/api/auth/context') {
|
||||
return { role: 'admin', workspaceId: 'local', permissions: ['*'] };
|
||||
}
|
||||
if (requestPath === '/api/tasks') return [task()];
|
||||
if (requestPath.endsWith('/status')) return { running: false };
|
||||
if (requestPath.endsWith('/conversation/fresh')) {
|
||||
expect(JSON.parse(String(options?.body))).toMatchObject({
|
||||
message: 'Implement the scoped task',
|
||||
agent: 'codex',
|
||||
});
|
||||
return { attemptId: 'attempt_1' };
|
||||
}
|
||||
if (requestPath.includes('/attempts/attempt_1/events?')) {
|
||||
eventRead += 1;
|
||||
return eventRead === 1
|
||||
? page([
|
||||
event(1, 'message.delta', { summary: 'Working on it.' }),
|
||||
event(2, 'tool.started', { summary: 'Run tests' }),
|
||||
event(3, 'approval.requested', { approvalId: approval.id }),
|
||||
])
|
||||
: page([
|
||||
event(4, 'tool.completed', { summary: 'Tests passed', success: true }),
|
||||
event(5, 'run.completed'),
|
||||
]);
|
||||
}
|
||||
if (requestPath === `/api/run-approvals/${approval.id}`) return approval;
|
||||
if (requestPath === `/api/run-approvals/${approval.id}/decision`) {
|
||||
decisions.push(JSON.parse(String(options?.body)));
|
||||
return { ...approval, status: 'approved' };
|
||||
}
|
||||
throw new Error(`Unexpected API request: ${requestPath}`);
|
||||
}) as AcpApiClient;
|
||||
const server = new AcpServerView({
|
||||
api: fakeApi,
|
||||
agent: 'codex',
|
||||
pollIntervalMs: 1,
|
||||
now: () => Date.parse('2026-07-24T12:00:00.000Z'),
|
||||
write: (record) => writes.push(record as unknown as Record<string, unknown>),
|
||||
});
|
||||
|
||||
await server.acceptLine(
|
||||
request(1, 'initialize', {
|
||||
protocolVersion: 1,
|
||||
clientCapabilities: {},
|
||||
clientInfo: { name: 'fixture', version: '1.0.0' },
|
||||
})
|
||||
);
|
||||
await server.acceptLine(
|
||||
request(2, 'session/new', {
|
||||
cwd: '/tmp/task_1',
|
||||
mcpServers: [],
|
||||
_meta: { 'veritas/taskId': 'task_1' },
|
||||
})
|
||||
);
|
||||
const sessionId = String((writes.at(-1)?.result as Record<string, unknown>).sessionId);
|
||||
const prompt = server.acceptLine(
|
||||
request(3, 'session/prompt', {
|
||||
sessionId,
|
||||
prompt: [{ type: 'text', text: 'Implement the scoped task' }],
|
||||
})
|
||||
);
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(writes.some((record) => record.method === 'session/request_permission')).toBe(true);
|
||||
});
|
||||
const permission = writes.find((record) => record.method === 'session/request_permission');
|
||||
await server.acceptLine(
|
||||
JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
id: permission?.id,
|
||||
result: { outcome: { outcome: 'selected', optionId: 'allow_once' } },
|
||||
})
|
||||
);
|
||||
await prompt;
|
||||
|
||||
expect(decisions).toEqual([
|
||||
{
|
||||
decision: 'approved',
|
||||
expectedRevision: 1,
|
||||
expectedActionHash: approval.actionHash,
|
||||
note: 'ACP client selected allow once.',
|
||||
},
|
||||
]);
|
||||
expect(
|
||||
writes.filter((record) => record.method === 'session/update').map((record) => record.params)
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
sessionId,
|
||||
update: expect.objectContaining({ sessionUpdate: 'agent_message_chunk' }),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
sessionId,
|
||||
update: expect.objectContaining({ sessionUpdate: 'tool_call' }),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
sessionId,
|
||||
update: expect.objectContaining({ sessionUpdate: 'tool_call_update' }),
|
||||
}),
|
||||
])
|
||||
);
|
||||
expect(writes.at(-1)).toMatchObject({
|
||||
jsonrpc: '2.0',
|
||||
id: 3,
|
||||
result: { stopReason: 'end_turn' },
|
||||
});
|
||||
});
|
||||
|
||||
it('loads and replays a durable attempt, then cancels it without a stop fallback', async () => {
|
||||
const writes: Record<string, unknown>[] = [];
|
||||
const calls: Array<{ path: string; body?: unknown }> = [];
|
||||
const attempt = {
|
||||
id: 'attempt_1',
|
||||
agent: 'codex',
|
||||
status: 'running',
|
||||
started: '2026-07-24T12:00:00.000Z',
|
||||
conversation: {
|
||||
schemaVersion: 'conversation-lifecycle/v1',
|
||||
mode: 'fresh',
|
||||
intent: 'fresh',
|
||||
state: 'active',
|
||||
contextWindow: {
|
||||
posture: 'healthy',
|
||||
measuredAt: '2026-07-24T12:00:00.000Z',
|
||||
},
|
||||
createdAt: '2026-07-24T12:00:00.000Z',
|
||||
updatedAt: '2026-07-24T12:00:00.000Z',
|
||||
},
|
||||
} as Task['attempt'];
|
||||
const fakeApi = vi.fn(async (requestPath: string, options?: RequestInit) => {
|
||||
calls.push({
|
||||
path: requestPath,
|
||||
...(options?.body ? { body: JSON.parse(String(options.body)) } : {}),
|
||||
});
|
||||
if (requestPath === '/api/tasks') return [task(attempt)];
|
||||
if (requestPath.includes('/events?')) {
|
||||
return page([event(2, 'message.delta', { summary: 'replayed' })]);
|
||||
}
|
||||
if (requestPath.endsWith('/conversation/interrupt')) {
|
||||
return { delivered: true };
|
||||
}
|
||||
throw new Error(`Unexpected API request: ${requestPath}`);
|
||||
}) as AcpApiClient;
|
||||
const server = new AcpServerView({
|
||||
api: fakeApi,
|
||||
boundTaskId: 'task_1',
|
||||
write: (record) => writes.push(record as unknown as Record<string, unknown>),
|
||||
});
|
||||
const sessionId = `vkacp_${Buffer.from('task_1').toString('base64url')}`;
|
||||
|
||||
await server.acceptLine(
|
||||
request(1, 'session/load', {
|
||||
sessionId,
|
||||
cwd: '/tmp/task_1',
|
||||
mcpServers: [],
|
||||
_meta: { 'veritas/afterSequence': 1 },
|
||||
})
|
||||
);
|
||||
await vi.waitFor(() => {
|
||||
expect(writes.some((record) => record.method === 'session/update')).toBe(true);
|
||||
});
|
||||
await server.acceptLine(
|
||||
JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: 'session/cancel',
|
||||
params: { sessionId },
|
||||
})
|
||||
);
|
||||
|
||||
expect(calls).toContainEqual({
|
||||
path: '/api/agents/task_1/conversation/interrupt',
|
||||
body: { attemptId: 'attempt_1' },
|
||||
});
|
||||
expect(calls.some((call) => call.path.endsWith('/stop'))).toBe(false);
|
||||
});
|
||||
|
||||
it('disconnects the protocol view without stopping or interrupting the durable run', async () => {
|
||||
const calls: string[] = [];
|
||||
const fakeApi = vi.fn(async (requestPath: string) => {
|
||||
calls.push(requestPath);
|
||||
if (requestPath === '/api/tasks') return [task()];
|
||||
if (requestPath.endsWith('/status')) return { running: false };
|
||||
if (requestPath.endsWith('/conversation/fresh')) return { attemptId: 'attempt_1' };
|
||||
if (requestPath.includes('/events?')) return page([]);
|
||||
throw new Error(`Unexpected API request: ${requestPath}`);
|
||||
}) as AcpApiClient;
|
||||
const server = new AcpServerView({
|
||||
api: fakeApi,
|
||||
boundTaskId: 'task_1',
|
||||
pollIntervalMs: 1,
|
||||
write: vi.fn(),
|
||||
});
|
||||
const sessionId = `vkacp_${Buffer.from('task_1').toString('base64url')}`;
|
||||
|
||||
await server.acceptLine(request(1, 'session/new', { cwd: '/tmp/task_1', mcpServers: [] }));
|
||||
const prompt = server.acceptLine(
|
||||
request(2, 'session/prompt', {
|
||||
sessionId,
|
||||
prompt: [{ type: 'text', text: 'Keep the durable run alive' }],
|
||||
})
|
||||
);
|
||||
await vi.waitFor(() => {
|
||||
expect(calls.some((call) => call.includes('/events?'))).toBe(true);
|
||||
});
|
||||
server.disconnect();
|
||||
await prompt;
|
||||
|
||||
expect(calls.some((call) => call.endsWith('/stop'))).toBe(false);
|
||||
expect(calls.some((call) => call.endsWith('/conversation/interrupt'))).toBe(false);
|
||||
});
|
||||
|
||||
it('uses the same ACP client contract for two configured providers', async () => {
|
||||
const launchedAgents: unknown[] = [];
|
||||
|
||||
for (const agent of ['codex', 'claude']) {
|
||||
const writes: Record<string, unknown>[] = [];
|
||||
const fakeApi = vi.fn(async (requestPath: string, options?: RequestInit) => {
|
||||
if (requestPath === '/api/tasks') return [task()];
|
||||
if (requestPath.endsWith('/status')) return { running: false };
|
||||
if (requestPath.endsWith('/conversation/fresh')) {
|
||||
launchedAgents.push(JSON.parse(String(options?.body)).agent);
|
||||
return { attemptId: 'attempt_1' };
|
||||
}
|
||||
if (requestPath.includes('/events?')) return page([event(1, 'run.completed')]);
|
||||
throw new Error(`Unexpected API request: ${requestPath}`);
|
||||
}) as AcpApiClient;
|
||||
const server = new AcpServerView({
|
||||
api: fakeApi,
|
||||
agent,
|
||||
boundTaskId: 'task_1',
|
||||
write: (record) => writes.push(record as unknown as Record<string, unknown>),
|
||||
});
|
||||
|
||||
await server.acceptLine(request(1, 'session/new', { cwd: '/tmp/task_1', mcpServers: [] }));
|
||||
const sessionId = String((writes.at(-1)?.result as Record<string, unknown>).sessionId);
|
||||
await server.acceptLine(
|
||||
request(2, 'session/prompt', {
|
||||
sessionId,
|
||||
prompt: [{ type: 'text', text: 'Use the selected provider' }],
|
||||
})
|
||||
);
|
||||
expect(writes.at(-1)).toMatchObject({
|
||||
id: 2,
|
||||
result: { stopReason: 'end_turn' },
|
||||
});
|
||||
}
|
||||
|
||||
expect(launchedAgents).toEqual(['codex', 'claude']);
|
||||
});
|
||||
|
||||
it('fails malformed, unsupported, and client-owned tool-catalog requests closed', async () => {
|
||||
const writes: Record<string, unknown>[] = [];
|
||||
const fakeApi = vi.fn(async (requestPath: string) => {
|
||||
if (requestPath === '/api/tasks') return [task()];
|
||||
throw new Error(`Unexpected API request: ${requestPath}`);
|
||||
}) as AcpApiClient;
|
||||
const server = new AcpServerView({
|
||||
api: fakeApi,
|
||||
boundTaskId: 'task_1',
|
||||
write: (record) => writes.push(record as unknown as Record<string, unknown>),
|
||||
});
|
||||
|
||||
await server.acceptLine('{bad');
|
||||
await server.acceptLine(request(2, 'unknown/method', {}));
|
||||
await server.acceptLine(
|
||||
request(3, 'session/new', {
|
||||
cwd: '/tmp/task_1',
|
||||
mcpServers: [{ name: 'unowned', command: 'node', args: [], env: [] }],
|
||||
})
|
||||
);
|
||||
|
||||
expect(writes.map((record) => (record.error as Record<string, unknown>)?.code)).toEqual([
|
||||
-32700, -32601, -32003,
|
||||
]);
|
||||
});
|
||||
|
||||
it('reports API-backed readiness without claiming provider capabilities', async () => {
|
||||
const ready = await readAcpStatus(
|
||||
vi.fn(async () => ({ role: 'admin', workspaceId: 'local' })) as AcpApiClient
|
||||
);
|
||||
expect(ready).toMatchObject({
|
||||
protocolVersion: 1,
|
||||
transport: 'stdio',
|
||||
ready: true,
|
||||
providerNeutral: true,
|
||||
durableRuns: true,
|
||||
role: 'admin',
|
||||
workspaceId: 'local',
|
||||
});
|
||||
|
||||
const blocked = await readAcpStatus(
|
||||
vi.fn(async () => {
|
||||
throw new Error('API unavailable');
|
||||
}) as AcpApiClient
|
||||
);
|
||||
expect(blocked).toMatchObject({ ready: false, error: 'API unavailable' });
|
||||
});
|
||||
});
|
||||
|
||||
function page(events: RunEventEnvelope[]): RunEventPage {
|
||||
return {
|
||||
schemaVersion: 'run-event/v1',
|
||||
taskId: 'task_1',
|
||||
attemptId: 'attempt_1',
|
||||
events,
|
||||
nextCursor: events.at(-1)?.sequence ?? 0,
|
||||
hasMore: false,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,438 +0,0 @@
|
|||
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,7 +1,4 @@
|
|||
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 { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { Command } from 'commander';
|
||||
|
||||
const { mockApi, mockFindTask } = vi.hoisted(() => ({
|
||||
|
|
@ -14,23 +11,6 @@ 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();
|
||||
|
|
@ -58,10 +38,6 @@ describe('vk agent runtime capability controls', () => {
|
|||
'task_1',
|
||||
'--agent',
|
||||
'codex',
|
||||
'--phase',
|
||||
'implement',
|
||||
'--parent-attempt',
|
||||
'attempt_parent',
|
||||
'--require-capability',
|
||||
'tool.mcp',
|
||||
'output.structured',
|
||||
|
|
@ -70,43 +46,12 @@ describe('vk agent runtime capability controls', () => {
|
|||
{ from: 'user' }
|
||||
);
|
||||
|
||||
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', {
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/start', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
agent: 'codex',
|
||||
profileId: undefined,
|
||||
phase: 'plan',
|
||||
requiredRuntimeCapabilities: undefined,
|
||||
commitPolicy: undefined,
|
||||
parentAttemptId: 'attempt_parent',
|
||||
requiredRuntimeCapabilities: ['tool.mcp', 'output.structured'],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
|
@ -121,9 +66,14 @@ describe('vk agent runtime capability controls', () => {
|
|||
{ from: 'user' }
|
||||
);
|
||||
|
||||
expectLaunchBody({
|
||||
agent: 'codex',
|
||||
commitPolicy: 'forbidden',
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/start', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
agent: 'codex',
|
||||
profileId: undefined,
|
||||
requiredRuntimeCapabilities: undefined,
|
||||
commitPolicy: 'forbidden',
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -169,237 +119,6 @@ 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();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(
|
||||
[
|
||||
'agent:fork',
|
||||
'task_1',
|
||||
'--source-attempt',
|
||||
'attempt_parent',
|
||||
'--message',
|
||||
'Explore the alternate fix',
|
||||
'--fork-turn',
|
||||
'turn_7',
|
||||
'--phase',
|
||||
'explore',
|
||||
'--require-capability',
|
||||
'tool.mcp',
|
||||
'--json',
|
||||
],
|
||||
{ from: 'user' }
|
||||
);
|
||||
|
||||
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'],
|
||||
});
|
||||
});
|
||||
|
||||
it('binds compact controls to the exact active attempt', async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerAgentCommands(program);
|
||||
|
||||
await program.parseAsync(['agent:compact', 'task_1', '--attempt', 'attempt_1', '--json'], {
|
||||
from: 'user',
|
||||
});
|
||||
|
||||
expect(mockApi).toHaveBeenCalledWith('/api/agents/task_1/conversation/compact', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ attemptId: 'attempt_1' }),
|
||||
});
|
||||
});
|
||||
|
||||
it('forwards attempt and manifest provenance when completing a run', async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
|
|
@ -431,83 +150,4 @@ 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',
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -90,32 +90,6 @@ describe('CLI API permission preflight', () => {
|
|||
expect(fetchMock.mock.calls[0][0]).toBe('http://vk.test/api/auth/context');
|
||||
});
|
||||
|
||||
it('requires agent write permission to start a fresh conversation', async () => {
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
jsonResponse({
|
||||
role: 'read-only',
|
||||
isLocalhost: false,
|
||||
permissions: ['agent:read'],
|
||||
})
|
||||
);
|
||||
globalThis.fetch = fetchMock as unknown as typeof fetch;
|
||||
|
||||
const api = createGuardedApiClient('http://vk.test', 'reader-key');
|
||||
|
||||
await expect(
|
||||
api('/api/agents/task_1/conversation/fresh', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ message: 'blocked' }),
|
||||
})
|
||||
).rejects.toMatchObject({
|
||||
required: ['agent:write'],
|
||||
path: '/api/agents/task_1/conversation/fresh',
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('requires task write permission for delegated workspace intake', async () => {
|
||||
const fetchMock = vi.fn().mockResolvedValue(
|
||||
jsonResponse({
|
||||
|
|
|
|||
|
|
@ -19,25 +19,6 @@ function doctorFetch(routes: Record<string, Response>) {
|
|||
}) as unknown as typeof fetch;
|
||||
}
|
||||
|
||||
function compatibilityResponse(supportStatuses: unknown[]): Response {
|
||||
return jsonResponse({
|
||||
schemaVersion: 'harness-compatibility-matrix/v1',
|
||||
generatedAt: '2026-06-04T07:00:00.000Z',
|
||||
probeRevision: 14,
|
||||
digest: 'a'.repeat(64),
|
||||
tierDefinitions: {},
|
||||
records: [
|
||||
{
|
||||
profileId: 'openai-codex-app-server',
|
||||
testedVersions: ['codex-cli 0.145.0'],
|
||||
sourceAvailability: 'open-source',
|
||||
certification: { status: 'not-run' },
|
||||
},
|
||||
],
|
||||
supportStatuses,
|
||||
});
|
||||
}
|
||||
|
||||
const baseRoutes: Record<string, Response> = {
|
||||
'/api/health': jsonResponse({ ok: true, version: '4.3.2', uptimeMs: 1000 }),
|
||||
'/api/auth/context': jsonResponse({
|
||||
|
|
@ -56,23 +37,6 @@ const baseRoutes: Record<string, Response> = {
|
|||
provider: 'codex-cli',
|
||||
},
|
||||
]),
|
||||
'/api/config/harness-compatibility': compatibilityResponse([
|
||||
{
|
||||
agentType: 'codex',
|
||||
profileId: 'openai-codex-cli',
|
||||
adapterId: 'codex-cli',
|
||||
transport: 'process-jsonl',
|
||||
supportTier: 'configured',
|
||||
reason: 'Certification evidence is not current.',
|
||||
failureClass: 'none',
|
||||
checkedAt: '2026-06-04T07:00:00.000Z',
|
||||
enabled: true,
|
||||
executableFound: true,
|
||||
authenticated: true,
|
||||
diagnosticCommands: ['codex --version', 'codex login status'],
|
||||
remediation: ['Run vk doctor.'],
|
||||
},
|
||||
]),
|
||||
'/api/agents/routing': jsonResponse({
|
||||
enabled: true,
|
||||
defaultAgent: 'codex',
|
||||
|
|
@ -90,10 +54,6 @@ const baseRoutes: Record<string, Response> = {
|
|||
cli: { installed: true, authenticated: true },
|
||||
recommendations: [],
|
||||
}),
|
||||
'/api/integrations/communication/adapters/buzz-default/health': jsonResponse(
|
||||
{ error: 'not found' },
|
||||
404
|
||||
),
|
||||
};
|
||||
|
||||
describe('vk doctor', () => {
|
||||
|
|
@ -120,12 +80,6 @@ describe('vk doctor', () => {
|
|||
expect(report.checks.find((check) => check.id === 'agents')).toMatchObject({
|
||||
status: 'pass',
|
||||
});
|
||||
expect(report.checks.find((check) => check.id === 'harness-support')).toMatchObject({
|
||||
status: 'warn',
|
||||
details: expect.objectContaining({
|
||||
configured: 1,
|
||||
}),
|
||||
});
|
||||
expect(formatDoctorReport(report)).toContain('Doctor result: clean');
|
||||
});
|
||||
|
||||
|
|
@ -172,55 +126,6 @@ describe('vk doctor', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('fails closed for an enabled unsupported harness and preserves safe remediation', async () => {
|
||||
const routes = {
|
||||
...baseRoutes,
|
||||
'/api/config/harness-compatibility': compatibilityResponse([
|
||||
{
|
||||
agentType: 'claude-code',
|
||||
profileId: 'claude-code',
|
||||
transport: 'process-jsonl',
|
||||
supportTier: 'unsupported',
|
||||
reason: 'No executable adapter is registered.',
|
||||
failureClass: 'adapter-unavailable',
|
||||
checkedAt: '2026-06-04T07:00:00.000Z',
|
||||
enabled: true,
|
||||
executableFound: true,
|
||||
authenticated: true,
|
||||
diagnosticCommands: ['claude --version'],
|
||||
remediation: ['Disable this profile or install a supported adapter.'],
|
||||
},
|
||||
]),
|
||||
};
|
||||
|
||||
const report = await runDoctorChecks(
|
||||
{ apiBase: 'http://vk.test', cwd: '/repo', timeoutMs: 1000 },
|
||||
{
|
||||
fetch: doctorFetch(routes),
|
||||
env: {},
|
||||
findProjectRoot: async () => '/repo',
|
||||
countPromptTemplateFiles: async () => 1,
|
||||
resolveCommand: async (command) =>
|
||||
command === 'vk' ? '/repo/cli/dist/index.js' : `/usr/bin/${command}`,
|
||||
now: () => new Date('2026-06-04T07:00:00.000Z'),
|
||||
}
|
||||
);
|
||||
|
||||
expect(report.ok).toBe(false);
|
||||
expect(report.checks.find((check) => check.id === 'harness-support')).toMatchObject({
|
||||
status: 'fail',
|
||||
details: {
|
||||
blocking: [
|
||||
expect.objectContaining({
|
||||
profileId: 'claude-code',
|
||||
diagnosticCommands: ['claude --version'],
|
||||
remediation: ['Disable this profile or install a supported adapter.'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('redacts local paths and webhook secrets from support-safe JSON', async () => {
|
||||
const routes = {
|
||||
...baseRoutes,
|
||||
|
|
@ -273,121 +178,4 @@ describe('vk doctor', () => {
|
|||
status: 'warn',
|
||||
});
|
||||
});
|
||||
|
||||
it('reports exact Buzz health in human and JSON output', async () => {
|
||||
const routes = {
|
||||
...baseRoutes,
|
||||
'/api/integrations/communication/adapters/buzz-default/health': jsonResponse({
|
||||
adapterId: 'buzz-default',
|
||||
status: 'healthy',
|
||||
configured: true,
|
||||
canSend: false,
|
||||
canReceiveReplies: false,
|
||||
checkedAt: '2026-07-23T18:00:00.000Z',
|
||||
detail:
|
||||
'Buzz relay identity, configured signing identity, membership posture, and read capabilities are compatible.',
|
||||
reasonCode: 'ok',
|
||||
buzz: {
|
||||
schemaVersion: 'buzz-compatibility/v1',
|
||||
probeRevision: 1,
|
||||
testedRelease: '0.4.24',
|
||||
testedCommit: '710ed9fff57878a1d69f809b80a6ee0416c53fc4',
|
||||
status: 'healthy',
|
||||
reasonCode: 'ok',
|
||||
detail: 'compatible',
|
||||
configuredRelayHttpUrl: 'https://relay.example.test',
|
||||
resolvedRelayHttpUrl: 'https://relay.example.test',
|
||||
resolvedRelayWebSocketUrl: 'wss://relay.example.test',
|
||||
expectedCommunity: 'relay.example.test',
|
||||
observedCommunity: 'relay.example.test',
|
||||
publicKeyFingerprint: 'abc123abc123',
|
||||
checks: {
|
||||
relayIdentity: 'verified',
|
||||
communityBinding: 'verified',
|
||||
configuredIdentity: 'verified',
|
||||
authentication: 'verified',
|
||||
membership: 'verified',
|
||||
channelRead: 'verified',
|
||||
messageRead: 'verified',
|
||||
},
|
||||
commands: [],
|
||||
evidenceKey: 'safe-evidence',
|
||||
checkedAt: '2026-07-23T18:00:00.000Z',
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const report = await runDoctorChecks(
|
||||
{ apiBase: 'http://vk.test', cwd: '/repo', timeoutMs: 1000 },
|
||||
{
|
||||
fetch: doctorFetch(routes),
|
||||
env: {},
|
||||
findProjectRoot: async () => '/repo',
|
||||
countPromptTemplateFiles: async () => 1,
|
||||
resolveCommand: async (command) =>
|
||||
command === 'vk' ? '/repo/cli/dist/index.js' : `/usr/bin/${command}`,
|
||||
now: () => new Date('2026-07-23T18:00:00.000Z'),
|
||||
}
|
||||
);
|
||||
|
||||
expect(report.checks.find((check) => check.id === 'buzz')).toMatchObject({
|
||||
status: 'pass',
|
||||
details: {
|
||||
status: 'healthy',
|
||||
reasonCode: 'ok',
|
||||
expectedCommunity: 'relay.example.test',
|
||||
observedCommunity: 'relay.example.test',
|
||||
publicKeyFingerprint: 'abc123abc123',
|
||||
testedRelease: '0.4.24',
|
||||
buzz: {
|
||||
checks: {
|
||||
channelRead: 'verified',
|
||||
messageRead: 'verified',
|
||||
},
|
||||
evidenceKey: 'safe-evidence',
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(formatDoctorReport(report)).toContain('[PASS] Buzz compatibility');
|
||||
});
|
||||
|
||||
it('fails doctor for an enabled Buzz membership denial with remediation', async () => {
|
||||
const routes = {
|
||||
...baseRoutes,
|
||||
'/api/integrations/communication/adapters/buzz-default/health': jsonResponse({
|
||||
adapterId: 'buzz-default',
|
||||
status: 'not_member',
|
||||
configured: true,
|
||||
canSend: false,
|
||||
canReceiveReplies: false,
|
||||
checkedAt: '2026-07-23T18:00:00.000Z',
|
||||
detail: 'Buzz authenticated the identity but denied relay membership.',
|
||||
reasonCode: 'relay_membership_required',
|
||||
remediation: 'Add the public identity as a relay member.',
|
||||
}),
|
||||
};
|
||||
|
||||
const report = await runDoctorChecks(
|
||||
{ apiBase: 'http://vk.test', cwd: '/repo', timeoutMs: 1000 },
|
||||
{
|
||||
fetch: doctorFetch(routes),
|
||||
env: {},
|
||||
findProjectRoot: async () => '/repo',
|
||||
countPromptTemplateFiles: async () => 1,
|
||||
resolveCommand: async (command) =>
|
||||
command === 'vk' ? '/repo/cli/dist/index.js' : `/usr/bin/${command}`,
|
||||
now: () => new Date('2026-07-23T18:00:00.000Z'),
|
||||
}
|
||||
);
|
||||
|
||||
expect(report.ok).toBe(false);
|
||||
expect(report.checks.find((check) => check.id === 'buzz')).toMatchObject({
|
||||
status: 'fail',
|
||||
details: {
|
||||
status: 'not_member',
|
||||
reasonCode: 'relay_membership_required',
|
||||
},
|
||||
remediation: 'Add the public identity as a relay member.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,214 +0,0 @@
|
|||
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();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,846 +0,0 @@
|
|||
import { Buffer } from 'node:buffer';
|
||||
import path from 'node:path';
|
||||
import readline from 'node:readline';
|
||||
import type { Readable, Writable } from 'node:stream';
|
||||
import type { Command } from 'commander';
|
||||
import type {
|
||||
AcpContentBlock,
|
||||
AcpJsonRpcId,
|
||||
AcpJsonRpcMessage,
|
||||
AcpPromptResponse,
|
||||
ClientAuthContext,
|
||||
RunApprovalRequest,
|
||||
RunEventEnvelope,
|
||||
RunEventPage,
|
||||
Task,
|
||||
TaskAttempt,
|
||||
} from '@veritas-kanban/shared';
|
||||
import { ACP_PROTOCOL_VERSION } from '@veritas-kanban/shared';
|
||||
import { api } from '../utils/api.js';
|
||||
|
||||
const MAX_PROTOCOL_LINE_BYTES = 1024 * 1024;
|
||||
const DEFAULT_POLL_INTERVAL_MS = 250;
|
||||
const ACP_SERVER_NAME = 'Veritas Kanban';
|
||||
const ACP_SERVER_VERSION = '6.0.0';
|
||||
|
||||
export const ACP_SERVER_METHODS = [
|
||||
'initialize',
|
||||
'session/new',
|
||||
'session/load',
|
||||
'session/resume',
|
||||
'session/prompt',
|
||||
'session/cancel',
|
||||
] as const;
|
||||
|
||||
export type AcpApiClient = <T>(requestPath: string, options?: RequestInit) => Promise<T>;
|
||||
|
||||
export interface AcpServerViewOptions {
|
||||
api?: AcpApiClient;
|
||||
write: (record: AcpJsonRpcMessage) => void;
|
||||
boundTaskId?: string;
|
||||
agent?: string;
|
||||
profileId?: string;
|
||||
pollIntervalMs?: number;
|
||||
now?: () => number;
|
||||
}
|
||||
|
||||
interface ViewSession {
|
||||
sessionId: string;
|
||||
taskId: string;
|
||||
cwd: string;
|
||||
attemptId?: string;
|
||||
cursor: number;
|
||||
busy: boolean;
|
||||
}
|
||||
|
||||
interface MethodOutcome {
|
||||
result: unknown;
|
||||
afterResponse?: () => Promise<void>;
|
||||
}
|
||||
|
||||
interface PendingClientRequest {
|
||||
resolve: (value: unknown) => void;
|
||||
reject: (error: Error) => void;
|
||||
timer: NodeJS.Timeout;
|
||||
}
|
||||
|
||||
export interface AcpStatus {
|
||||
schemaVersion: 'veritas-acp-server-status/v1';
|
||||
protocolVersion: typeof ACP_PROTOCOL_VERSION;
|
||||
transport: 'stdio';
|
||||
ready: boolean;
|
||||
methods: readonly string[];
|
||||
durableRuns: true;
|
||||
providerNeutral: true;
|
||||
role?: string;
|
||||
workspaceId?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class AcpServerView {
|
||||
private readonly apiClient: AcpApiClient;
|
||||
private readonly writeRecord: (record: AcpJsonRpcMessage) => void;
|
||||
private readonly boundTaskId?: string;
|
||||
private readonly agent?: string;
|
||||
private readonly profileId?: string;
|
||||
private readonly pollIntervalMs: number;
|
||||
private readonly now: () => number;
|
||||
private readonly sessions = new Map<string, ViewSession>();
|
||||
private readonly pendingClientRequests = new Map<string | number, PendingClientRequest>();
|
||||
private nextClientRequestId = 1;
|
||||
private disconnected = false;
|
||||
|
||||
constructor(options: AcpServerViewOptions) {
|
||||
this.apiClient = options.api ?? api;
|
||||
this.writeRecord = (record) => {
|
||||
if (!this.disconnected) options.write(record);
|
||||
};
|
||||
this.boundTaskId = options.boundTaskId;
|
||||
this.agent = options.agent;
|
||||
this.profileId = options.profileId;
|
||||
this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
||||
this.now = options.now ?? Date.now;
|
||||
}
|
||||
|
||||
disconnect(): void {
|
||||
this.disconnected = true;
|
||||
for (const pending of this.pendingClientRequests.values()) {
|
||||
clearTimeout(pending.timer);
|
||||
pending.reject(new Error('ACP client disconnected.'));
|
||||
}
|
||||
this.pendingClientRequests.clear();
|
||||
}
|
||||
|
||||
async acceptLine(line: string): Promise<void> {
|
||||
if (Buffer.byteLength(line, 'utf8') > MAX_PROTOCOL_LINE_BYTES) {
|
||||
this.writeError(null, -32600, 'ACP record exceeds the 1 MiB limit.');
|
||||
return;
|
||||
}
|
||||
let record: unknown;
|
||||
try {
|
||||
record = JSON.parse(line);
|
||||
} catch {
|
||||
this.writeError(null, -32700, 'Invalid JSON.');
|
||||
return;
|
||||
}
|
||||
if (!isRecord(record) || record.jsonrpc !== '2.0') {
|
||||
this.writeError(null, -32600, 'Invalid JSON-RPC record.');
|
||||
return;
|
||||
}
|
||||
if ('result' in record || 'error' in record) {
|
||||
this.acceptClientResponse(record);
|
||||
return;
|
||||
}
|
||||
if (typeof record.method !== 'string') {
|
||||
this.writeError(
|
||||
validId(record.id) ? record.id : null,
|
||||
-32600,
|
||||
'JSON-RPC method is required.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const id = validId(record.id) ? record.id : undefined;
|
||||
if (id === undefined) {
|
||||
await this.handleNotification(record.method, record.params);
|
||||
return;
|
||||
}
|
||||
await this.handleRequest(id, record.method, record.params);
|
||||
}
|
||||
|
||||
private async handleRequest(id: AcpJsonRpcId, method: string, params: unknown): Promise<void> {
|
||||
try {
|
||||
const outcome = await this.dispatch(method, params);
|
||||
this.writeRecord({ jsonrpc: '2.0', id, result: outcome.result });
|
||||
if (outcome.afterResponse) void outcome.afterResponse();
|
||||
} catch (error) {
|
||||
const rpcError = error instanceof AcpViewError ? error : AcpViewError.internal(error);
|
||||
this.writeError(id, rpcError.code, rpcError.message, rpcError.data);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleNotification(method: string, params: unknown): Promise<void> {
|
||||
if (method !== 'session/cancel') return;
|
||||
try {
|
||||
const input = requiredRecord(params, 'session/cancel params');
|
||||
const session = this.requireSession(requiredString(input.sessionId, 'sessionId'));
|
||||
if (!session.attemptId) return;
|
||||
await this.apiClient(
|
||||
`/api/agents/${encodeURIComponent(session.taskId)}/conversation/interrupt`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ attemptId: session.attemptId }),
|
||||
}
|
||||
);
|
||||
} catch {
|
||||
// Notifications have no response. Durable run state remains authoritative.
|
||||
}
|
||||
}
|
||||
|
||||
private async dispatch(method: string, params: unknown): Promise<MethodOutcome> {
|
||||
switch (method) {
|
||||
case 'initialize':
|
||||
return this.initialize(params);
|
||||
case 'session/new':
|
||||
return this.newSession(params);
|
||||
case 'session/load':
|
||||
case 'session/resume':
|
||||
return this.loadSession(params);
|
||||
case 'session/prompt':
|
||||
return this.prompt(params);
|
||||
default:
|
||||
throw new AcpViewError(-32601, `Unsupported ACP method: ${method}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async initialize(params: unknown): Promise<MethodOutcome> {
|
||||
const input = requiredRecord(params, 'initialize params');
|
||||
if (input.protocolVersion !== ACP_PROTOCOL_VERSION) {
|
||||
throw new AcpViewError(-32602, 'Unsupported ACP protocol version.', {
|
||||
expected: ACP_PROTOCOL_VERSION,
|
||||
received: input.protocolVersion,
|
||||
});
|
||||
}
|
||||
await this.apiClient<ClientAuthContext>('/api/auth/context');
|
||||
return {
|
||||
result: {
|
||||
protocolVersion: ACP_PROTOCOL_VERSION,
|
||||
agentCapabilities: {
|
||||
loadSession: true,
|
||||
promptCapabilities: { image: false, audio: false, embeddedContext: false },
|
||||
mcpCapabilities: { http: false, sse: false },
|
||||
sessionCapabilities: { resume: {} },
|
||||
},
|
||||
agentInfo: {
|
||||
name: ACP_SERVER_NAME,
|
||||
title: ACP_SERVER_NAME,
|
||||
version: ACP_SERVER_VERSION,
|
||||
},
|
||||
_meta: {
|
||||
'veritas/providerNeutral': true,
|
||||
'veritas/durableRuns': true,
|
||||
'veritas/supportedMethods': ACP_SERVER_METHODS,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async newSession(params: unknown): Promise<MethodOutcome> {
|
||||
const input = requiredRecord(params, 'session/new params');
|
||||
rejectClientMcp(input.mcpServers);
|
||||
const metadata = optionalRecord(input._meta);
|
||||
const taskReference =
|
||||
this.boundTaskId ??
|
||||
optionalString(metadata['veritas/taskId']) ??
|
||||
optionalString(metadata.veritasTaskId);
|
||||
if (!taskReference) {
|
||||
throw new AcpViewError(
|
||||
-32602,
|
||||
'Bind the server with --task or pass _meta["veritas/taskId"].'
|
||||
);
|
||||
}
|
||||
const task = await this.resolveTask(taskReference);
|
||||
const cwd = requiredString(input.cwd, 'cwd');
|
||||
this.assertTaskWorktree(task, cwd);
|
||||
const sessionId = sessionIdForTask(task.id);
|
||||
this.sessions.set(sessionId, {
|
||||
sessionId,
|
||||
taskId: task.id,
|
||||
cwd,
|
||||
cursor: 0,
|
||||
busy: false,
|
||||
});
|
||||
return {
|
||||
result: {
|
||||
sessionId,
|
||||
_meta: { 'veritas/taskId': task.id },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async loadSession(params: unknown): Promise<MethodOutcome> {
|
||||
const input = requiredRecord(params, 'session/load params');
|
||||
rejectClientMcp(input.mcpServers);
|
||||
const sessionId = requiredString(input.sessionId, 'sessionId');
|
||||
const taskId = taskIdFromSession(sessionId);
|
||||
if (this.boundTaskId) {
|
||||
const boundTask = await this.resolveTask(this.boundTaskId);
|
||||
if (boundTask.id !== taskId) {
|
||||
throw new AcpViewError(-32003, 'ACP session is outside the bound task scope.');
|
||||
}
|
||||
}
|
||||
const task = await this.resolveTask(taskId);
|
||||
const cwd = requiredString(input.cwd, 'cwd');
|
||||
this.assertTaskWorktree(task, cwd);
|
||||
const metadata = optionalRecord(input._meta);
|
||||
const requestedAttemptId = optionalString(metadata['veritas/attemptId']);
|
||||
const attempt = requestedAttemptId
|
||||
? findTaskAttempt(task, requestedAttemptId)
|
||||
: latestTaskAttempt(task);
|
||||
if (requestedAttemptId && !attempt) {
|
||||
throw new AcpViewError(-32602, 'Requested Veritas attempt was not found.');
|
||||
}
|
||||
const afterSequence = optionalNonNegativeInteger(metadata['veritas/afterSequence']) ?? 0;
|
||||
const session: ViewSession = {
|
||||
sessionId,
|
||||
taskId: task.id,
|
||||
cwd,
|
||||
attemptId: attempt?.id,
|
||||
cursor: afterSequence,
|
||||
busy: false,
|
||||
};
|
||||
this.sessions.set(sessionId, session);
|
||||
return {
|
||||
result: {},
|
||||
...(attempt
|
||||
? {
|
||||
afterResponse: async () => {
|
||||
await this.replayAvailable(session);
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
private async prompt(params: unknown): Promise<MethodOutcome> {
|
||||
const input = requiredRecord(params, 'session/prompt params');
|
||||
const session = this.requireSession(requiredString(input.sessionId, 'sessionId'));
|
||||
if (session.busy) throw new AcpViewError(-32004, 'An ACP prompt is already active.');
|
||||
const message = promptText(input.prompt);
|
||||
session.busy = true;
|
||||
try {
|
||||
const task = await this.resolveTask(session.taskId);
|
||||
this.assertTaskWorktree(task, session.cwd);
|
||||
const status = await this.apiClient<{ running: boolean; attemptId?: string }>(
|
||||
`/api/agents/${encodeURIComponent(task.id)}/status`
|
||||
);
|
||||
if (status.running) {
|
||||
throw new AcpViewError(-32004, 'The scoped Veritas task already has an active turn.', {
|
||||
attemptId: status.attemptId,
|
||||
});
|
||||
}
|
||||
const source = session.attemptId
|
||||
? findTaskAttempt(task, session.attemptId)
|
||||
: latestTaskAttempt(task);
|
||||
const result = source?.conversation
|
||||
? await this.apiClient<{ attemptId: string }>(
|
||||
`/api/agents/${encodeURIComponent(task.id)}/conversation/follow-up`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
sourceAttemptId: source.id,
|
||||
message,
|
||||
profileId: this.profileId,
|
||||
}),
|
||||
}
|
||||
)
|
||||
: await this.apiClient<{ attemptId: string }>(
|
||||
`/api/agents/${encodeURIComponent(task.id)}/conversation/fresh`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
agent: this.profileId ? undefined : this.agent,
|
||||
profileId: this.profileId,
|
||||
}),
|
||||
}
|
||||
);
|
||||
session.attemptId = result.attemptId;
|
||||
session.cursor = 0;
|
||||
const response = await this.streamUntilTerminal(session);
|
||||
return { result: response };
|
||||
} finally {
|
||||
session.busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async streamUntilTerminal(session: ViewSession): Promise<AcpPromptResponse> {
|
||||
for (;;) {
|
||||
if (this.disconnected) {
|
||||
throw new AcpViewError(-32006, 'ACP client disconnected from the durable run.');
|
||||
}
|
||||
const page = await this.readEvents(session);
|
||||
for (const event of page.events) {
|
||||
const terminal = await this.projectEvent(session, event);
|
||||
session.cursor = Math.max(session.cursor, event.sequence);
|
||||
if (terminal) return terminal;
|
||||
}
|
||||
if (page.hasMore) continue;
|
||||
await delay(this.pollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private async replayAvailable(session: ViewSession): Promise<void> {
|
||||
if (!session.attemptId) return;
|
||||
for (;;) {
|
||||
if (this.disconnected) return;
|
||||
const page = await this.readEvents(session);
|
||||
for (const event of page.events) {
|
||||
await this.projectEvent(session, event);
|
||||
session.cursor = Math.max(session.cursor, event.sequence);
|
||||
}
|
||||
if (!page.hasMore) return;
|
||||
}
|
||||
}
|
||||
|
||||
private readEvents(session: ViewSession): Promise<RunEventPage> {
|
||||
if (!session.attemptId) throw new AcpViewError(-32002, 'ACP session has no Veritas attempt.');
|
||||
const query = new URLSearchParams({
|
||||
afterSequence: String(session.cursor),
|
||||
limit: '250',
|
||||
});
|
||||
return this.apiClient<RunEventPage>(
|
||||
`/api/agents/${encodeURIComponent(session.taskId)}/attempts/${encodeURIComponent(
|
||||
session.attemptId
|
||||
)}/events?${query.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
private async projectEvent(
|
||||
session: ViewSession,
|
||||
event: RunEventEnvelope
|
||||
): Promise<AcpPromptResponse | undefined> {
|
||||
if (event.kind === 'approval.requested') {
|
||||
await this.relayApproval(session, event);
|
||||
return undefined;
|
||||
}
|
||||
const update = eventToSessionUpdate(event);
|
||||
if (update) {
|
||||
this.writeRecord({
|
||||
jsonrpc: '2.0',
|
||||
method: 'session/update',
|
||||
params: {
|
||||
sessionId: session.sessionId,
|
||||
update,
|
||||
_meta: {
|
||||
'veritas/eventId': event.eventId,
|
||||
'veritas/sequence': event.sequence,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (event.kind === 'run.completed') {
|
||||
return {
|
||||
stopReason: 'end_turn',
|
||||
...(event.payload.usage ? { usage: event.payload.usage } : {}),
|
||||
_meta: { 'veritas/eventId': event.eventId, 'veritas/sequence': event.sequence },
|
||||
};
|
||||
}
|
||||
if (event.kind === 'run.interrupted') {
|
||||
return {
|
||||
stopReason: 'cancelled',
|
||||
_meta: { 'veritas/eventId': event.eventId, 'veritas/sequence': event.sequence },
|
||||
};
|
||||
}
|
||||
if (event.kind === 'run.failed') {
|
||||
return {
|
||||
stopReason: 'refusal',
|
||||
_meta: { 'veritas/eventId': event.eventId, 'veritas/sequence': event.sequence },
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async relayApproval(session: ViewSession, event: RunEventEnvelope): Promise<void> {
|
||||
const approvalId = optionalString(event.payload.approvalId);
|
||||
if (!approvalId) throw new AcpViewError(-32005, 'Approval event is missing its durable ID.');
|
||||
const approval = await this.apiClient<RunApprovalRequest>(
|
||||
`/api/run-approvals/${encodeURIComponent(approvalId)}`
|
||||
);
|
||||
const expiresIn = Math.max(1, Date.parse(approval.expiresAt) - this.now());
|
||||
let response: unknown;
|
||||
let timedOut = false;
|
||||
try {
|
||||
response = await this.requestClient(
|
||||
'session/request_permission',
|
||||
{
|
||||
sessionId: session.sessionId,
|
||||
toolCall: {
|
||||
toolCallId: approval.providerRequestId,
|
||||
title: approval.action,
|
||||
name: approval.actionClass,
|
||||
kind: approval.actionClass,
|
||||
status: 'pending',
|
||||
rawInput: {
|
||||
details: approval.details,
|
||||
resourceScope: approval.resourceScope,
|
||||
riskClass: approval.riskClass,
|
||||
policyReason: approval.policyReason,
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{ optionId: 'allow_once', name: 'Allow once', kind: 'allow_once' },
|
||||
{ optionId: 'reject_once', name: 'Reject', kind: 'reject_once' },
|
||||
],
|
||||
},
|
||||
expiresIn
|
||||
);
|
||||
} catch {
|
||||
timedOut = true;
|
||||
}
|
||||
const outcome = optionalRecord(optionalRecord(response).outcome);
|
||||
const selected = optionalString(outcome.outcome);
|
||||
const selectedOption = optionalString(outcome.optionId);
|
||||
const approved = selected === 'selected' && selectedOption === 'allow_once';
|
||||
await this.apiClient(`/api/run-approvals/${encodeURIComponent(approval.id)}/decision`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
decision: approved ? 'approved' : 'rejected',
|
||||
expectedRevision: approval.revision,
|
||||
expectedActionHash: approval.actionHash,
|
||||
note: timedOut
|
||||
? 'ACP client permission request timed out.'
|
||||
: approved
|
||||
? 'ACP client selected allow once.'
|
||||
: 'ACP client denied or cancelled the request.',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private requestClient(method: string, params: unknown, timeoutMs: number): Promise<unknown> {
|
||||
const id = `vk-client-${this.nextClientRequestId++}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
this.pendingClientRequests.delete(id);
|
||||
reject(new Error('ACP client request timed out.'));
|
||||
}, timeoutMs);
|
||||
this.pendingClientRequests.set(id, { resolve, reject, timer });
|
||||
this.writeRecord({ jsonrpc: '2.0', id, method, params });
|
||||
});
|
||||
}
|
||||
|
||||
private acceptClientResponse(record: Record<string, unknown>): void {
|
||||
if (!validId(record.id)) return;
|
||||
const pending = this.pendingClientRequests.get(record.id);
|
||||
if (!pending) return;
|
||||
this.pendingClientRequests.delete(record.id);
|
||||
clearTimeout(pending.timer);
|
||||
if (isRecord(record.error)) {
|
||||
pending.reject(
|
||||
new Error(optionalString(record.error.message) ?? 'ACP client request failed.')
|
||||
);
|
||||
return;
|
||||
}
|
||||
pending.resolve(record.result);
|
||||
}
|
||||
|
||||
private async resolveTask(reference: string): Promise<Task> {
|
||||
const tasks = await this.apiClient<Task[]>('/api/tasks');
|
||||
const exact = tasks.find((task) => task.id === reference);
|
||||
const suffixMatches = exact ? [] : tasks.filter((task) => task.id.endsWith(reference));
|
||||
const task = exact ?? (suffixMatches.length === 1 ? suffixMatches[0] : undefined);
|
||||
if (!task) {
|
||||
throw new AcpViewError(
|
||||
-32003,
|
||||
suffixMatches.length > 1 ? 'Task reference is ambiguous.' : 'Task was not found.'
|
||||
);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
private assertTaskWorktree(task: Task, cwd: string): void {
|
||||
const worktree = task.git?.worktreePath;
|
||||
if (!worktree) throw new AcpViewError(-32003, 'Task has no active worktree.');
|
||||
if (path.resolve(worktree) !== path.resolve(cwd)) {
|
||||
throw new AcpViewError(-32003, 'ACP cwd does not match the task worktree.');
|
||||
}
|
||||
}
|
||||
|
||||
private requireSession(sessionId: string): ViewSession {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (!session) throw new AcpViewError(-32002, 'ACP session is not loaded in this process.');
|
||||
return session;
|
||||
}
|
||||
|
||||
private writeError(id: AcpJsonRpcId | null, code: number, message: string, data?: unknown): void {
|
||||
this.writeRecord({
|
||||
jsonrpc: '2.0',
|
||||
id,
|
||||
error: {
|
||||
code,
|
||||
message,
|
||||
...(data === undefined ? {} : { data }),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function readAcpStatus(apiClient: AcpApiClient = api): Promise<AcpStatus> {
|
||||
try {
|
||||
const context = await apiClient<ClientAuthContext>('/api/auth/context');
|
||||
return {
|
||||
schemaVersion: 'veritas-acp-server-status/v1',
|
||||
protocolVersion: ACP_PROTOCOL_VERSION,
|
||||
transport: 'stdio',
|
||||
ready: true,
|
||||
methods: ACP_SERVER_METHODS,
|
||||
durableRuns: true,
|
||||
providerNeutral: true,
|
||||
role: context.role,
|
||||
workspaceId: context.workspaceId,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
schemaVersion: 'veritas-acp-server-status/v1',
|
||||
protocolVersion: ACP_PROTOCOL_VERSION,
|
||||
transport: 'stdio',
|
||||
ready: false,
|
||||
methods: ACP_SERVER_METHODS,
|
||||
durableRuns: true,
|
||||
providerNeutral: true,
|
||||
error: boundedError(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function runAcpStdioServer(options: {
|
||||
stdin?: Readable;
|
||||
stdout?: Writable;
|
||||
stderr?: Writable;
|
||||
boundTaskId?: string;
|
||||
agent?: string;
|
||||
profileId?: string;
|
||||
api?: AcpApiClient;
|
||||
pollIntervalMs?: number;
|
||||
}): void {
|
||||
const input = options.stdin ?? process.stdin;
|
||||
const output = options.stdout ?? process.stdout;
|
||||
const errors = options.stderr ?? process.stderr;
|
||||
const server = new AcpServerView({
|
||||
...(options.api ? { api: options.api } : {}),
|
||||
boundTaskId: options.boundTaskId,
|
||||
agent: options.agent,
|
||||
profileId: options.profileId,
|
||||
pollIntervalMs: options.pollIntervalMs,
|
||||
write: (record) => {
|
||||
output.write(`${JSON.stringify(record)}\n`);
|
||||
},
|
||||
});
|
||||
input.setEncoding('utf8');
|
||||
const lines = readline.createInterface({ input });
|
||||
lines.on('line', (line) => {
|
||||
void server.acceptLine(line);
|
||||
});
|
||||
lines.on('error', (error) => {
|
||||
errors.write(`ACP stdio input failed: ${boundedError(error)}\n`);
|
||||
});
|
||||
lines.on('close', () => {
|
||||
server.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
export function registerAcpCommands(program: Command): void {
|
||||
const acp = program.command('acp').description('Agent Client Protocol server view');
|
||||
acp
|
||||
.command('status')
|
||||
.description('Report ACP server-view readiness')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options: { json?: boolean }) => {
|
||||
const status = await readAcpStatus();
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(status, null, 2));
|
||||
} else {
|
||||
console.log(
|
||||
`${status.ready ? 'ready' : 'not ready'}: ACP v${status.protocolVersion} over ${status.transport}`
|
||||
);
|
||||
if (status.error) console.error(status.error);
|
||||
}
|
||||
if (!status.ready) process.exitCode = 1;
|
||||
});
|
||||
|
||||
acp
|
||||
.command('serve')
|
||||
.description('Serve the provider-neutral Veritas ACP view over stdio')
|
||||
.requiredOption('--stdio', 'Use newline-delimited JSON-RPC over stdio')
|
||||
.option('--task <taskId>', 'Bind this process to one Veritas task')
|
||||
.option('--agent <agent>', 'Agent for a fresh scoped conversation')
|
||||
.option('--profile <profileId>', 'Agent profile for a fresh scoped conversation')
|
||||
.action(
|
||||
(options: { stdio: boolean; task?: string; agent?: string; profile?: string }): void => {
|
||||
runAcpStdioServer({
|
||||
boundTaskId: options.task,
|
||||
agent: options.agent,
|
||||
profileId: options.profile,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function eventToSessionUpdate(event: RunEventEnvelope): Record<string, unknown> | undefined {
|
||||
const summary = eventSummary(event);
|
||||
switch (event.kind) {
|
||||
case 'message.delta':
|
||||
return {
|
||||
sessionUpdate: 'agent_message_chunk',
|
||||
content: { type: 'text', text: summary },
|
||||
};
|
||||
case 'reasoning.delta':
|
||||
return {
|
||||
sessionUpdate: 'agent_thought_chunk',
|
||||
content: { type: 'text', text: summary },
|
||||
};
|
||||
case 'tool.started':
|
||||
return {
|
||||
sessionUpdate: 'tool_call',
|
||||
toolCallId: event.itemId ?? event.eventId,
|
||||
title: summary,
|
||||
kind: optionalString(event.payload.actionClass) ?? 'other',
|
||||
status: 'in_progress',
|
||||
rawInput: event.payload.input,
|
||||
};
|
||||
case 'tool.completed':
|
||||
return {
|
||||
sessionUpdate: 'tool_call_update',
|
||||
toolCallId: event.itemId ?? event.eventId,
|
||||
status: event.payload.success === false ? 'failed' : 'completed',
|
||||
content: summary ? [{ type: 'content', content: { type: 'text', text: summary } }] : [],
|
||||
};
|
||||
case 'progress':
|
||||
return {
|
||||
sessionUpdate: 'plan',
|
||||
entries: [{ content: summary, priority: 'medium', status: 'in_progress' }],
|
||||
};
|
||||
case 'approval.resolved':
|
||||
return {
|
||||
sessionUpdate: 'tool_call_update',
|
||||
toolCallId: optionalString(event.payload.approvalId) ?? event.eventId,
|
||||
status: event.payload.status === 'approved' ? 'completed' : 'failed',
|
||||
content: summary ? [{ type: 'content', content: { type: 'text', text: summary } }] : [],
|
||||
};
|
||||
case 'run.failed':
|
||||
return summary
|
||||
? {
|
||||
sessionUpdate: 'agent_message_chunk',
|
||||
content: { type: 'text', text: summary },
|
||||
}
|
||||
: undefined;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function eventSummary(event: RunEventEnvelope): string {
|
||||
return (
|
||||
optionalString(event.payload.summary) ??
|
||||
optionalString(event.payload.message) ??
|
||||
optionalString(event.payload.error) ??
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
function promptText(value: unknown): string {
|
||||
if (!Array.isArray(value) || value.length === 0) {
|
||||
throw new AcpViewError(-32602, 'session/prompt requires at least one text block.');
|
||||
}
|
||||
const blocks = value as AcpContentBlock[];
|
||||
const unsupported = blocks.find((block) => !isRecord(block) || block.type !== 'text');
|
||||
if (unsupported) {
|
||||
throw new AcpViewError(-32602, 'The Veritas ACP server view accepts text prompts only.');
|
||||
}
|
||||
const text = blocks
|
||||
.map((block) => (block.type === 'text' ? block.text : ''))
|
||||
.join('\n')
|
||||
.trim();
|
||||
if (!text || text.length > 20_000) {
|
||||
throw new AcpViewError(-32602, 'ACP prompt must contain 1 to 20,000 text characters.');
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function rejectClientMcp(value: unknown): void {
|
||||
if (value === undefined) return;
|
||||
if (!Array.isArray(value)) throw new AcpViewError(-32602, 'mcpServers must be an array.');
|
||||
if (value.length > 0) {
|
||||
throw new AcpViewError(
|
||||
-32003,
|
||||
'ACP clients cannot override the immutable Veritas run tool catalog.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function sessionIdForTask(taskId: string): string {
|
||||
return `vkacp_${Buffer.from(taskId, 'utf8').toString('base64url')}`;
|
||||
}
|
||||
|
||||
function taskIdFromSession(sessionId: string): string {
|
||||
if (!/^vkacp_[A-Za-z0-9_-]+$/.test(sessionId)) {
|
||||
throw new AcpViewError(-32602, 'Invalid Veritas ACP session ID.');
|
||||
}
|
||||
try {
|
||||
const taskId = Buffer.from(sessionId.slice('vkacp_'.length), 'base64url').toString('utf8');
|
||||
if (!taskId || Buffer.byteLength(taskId, 'utf8') > 200) throw new Error('invalid');
|
||||
return taskId;
|
||||
} catch {
|
||||
throw new AcpViewError(-32602, 'Invalid Veritas ACP session ID.');
|
||||
}
|
||||
}
|
||||
|
||||
function latestTaskAttempt(task: Task): TaskAttempt | undefined {
|
||||
const attempts = [task.attempt, ...(task.attempts ?? [])].filter(
|
||||
(attempt): attempt is TaskAttempt => Boolean(attempt)
|
||||
);
|
||||
return attempts.sort((left, right) => {
|
||||
const leftTime = Date.parse(left.started ?? left.ended ?? '') || 0;
|
||||
const rightTime = Date.parse(right.started ?? right.ended ?? '') || 0;
|
||||
return rightTime - leftTime;
|
||||
})[0];
|
||||
}
|
||||
|
||||
function findTaskAttempt(task: Task, attemptId: string): TaskAttempt | undefined {
|
||||
return [task.attempt, ...(task.attempts ?? [])]
|
||||
.filter((attempt): attempt is TaskAttempt => Boolean(attempt))
|
||||
.find((attempt) => attempt.id === attemptId);
|
||||
}
|
||||
|
||||
function requiredRecord(value: unknown, label: string): Record<string, unknown> {
|
||||
if (!isRecord(value)) throw new AcpViewError(-32602, `${label} must be an object.`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function optionalRecord(value: unknown): Record<string, unknown> {
|
||||
return isRecord(value) ? value : {};
|
||||
}
|
||||
|
||||
function requiredString(value: unknown, label: string): string {
|
||||
const result = optionalString(value);
|
||||
if (!result) throw new AcpViewError(-32602, `${label} must be a non-empty string.`);
|
||||
return result;
|
||||
}
|
||||
|
||||
function optionalString(value: unknown): string | undefined {
|
||||
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
function optionalNonNegativeInteger(value: unknown): number | undefined {
|
||||
return typeof value === 'number' && Number.isInteger(value) && value >= 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function validId(value: unknown): value is AcpJsonRpcId {
|
||||
return typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value));
|
||||
}
|
||||
|
||||
function delay(milliseconds: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
||||
}
|
||||
|
||||
function boundedError(error: unknown): string {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return message.replace(/[\r\n\t]+/g, ' ').slice(0, 1_000);
|
||||
}
|
||||
|
||||
class AcpViewError extends Error {
|
||||
constructor(
|
||||
readonly code: number,
|
||||
message: string,
|
||||
readonly data?: unknown
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
static internal(error: unknown): AcpViewError {
|
||||
return new AcpViewError(-32000, boundedError(error));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,409 +0,0 @@
|
|||
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,5 +1,4 @@
|
|||
import { Command } from 'commander';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import chalk from 'chalk';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
|
@ -10,182 +9,13 @@ import type {
|
|||
AgentProfilePackageFormat,
|
||||
AgentProfilePackageSummary,
|
||||
AgentProfileValidationResult,
|
||||
ConversationLifecycleRecord,
|
||||
ConversationLifecycleResult,
|
||||
PhaseCapabilityEvidence,
|
||||
PhaseTransitionRecord,
|
||||
PhaseTransitionResult,
|
||||
RunApprovalRequest,
|
||||
RunRecoveryRecord,
|
||||
RunLaunchManifestPreview,
|
||||
RunPhaseAuthoritySnapshot,
|
||||
WorkspaceExecutionTrustDecision,
|
||||
WorkspaceExecutionTrustDecisionMode,
|
||||
WorkspaceExecutionTrustScanResult,
|
||||
} from '@veritas-kanban/shared';
|
||||
|
||||
type ConversationTurnAction = 'resume' | 'follow-up' | 'fork';
|
||||
type ConversationControlAction = 'interrupt' | 'compact' | 'archive' | 'close';
|
||||
|
||||
interface ConversationTurnOptions {
|
||||
sourceAttempt: string;
|
||||
message: string;
|
||||
forkTurn?: string;
|
||||
profile?: string;
|
||||
phase?: string;
|
||||
requireCapability?: string[];
|
||||
commitPolicy?: string;
|
||||
json?: boolean;
|
||||
}
|
||||
|
||||
interface ConversationControlOptions {
|
||||
attempt: string;
|
||||
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';
|
||||
}
|
||||
|
||||
async function resolveTaskId(id: string): Promise<string> {
|
||||
const task = await findTask(id);
|
||||
if (!task) throw new Error(`Task not found: ${id}`);
|
||||
return task.id;
|
||||
}
|
||||
|
||||
function readPhaseEvidence(filePath: string): PhaseCapabilityEvidence {
|
||||
return JSON.parse(readFileSync(path.resolve(filePath), 'utf8')) as PhaseCapabilityEvidence;
|
||||
}
|
||||
|
||||
function printConversationResult(
|
||||
action: string,
|
||||
result: {
|
||||
attemptId: string;
|
||||
delivered?: boolean;
|
||||
note?: string;
|
||||
conversation?: ConversationLifecycleRecord;
|
||||
},
|
||||
json?: boolean
|
||||
): void {
|
||||
if (json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.green(`✓ Conversation ${action}`));
|
||||
console.log(chalk.dim(`Attempt ID: ${result.attemptId}`));
|
||||
if (result.conversation?.conversationId) {
|
||||
console.log(chalk.dim(`Conversation ID: ${result.conversation.conversationId}`));
|
||||
}
|
||||
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,
|
||||
description: string
|
||||
): void {
|
||||
const command = program
|
||||
.command(`agent:${action} <id>`)
|
||||
.description(description)
|
||||
.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'
|
||||
)
|
||||
.option(
|
||||
'--commit-policy <policy>',
|
||||
'Commit policy for this run (forbidden, allowed, or required)'
|
||||
)
|
||||
.option('--json', 'Output as JSON');
|
||||
|
||||
if (action === 'fork') {
|
||||
command.option('--fork-turn <turnId>', 'Provider turn boundary to fork from');
|
||||
}
|
||||
|
||||
command.action(async (id: string, options: ConversationTurnOptions) => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<{
|
||||
attemptId: string;
|
||||
conversation?: ConversationLifecycleRecord;
|
||||
}>(`/api/agents/${taskId}/conversation/${action}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
sourceAttemptId: options.sourceAttempt,
|
||||
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);
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function registerConversationControlCommand(
|
||||
program: Command,
|
||||
action: ConversationControlAction,
|
||||
description: string
|
||||
): void {
|
||||
program
|
||||
.command(`agent:${action} <id>`)
|
||||
.description(description)
|
||||
.requiredOption('--attempt <attemptId>', 'Exact active attempt ID')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id: string, options: ConversationControlOptions) => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<ConversationLifecycleResult>(
|
||||
`/api/agents/${taskId}/conversation/${action}`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ attemptId: options.attempt }),
|
||||
}
|
||||
);
|
||||
printConversationResult(action, result, options.json);
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function registerAgentCommands(program: Command): void {
|
||||
// Start agent on task
|
||||
program
|
||||
|
|
@ -197,7 +27,6 @@ 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'
|
||||
|
|
@ -206,7 +35,6 @@ export function registerAgentCommands(program: Command): void {
|
|||
'--commit-policy <policy>',
|
||||
'Commit policy for this run (forbidden, allowed, or required)'
|
||||
)
|
||||
.option('--parent-attempt <attemptId>', 'Compare launch inputs with a parent attempt')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
|
|
@ -232,11 +60,8 @@ 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()}`,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -253,190 +78,6 @@ export function registerAgentCommands(program: Command): void {
|
|||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command('launch-preview <id>')
|
||||
.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'
|
||||
)
|
||||
.option(
|
||||
'--commit-policy <policy>',
|
||||
'Commit policy for this run (forbidden, allowed, or required)'
|
||||
)
|
||||
.option('--parent-attempt <attemptId>', 'Compare launch inputs with a parent attempt')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
try {
|
||||
const task = await findTask(id);
|
||||
if (!task) throw new Error(`Task not found: ${id}`);
|
||||
const preview = await api<RunLaunchManifestPreview>(
|
||||
`/api/agents/${task.id}/launch-preview`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
agent: options.profile ? undefined : options.agent,
|
||||
profileId: options.profile,
|
||||
phase: options.phase,
|
||||
requiredRuntimeCapabilities: options.requireCapability,
|
||||
commitPolicy: options.commitPolicy,
|
||||
parentAttemptId: options.parentAttempt,
|
||||
}),
|
||||
}
|
||||
);
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify(preview, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(chalk.bold('Run launch manifest'));
|
||||
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')}`
|
||||
);
|
||||
for (const blocker of preview.manifest.enforcement.blockers) {
|
||||
console.log(chalk.red(` Blocker ${blocker.code}: ${blocker.detail}`));
|
||||
}
|
||||
if (preview.drift) {
|
||||
console.log(
|
||||
` Parent drift: ${preview.drift.material ? chalk.yellow('material') : chalk.green('none')}`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
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');
|
||||
|
|
@ -592,302 +233,6 @@ 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',
|
||||
'Resume a terminal provider conversation without replaying prior prompts'
|
||||
);
|
||||
registerConversationTurnCommand(
|
||||
program,
|
||||
'follow-up',
|
||||
'Start a provider-native follow-up turn from a terminal attempt'
|
||||
);
|
||||
registerConversationTurnCommand(
|
||||
program,
|
||||
'fork',
|
||||
'Fork provider-native history from a terminal attempt'
|
||||
);
|
||||
|
||||
program
|
||||
.command('agent:steer <id>')
|
||||
.description('Steer the exact active provider turn')
|
||||
.requiredOption('--attempt <attemptId>', 'Exact active attempt ID')
|
||||
.requiredOption('-m, --message <text>', 'Steering message')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(
|
||||
async (
|
||||
id: string,
|
||||
options: ConversationControlOptions & { message: string }
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const taskId = await resolveTaskId(id);
|
||||
const result = await api<ConversationLifecycleResult>(
|
||||
`/api/agents/${taskId}/conversation/steer`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
attemptId: options.attempt,
|
||||
message: options.message,
|
||||
}),
|
||||
}
|
||||
);
|
||||
printConversationResult('steered', result, options.json);
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Error: ${(err as Error).message}`));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
registerConversationControlCommand(
|
||||
program,
|
||||
'interrupt',
|
||||
'Interrupt the exact active provider turn'
|
||||
);
|
||||
registerConversationControlCommand(
|
||||
program,
|
||||
'compact',
|
||||
'Compact the active provider conversation'
|
||||
);
|
||||
registerConversationControlCommand(
|
||||
program,
|
||||
'archive',
|
||||
'Archive the active provider conversation'
|
||||
);
|
||||
registerConversationControlCommand(program, 'close', 'Close the active provider conversation');
|
||||
|
||||
// Get pending agent requests (for Veritas to process)
|
||||
program
|
||||
.command('agents:pending')
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ import { readFile, readdir, stat } from 'node:fs/promises';
|
|||
import path from 'node:path';
|
||||
import { promisify } from 'node:util';
|
||||
import { API_BASE, buildApiHeaders } from '../utils/api.js';
|
||||
import type {
|
||||
CommunicationAdapterHealth,
|
||||
HarnessCompatibilityMatrix,
|
||||
HarnessSupportStatus,
|
||||
} from '@veritas-kanban/shared';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const CRITICAL_STATUSES = new Set<DoctorCheck['id']>([
|
||||
|
|
@ -18,9 +13,7 @@ const CRITICAL_STATUSES = new Set<DoctorCheck['id']>([
|
|||
'api-auth',
|
||||
'tasks',
|
||||
'agents',
|
||||
'harness-support',
|
||||
'routing',
|
||||
'buzz',
|
||||
]);
|
||||
|
||||
export type DoctorStatus = 'pass' | 'warn' | 'fail' | 'skip';
|
||||
|
|
@ -545,155 +538,6 @@ function buildCodexCheck(health: CodexHealthResponse | null): DoctorCheck {
|
|||
);
|
||||
}
|
||||
|
||||
function buildBuzzCheck(
|
||||
health: CommunicationAdapterHealth | null,
|
||||
responseStatus: number
|
||||
): DoctorCheck {
|
||||
if (!health) {
|
||||
if (responseStatus === 404) {
|
||||
return check('buzz', 'Buzz compatibility', 'skip', 'Buzz is not configured');
|
||||
}
|
||||
return check(
|
||||
'buzz',
|
||||
'Buzz compatibility',
|
||||
'warn',
|
||||
'Buzz compatibility diagnostics are unavailable',
|
||||
{ status: responseStatus },
|
||||
'Verify settings-read permission and the communication adapter API.'
|
||||
);
|
||||
}
|
||||
|
||||
const details = {
|
||||
status: health.status,
|
||||
reasonCode: health.reasonCode,
|
||||
configured: health.configured,
|
||||
canSend: health.canSend,
|
||||
canReceiveReplies: health.canReceiveReplies,
|
||||
checkedAt: health.checkedAt,
|
||||
expectedCommunity: health.buzz?.expectedCommunity,
|
||||
observedCommunity: health.buzz?.observedCommunity,
|
||||
publicKeyFingerprint: health.buzz?.publicKeyFingerprint,
|
||||
testedRelease: health.buzz?.testedRelease,
|
||||
testedCommit: health.buzz?.testedCommit,
|
||||
probeRevision: health.buzz?.probeRevision,
|
||||
commands: health.buzz?.commands,
|
||||
buzz: health.buzz,
|
||||
};
|
||||
|
||||
if (health.status === 'disabled') {
|
||||
return check('buzz', 'Buzz compatibility', 'skip', health.detail, details, health.remediation);
|
||||
}
|
||||
if (health.status === 'healthy') {
|
||||
return check('buzz', 'Buzz compatibility', 'pass', health.detail, details);
|
||||
}
|
||||
if (health.status === 'degraded' || health.status === 'warning') {
|
||||
return check('buzz', 'Buzz compatibility', 'warn', health.detail, details, health.remediation);
|
||||
}
|
||||
return check('buzz', 'Buzz compatibility', 'fail', health.detail, details, health.remediation);
|
||||
}
|
||||
|
||||
function buildHarnessSupportCheck(statuses: HarnessSupportStatus[] | null): DoctorCheck {
|
||||
if (!statuses) {
|
||||
return check(
|
||||
'harness-support',
|
||||
'Harness support',
|
||||
'skip',
|
||||
'Skipped because harness support evidence was unavailable'
|
||||
);
|
||||
}
|
||||
|
||||
const counts = Object.fromEntries(
|
||||
['detected', 'configured', 'certified', 'degraded', 'unsupported'].map((tier) => [
|
||||
tier,
|
||||
statuses.filter((status) => status.supportTier === tier).length,
|
||||
])
|
||||
) as Record<HarnessSupportStatus['supportTier'], number>;
|
||||
const enabled = statuses.filter((status) => status.enabled);
|
||||
const blocking = enabled.filter(
|
||||
(status) => status.supportTier === 'degraded' || status.supportTier === 'unsupported'
|
||||
);
|
||||
|
||||
if (blocking.length > 0) {
|
||||
return check(
|
||||
'harness-support',
|
||||
'Harness support',
|
||||
'fail',
|
||||
`${blocking.length} enabled harness profile(s) cannot dispatch safely`,
|
||||
{
|
||||
...counts,
|
||||
blocking: blocking.map((status) => ({
|
||||
profileId: status.profileId,
|
||||
adapterId: status.adapterId,
|
||||
tier: status.supportTier,
|
||||
failureClass: status.failureClass,
|
||||
reason: status.reason,
|
||||
diagnosticCommands: status.diagnosticCommands,
|
||||
remediation: status.remediation,
|
||||
})),
|
||||
},
|
||||
'Disable unsupported profiles or follow the profile remediation before dispatch.'
|
||||
);
|
||||
}
|
||||
|
||||
const uncertified = enabled.filter((status) => status.supportTier !== 'certified');
|
||||
if (uncertified.length > 0) {
|
||||
return check(
|
||||
'harness-support',
|
||||
'Harness support',
|
||||
'warn',
|
||||
`${uncertified.length} enabled harness profile(s) are configured but not certified`,
|
||||
counts,
|
||||
'Run the pinned harness conformance fixtures before treating these runtimes as certified.'
|
||||
);
|
||||
}
|
||||
|
||||
return check(
|
||||
'harness-support',
|
||||
'Harness support',
|
||||
'pass',
|
||||
`${enabled.length} enabled harness profile(s) have current certification evidence`,
|
||||
counts
|
||||
);
|
||||
}
|
||||
|
||||
function buildHarnessCompatibilityCheck(matrix: HarnessCompatibilityMatrix | null): DoctorCheck {
|
||||
if (!matrix) {
|
||||
return check(
|
||||
'harness-compatibility',
|
||||
'Harness compatibility',
|
||||
'skip',
|
||||
'Skipped because compatibility evidence was unavailable'
|
||||
);
|
||||
}
|
||||
const stale = matrix.records.filter(
|
||||
(record) => record.certification.status === 'failed' || record.certification.status === 'stale'
|
||||
);
|
||||
return check(
|
||||
'harness-compatibility',
|
||||
'Harness compatibility',
|
||||
stale.length > 0 ? 'warn' : 'pass',
|
||||
stale.length > 0
|
||||
? `${stale.length} reviewed harness certification record(s) are stale or failed`
|
||||
: `${matrix.records.length} reviewed harness profiles share matrix ${matrix.digest.slice(0, 12)}`,
|
||||
{
|
||||
schemaVersion: matrix.schemaVersion,
|
||||
digest: matrix.digest,
|
||||
probeRevision: matrix.probeRevision,
|
||||
records: matrix.records.map((record) => ({
|
||||
profileId: record.profileId,
|
||||
testedVersions: record.testedVersions,
|
||||
testedBuilds: record.testedBuilds,
|
||||
sourceAvailability: record.sourceAvailability,
|
||||
certification: record.certification.status,
|
||||
supportTier: record.supportStatus?.supportTier,
|
||||
})),
|
||||
},
|
||||
stale.length > 0
|
||||
? 'Re-run the pinned deterministic fixtures before treating the affected builds as certified.'
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
export async function runDoctorChecks(
|
||||
input: Partial<DoctorOptions> = {},
|
||||
depsInput: Partial<DoctorDependencies> = {}
|
||||
|
|
@ -786,8 +630,6 @@ export async function runDoctorChecks(
|
|||
);
|
||||
|
||||
let agents: AgentConfigResponse[] | null = null;
|
||||
let harnessSupport: HarnessSupportStatus[] | null = null;
|
||||
let harnessCompatibility: HarnessCompatibilityMatrix | null = null;
|
||||
let routing: RoutingConfigResponse | null = null;
|
||||
let settings: FeatureSettingsResponse | null = null;
|
||||
|
||||
|
|
@ -847,16 +689,6 @@ export async function runDoctorChecks(
|
|||
);
|
||||
agents = agentsResponse.ok ? agentsResponse.data : null;
|
||||
|
||||
const harnessCompatibilityResponse = await requestJson<HarnessCompatibilityMatrix>(
|
||||
deps,
|
||||
options,
|
||||
'/api/config/harness-compatibility'
|
||||
);
|
||||
harnessCompatibility = harnessCompatibilityResponse.ok
|
||||
? harnessCompatibilityResponse.data
|
||||
: null;
|
||||
harnessSupport = harnessCompatibility?.supportStatuses ?? null;
|
||||
|
||||
const routingResponse = await requestJson<RoutingConfigResponse>(
|
||||
deps,
|
||||
options,
|
||||
|
|
@ -903,13 +735,6 @@ export async function runDoctorChecks(
|
|||
'/api/settings/codex/health'
|
||||
);
|
||||
checks.push(buildCodexCheck(codexHealth.ok ? codexHealth.data : null));
|
||||
|
||||
const buzzHealth = await requestJson<CommunicationAdapterHealth>(
|
||||
deps,
|
||||
options,
|
||||
'/api/integrations/communication/adapters/buzz-default/health'
|
||||
);
|
||||
checks.push(buildBuzzCheck(buzzHealth.ok ? buzzHealth.data : null, buzzHealth.status));
|
||||
} else {
|
||||
checks.push(
|
||||
check('api-auth', 'API authentication', 'skip', 'Skipped because API is unreachable')
|
||||
|
|
@ -923,26 +748,10 @@ export async function runDoctorChecks(
|
|||
checks.push(
|
||||
check('codex-health', 'Codex health', 'skip', 'Skipped because API is unreachable')
|
||||
);
|
||||
checks.push(
|
||||
check('harness-support', 'Harness support', 'skip', 'Skipped because API is unreachable')
|
||||
);
|
||||
checks.push(
|
||||
check(
|
||||
'harness-compatibility',
|
||||
'Harness compatibility',
|
||||
'skip',
|
||||
'Skipped because API is unreachable'
|
||||
)
|
||||
);
|
||||
checks.push(check('buzz', 'Buzz compatibility', 'skip', 'Skipped because API is unreachable'));
|
||||
}
|
||||
|
||||
const agentResult = await buildAgentCheck(deps, agents);
|
||||
checks.push(agentResult.check);
|
||||
if (apiReachable) {
|
||||
checks.push(buildHarnessSupportCheck(harnessSupport));
|
||||
checks.push(buildHarnessCompatibilityCheck(harnessCompatibility));
|
||||
}
|
||||
checks.push(
|
||||
buildRoutingCheck(routing, agentResult.availableAgents, agentResult.configuredAgents)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,312 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
import { readFileSync } from 'node:fs';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { Command } from 'commander';
|
||||
import chalk from 'chalk';
|
||||
import type {
|
||||
RunToolCatalog,
|
||||
ToolInvocationResult,
|
||||
ToolServerDefinition,
|
||||
ToolServerDefinitionInput,
|
||||
ToolServerDiscovery,
|
||||
} from '@veritas-kanban/shared';
|
||||
import { api } from '../utils/api.js';
|
||||
|
||||
export function registerToolServerCommands(program: Command): void {
|
||||
const servers = program
|
||||
.command('tool-servers')
|
||||
.alias('tools')
|
||||
.description('Manage run-scoped MCP and tool servers');
|
||||
|
||||
servers
|
||||
.command('list')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (options) => {
|
||||
await execute(async () => {
|
||||
const definitions = await api<ToolServerDefinition[]>('/api/tool-servers');
|
||||
if (options.json) return printJson(definitions);
|
||||
for (const definition of definitions) {
|
||||
console.log(
|
||||
`${chalk.bold(definition.id)} ${definition.enabled ? chalk.green('enabled') : chalk.yellow('disabled')} ${definition.version} ${definition.transport.kind}`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('get <id>')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, _options) => {
|
||||
await execute(async () => {
|
||||
const definition = await api<ToolServerDefinition>(
|
||||
`/api/tool-servers/${encodeURIComponent(id)}`
|
||||
);
|
||||
printJson(definition);
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('create <file>')
|
||||
.description('Create a definition from a JSON file')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (file, options) => {
|
||||
await execute(async () => {
|
||||
const input = JSON.parse(readFileSync(file, 'utf8')) as ToolServerDefinitionInput;
|
||||
const definition = await api<ToolServerDefinition>('/api/tool-servers', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
if (options.json) return printJson(definition);
|
||||
console.log(chalk.green(`Created ${definition.id}@${definition.version}`));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('update <id> <file>')
|
||||
.description('Replace a definition from a JSON file')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, file, options) => {
|
||||
await execute(async () => {
|
||||
const input = JSON.parse(readFileSync(file, 'utf8')) as ToolServerDefinitionInput;
|
||||
const definition = await api<ToolServerDefinition>(
|
||||
`/api/tool-servers/${encodeURIComponent(id)}`,
|
||||
{
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(input),
|
||||
}
|
||||
);
|
||||
if (options.json) return printJson(definition);
|
||||
console.log(chalk.green(`Updated ${definition.id}@${definition.version}`));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('delete <id>')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
await execute(async () => {
|
||||
const result = await api<{ deleted: string }>(
|
||||
`/api/tool-servers/${encodeURIComponent(id)}`,
|
||||
{ method: 'DELETE' }
|
||||
);
|
||||
if (options.json) return printJson(result);
|
||||
console.log(chalk.green(`Deleted ${result.deleted}`));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('enable <id>')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
await execute(async () => {
|
||||
const updated = await setDefinitionEnabled(id, true);
|
||||
if (options.json) return printJson(updated);
|
||||
console.log(chalk.green(`Enabled ${updated.id}`));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('disable <id>')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
await execute(async () => {
|
||||
const updated = await setDefinitionEnabled(id, false);
|
||||
if (options.json) return printJson(updated);
|
||||
console.log(chalk.green(`Disabled ${updated.id}`));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('version <id> <version>')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, version, options) => {
|
||||
await execute(async () => {
|
||||
const current = await getDefinition(id);
|
||||
const updated = await replaceDefinition(id, { ...current, version });
|
||||
if (options.json) return printJson(updated);
|
||||
console.log(chalk.green(`Versioned ${updated.id}@${updated.version}`));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('discover <id>')
|
||||
.option('--force', 'Ignore a matching discovery cache entry')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (id, options) => {
|
||||
await execute(async () => {
|
||||
const discovery = await api<ToolServerDiscovery>(
|
||||
`/api/tool-servers/${encodeURIComponent(id)}/discover`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ force: options.force === true }),
|
||||
}
|
||||
);
|
||||
if (options.json) return printJson(discovery);
|
||||
console.log(
|
||||
`${discovery.status === 'ready' ? chalk.green('ready') : chalk.red('failed')} ${discovery.serverId}@${discovery.serverVersion} tools=${discovery.tools.length}`
|
||||
);
|
||||
if (discovery.error) console.log(chalk.red(discovery.error));
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('catalog <taskId> <attemptId>')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (taskId, attemptId) => {
|
||||
await execute(async () => {
|
||||
printJson(
|
||||
await api<RunToolCatalog>(
|
||||
`/api/tool-servers/runs/${encodeURIComponent(taskId)}/${encodeURIComponent(attemptId)}/catalog`
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
servers
|
||||
.command('call <taskId> <attemptId> <serverId> <tool>')
|
||||
.requiredOption('--arguments <json>', 'JSON object of tool arguments')
|
||||
.option('--operation-id <id>', 'Stable caller operation ID', randomUUID())
|
||||
.option('--approval-id <id>', 'Approved run approval ID')
|
||||
.option('--json', 'Output as JSON')
|
||||
.action(async (taskId, attemptId, serverId, tool, options) => {
|
||||
await execute(async () => {
|
||||
const result = await api<ToolInvocationResult>('/api/tool-servers/call', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
taskId,
|
||||
attemptId,
|
||||
serverId,
|
||||
tool,
|
||||
arguments: JSON.parse(options.arguments),
|
||||
operationId: options.operationId,
|
||||
approvalId: options.approvalId,
|
||||
}),
|
||||
});
|
||||
if (options.json) return printJson(result);
|
||||
console.log(
|
||||
`${result.isError ? chalk.red('error') : chalk.green('complete')} ${result.serverId}/${result.tool} event=${result.eventId}`
|
||||
);
|
||||
printJson(result.content);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function execute(action: () => Promise<void>): Promise<void> {
|
||||
try {
|
||||
await action();
|
||||
} catch (error) {
|
||||
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
function printJson(value: unknown): void {
|
||||
console.log(JSON.stringify(value, null, 2));
|
||||
}
|
||||
|
||||
async function getDefinition(id: string): Promise<ToolServerDefinition> {
|
||||
return api<ToolServerDefinition>(`/api/tool-servers/${encodeURIComponent(id)}`);
|
||||
}
|
||||
|
||||
async function replaceDefinition(
|
||||
id: string,
|
||||
definition: ToolServerDefinition
|
||||
): Promise<ToolServerDefinition> {
|
||||
const {
|
||||
schemaVersion: _schemaVersion,
|
||||
digest: _digest,
|
||||
createdAt: _createdAt,
|
||||
updatedAt: _updatedAt,
|
||||
...input
|
||||
} = definition;
|
||||
return api<ToolServerDefinition>(`/api/tool-servers/${encodeURIComponent(id)}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(input satisfies ToolServerDefinitionInput),
|
||||
});
|
||||
}
|
||||
|
||||
async function setDefinitionEnabled(id: string, enabled: boolean): Promise<ToolServerDefinition> {
|
||||
const current = await getDefinition(id);
|
||||
return replaceDefinition(id, { ...current, enabled });
|
||||
}
|
||||
|
|
@ -23,10 +23,6 @@ import { registerWorkspaceCommands } from './commands/workspaces.js';
|
|||
import { registerSchedulerCommands } from './commands/scheduler.js';
|
||||
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,9 +57,5 @@ registerWorkspaceCommands(program);
|
|||
registerSchedulerCommands(program);
|
||||
registerQueueMonitorCommands(program);
|
||||
registerSqliteCommands(program);
|
||||
registerToolServerCommands(program);
|
||||
registerAcpCommands(program);
|
||||
registerAdmissionCommands(program);
|
||||
registerGoalCommands(program);
|
||||
|
||||
program.parse();
|
||||
|
|
|
|||
|
|
@ -5,21 +5,5 @@ 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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Veritas Kanban Desktop
|
||||
|
||||
This package is the v6 native desktop application. It uses Electron with
|
||||
This package is the v5 native desktop scaffold. It uses Electron with
|
||||
electron-vite, starts the existing Veritas server as the local backend, and
|
||||
loads the existing web UI.
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ can be tested without reusing the default development home.
|
|||
work.
|
||||
- The renderer uses the existing Veritas web app and has no Node, filesystem,
|
||||
process, or secret access.
|
||||
- The preload bridge exposes only typed desktop operations. The current v6
|
||||
- The preload bridge exposes only typed desktop operations. The current v5
|
||||
contract covers app/setup diagnostics, local server lifecycle, connection
|
||||
validation, update status, native command dispatch, upload/import picking,
|
||||
diagnostics bundles, notification actions, work product export, external URL
|
||||
|
|
@ -37,7 +37,7 @@ can be tested without reusing the default development home.
|
|||
desktop bridge contract module so main and preload cannot drift silently.
|
||||
- Dangerous bridge methods require typed request objects and contract validators
|
||||
before native execution. Unsupported native features return explicit
|
||||
placeholder results until their dedicated roadmap issues implement the backing
|
||||
placeholder results until their dedicated v5 issues implement the backing
|
||||
behavior.
|
||||
- Fresh packaged installs store desktop data below the OS app data directory
|
||||
returned by Electron `app.getPath('userData')`, then under
|
||||
|
|
@ -97,7 +97,7 @@ metadata, and platform smoke steps are documented in
|
|||
`desktop:package:mac:unsigned` script for Mac release validation. The
|
||||
`desktop:package:linux:unsigned` and `desktop:package:windows:unsigned` scripts
|
||||
produce preview-only, non-GA artifacts for post-GA readiness checks. Use
|
||||
`desktop:release:mac` for stable macOS releases; use `desktop:release:linux` or
|
||||
`desktop:release:mac` only for v5 GA; use `desktop:release:linux` or
|
||||
`desktop:release:windows` only after the platform release prerequisites in the
|
||||
release guide are promoted.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@ 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.1.2",
|
||||
"version": "5.2.5",
|
||||
"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.2.0",
|
||||
"electron": "^43.4.1",
|
||||
"@types/node": "^26.1.1",
|
||||
"electron": "^43.1.0",
|
||||
"electron-builder": "^26.15.3",
|
||||
"electron-vite": "^5.0.0",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.2.1",
|
||||
"vitest": "^4.1.11"
|
||||
"vite": "^8.1.4",
|
||||
"vitest": "^4.1.10"
|
||||
},
|
||||
"build": {
|
||||
"appId": "io.digitalmeld.veritas-kanban",
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ function shell(): Shell {
|
|||
}
|
||||
|
||||
function handlers(): DesktopBridgeHandlerMap {
|
||||
return createDesktopBridgeHandlers(runtime(), shell(), false, '6.0.1');
|
||||
return createDesktopBridgeHandlers(runtime(), shell(), false);
|
||||
}
|
||||
|
||||
describe('desktop bridge contracts', () => {
|
||||
|
|
@ -124,7 +124,7 @@ describe('desktop bridge contracts', () => {
|
|||
}),
|
||||
} as unknown as IpcMain;
|
||||
|
||||
registerDesktopBridge(ipcMain, runtime(), shell(), false, '6.0.1');
|
||||
registerDesktopBridge(ipcMain, runtime(), shell(), false);
|
||||
|
||||
expect([...registered.keys()].sort()).toEqual(
|
||||
DESKTOP_BRIDGE_METHOD_NAMES.map((method) => DESKTOP_BRIDGE_METHODS[method].channel).sort()
|
||||
|
|
@ -132,19 +132,6 @@ 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,
|
||||
|
|
@ -185,8 +172,7 @@ describe('desktop bridge contracts', () => {
|
|||
const bridgeHandlers: DesktopBridgeHandlerMap = createDesktopBridgeHandlers(
|
||||
runtime(),
|
||||
fakeShell,
|
||||
false,
|
||||
'6.0.1'
|
||||
false
|
||||
);
|
||||
|
||||
await expect(
|
||||
|
|
@ -207,7 +193,7 @@ describe('desktop bridge contracts', () => {
|
|||
|
||||
it('validates restart confirmation before restarting the local server', async () => {
|
||||
const fakeRuntime = runtime();
|
||||
const bridgeHandlers = createDesktopBridgeHandlers(fakeRuntime, shell(), false, '6.0.1');
|
||||
const bridgeHandlers = createDesktopBridgeHandlers(fakeRuntime, shell(), false);
|
||||
|
||||
expect(() => bridgeHandlers.restartLocalServer({ confirmation: 'restart' } as never)).toThrow(
|
||||
'explicit restart confirmation'
|
||||
|
|
|
|||
|
|
@ -93,10 +93,6 @@ 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,8 +43,7 @@ function updateStatus(state: DesktopUpdateStatus['state']): DesktopUpdateStatus
|
|||
describe('desktop native menu', () => {
|
||||
it('exposes common actions with keyboard shortcuts', () => {
|
||||
const dispatch = vi.fn();
|
||||
const copyVersionInfo = vi.fn();
|
||||
const template = createDesktopMenuTemplate({ status: status(), dispatch, copyVersionInfo });
|
||||
const template = createDesktopMenuTemplate({ status: status(), dispatch });
|
||||
const labels = template.flatMap((item) =>
|
||||
Array.isArray(item.submenu) ? item.submenu.map((child) => child.label) : []
|
||||
);
|
||||
|
|
@ -55,15 +54,6 @@ 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)
|
||||
|
|
@ -76,11 +66,7 @@ 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(),
|
||||
copyVersionInfo: vi.fn(),
|
||||
});
|
||||
const template = createDesktopMenuTemplate({ status: status(), dispatch: vi.fn() });
|
||||
|
||||
expect(template.some((item) => item.role === 'editMenu')).toBe(true);
|
||||
});
|
||||
|
|
@ -89,7 +75,6 @@ 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')
|
||||
|
|
@ -103,7 +88,6 @@ 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')
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { describe, expect, it, vi } from 'vitest';
|
|||
|
||||
import {
|
||||
DesktopUpdateService,
|
||||
ElectronAutoUpdaterAdapter,
|
||||
resolveDesktopUpdateChannel,
|
||||
type DesktopUpdateAdapter,
|
||||
type DesktopUpdateAdapterConfigureOptions,
|
||||
|
|
@ -37,7 +36,7 @@ class FakeUpdateAdapter implements DesktopUpdateAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
function service(adapter = new FakeUpdateAdapter(), currentVersion = '4.3.2') {
|
||||
function service(adapter = new FakeUpdateAdapter()) {
|
||||
const emitStatus = vi.fn();
|
||||
return {
|
||||
adapter,
|
||||
|
|
@ -45,7 +44,7 @@ function service(adapter = new FakeUpdateAdapter(), currentVersion = '4.3.2') {
|
|||
service: new DesktopUpdateService({
|
||||
adapter,
|
||||
packaged: true,
|
||||
currentVersion,
|
||||
currentVersion: '4.3.2',
|
||||
channel: 'stable',
|
||||
now: () => new Date('2026-05-31T00:00:00.000Z'),
|
||||
emitStatus,
|
||||
|
|
@ -58,7 +57,6 @@ describe('desktop update service', () => {
|
|||
const harness = service();
|
||||
|
||||
expect(harness.adapter.configure).toHaveBeenCalledWith({
|
||||
allowDowngrade: false,
|
||||
allowPrerelease: false,
|
||||
autoDownload: false,
|
||||
autoInstallOnAppQuit: false,
|
||||
|
|
@ -92,31 +90,6 @@ describe('desktop update service', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('disables downgrade after assigning the updater channel', () => {
|
||||
const updater = {
|
||||
allowDowngrade: false,
|
||||
allowPrerelease: false,
|
||||
autoDownload: true,
|
||||
autoInstallOnAppQuit: true,
|
||||
forceDevUpdateConfig: false,
|
||||
set channel(_value: string | null) {
|
||||
this.allowDowngrade = true;
|
||||
},
|
||||
};
|
||||
const adapter = new ElectronAutoUpdaterAdapter(updater as never);
|
||||
|
||||
adapter.configure({
|
||||
allowDowngrade: false,
|
||||
allowPrerelease: false,
|
||||
autoDownload: false,
|
||||
autoInstallOnAppQuit: false,
|
||||
channel: 'stable',
|
||||
forceDevUpdateConfig: false,
|
||||
});
|
||||
|
||||
expect(updater.allowDowngrade).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps dev builds unsupported unless force dev update config is enabled', async () => {
|
||||
const adapter = new FakeUpdateAdapter();
|
||||
const updateService = new DesktopUpdateService({
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
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,10 +2,11 @@ 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,
|
||||
|
|
@ -203,12 +204,17 @@ export function createDesktopBridgeHandlers(
|
|||
runtime: DesktopRuntime,
|
||||
shell: Shell,
|
||||
packaged: boolean,
|
||||
appVersion: string,
|
||||
commandDispatcher?: DesktopCommandDispatcher,
|
||||
updateService?: DesktopUpdateService,
|
||||
windowControls?: DesktopWindowControls
|
||||
): DesktopBridgeHandlerMap {
|
||||
const appInfo = () => createDesktopAppInfo(appVersion, packaged);
|
||||
const appInfo = (): DesktopAppInfo => ({
|
||||
name: DESKTOP_APP_NAME,
|
||||
appId: DESKTOP_APP_ID,
|
||||
version: process.env.npm_package_version || '0.0.0',
|
||||
platform: process.platform,
|
||||
packaged,
|
||||
});
|
||||
|
||||
return {
|
||||
getAppInfo: appInfo,
|
||||
|
|
@ -237,7 +243,7 @@ export function createDesktopBridgeHandlers(
|
|||
updateService?.snapshot() ?? {
|
||||
state: 'unsupported',
|
||||
currentVersion: appInfo().version,
|
||||
channel: appInfo().channel,
|
||||
channel: packaged ? 'stable' : 'dev',
|
||||
checkedAt: new Date().toISOString(),
|
||||
detail: 'Updater service is not initialized.',
|
||||
},
|
||||
|
|
@ -298,7 +304,6 @@ export function registerDesktopBridge(
|
|||
runtime: DesktopRuntime,
|
||||
shell: Shell,
|
||||
packaged: boolean,
|
||||
appVersion: string,
|
||||
commandDispatcher?: DesktopCommandDispatcher,
|
||||
updateService?: DesktopUpdateService,
|
||||
windowControls?: DesktopWindowControls
|
||||
|
|
@ -307,7 +312,6 @@ export function registerDesktopBridge(
|
|||
runtime,
|
||||
shell,
|
||||
packaged,
|
||||
appVersion,
|
||||
commandDispatcher,
|
||||
updateService,
|
||||
windowControls
|
||||
|
|
@ -317,7 +321,8 @@ 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,8 +60,6 @@ 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,11 +20,6 @@ import {
|
|||
ElectronAutoUpdaterAdapter,
|
||||
resolveDesktopUpdateChannel,
|
||||
} from './updates.js';
|
||||
import {
|
||||
createDesktopAboutPanelOptions,
|
||||
createDesktopAppInfo,
|
||||
formatDesktopVersionInfo,
|
||||
} from './version-info.js';
|
||||
import {
|
||||
DESKTOP_BRIDGE_EVENTS,
|
||||
redactDesktopBridgeValue,
|
||||
|
|
@ -179,11 +174,9 @@ 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);
|
||||
|
|
@ -193,11 +186,10 @@ function refreshDesktopMenu(): void {
|
|||
}
|
||||
|
||||
function updateServiceFallback(packaged: boolean): DesktopUpdateStatus {
|
||||
const appInfo = createDesktopAppInfo(app.getVersion(), packaged);
|
||||
return {
|
||||
state: 'unsupported',
|
||||
currentVersion: appInfo.version,
|
||||
channel: appInfo.channel,
|
||||
currentVersion: app.getVersion(),
|
||||
channel: packaged ? 'stable' : 'dev',
|
||||
checkedAt: new Date().toISOString(),
|
||||
detail: 'Updater service is not initialized.',
|
||||
};
|
||||
|
|
@ -208,8 +200,6 @@ 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;
|
||||
|
|
@ -319,29 +309,20 @@ async function boot(): Promise<void> {
|
|||
},
|
||||
});
|
||||
|
||||
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() };
|
||||
},
|
||||
}
|
||||
);
|
||||
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() };
|
||||
},
|
||||
});
|
||||
refreshDesktopMenu();
|
||||
runtime.on('status', (status) => {
|
||||
activeMainWindow()?.webContents.send(DESKTOP_BRIDGE_EVENTS.serverStatus.channel, status);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import type {
|
|||
|
||||
export interface ConfigureDesktopMenuOptions {
|
||||
dispatch(command: DesktopCommandName): void;
|
||||
copyVersionInfo(): void;
|
||||
status: DesktopStatusSnapshot;
|
||||
updateStatus?: DesktopUpdateStatus;
|
||||
}
|
||||
|
|
@ -39,13 +38,6 @@ 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'),
|
||||
|
|
@ -69,13 +61,7 @@ export function createDesktopMenuTemplate(
|
|||
{ role: 'editMenu' },
|
||||
{
|
||||
label: 'Navigate',
|
||||
submenu: [
|
||||
command('open-command-center'),
|
||||
command('open-search'),
|
||||
command('open-settings'),
|
||||
{ type: 'separator' },
|
||||
command('reset-layout'),
|
||||
],
|
||||
submenu: [command('open-command-center'), command('open-search'), command('open-settings')],
|
||||
},
|
||||
{
|
||||
label: 'Desktop',
|
||||
|
|
|
|||
|
|
@ -70,10 +70,6 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ type DesktopUpdateEvent =
|
|||
| 'error';
|
||||
|
||||
export interface DesktopUpdateAdapterConfigureOptions {
|
||||
allowDowngrade: boolean;
|
||||
allowPrerelease: boolean;
|
||||
autoDownload: boolean;
|
||||
autoInstallOnAppQuit: boolean;
|
||||
|
|
@ -49,9 +48,6 @@ export class ElectronAutoUpdaterAdapter implements DesktopUpdateAdapter {
|
|||
this.updater.autoInstallOnAppQuit = options.autoInstallOnAppQuit;
|
||||
this.updater.allowPrerelease = options.allowPrerelease;
|
||||
this.updater.channel = options.channel === 'stable' ? null : options.channel;
|
||||
// electron-updater sets allowDowngrade=true whenever channel is assigned.
|
||||
// Stable checks must never turn older release metadata into an update.
|
||||
this.updater.allowDowngrade = options.allowDowngrade;
|
||||
this.updater.forceDevUpdateConfig = options.forceDevUpdateConfig;
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +83,6 @@ export class DesktopUpdateService {
|
|||
: this.createStatus('unsupported', 'Updater checks run only from packaged builds.');
|
||||
|
||||
options.adapter.configure({
|
||||
allowDowngrade: false,
|
||||
allowPrerelease: options.channel !== 'stable',
|
||||
autoDownload: false,
|
||||
autoInstallOnAppQuit: false,
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
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,7 +90,6 @@ export const DESKTOP_COMMAND_NAMES = [
|
|||
'open-search',
|
||||
'open-settings',
|
||||
'open-command-center',
|
||||
'reset-layout',
|
||||
'import-data',
|
||||
'export-data',
|
||||
'create-backup',
|
||||
|
|
|
|||
|
|
@ -5,21 +5,5 @@ 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
|
||||
# Kept aligned with the image entrypoint; persistent paths resolve from DATA_DIR.
|
||||
# IMPORTANT: Must match Dockerfile WORKDIR (/app/server) for correct path resolution
|
||||
working_dir: /app/server
|
||||
ports:
|
||||
# Demo instance port (do NOT use production 3001)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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:** `<storage-root>/.veritas-kanban/agent-registry.json`
|
||||
**Storage:** `.veritas-kanban/agent-registry.json`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -406,14 +406,13 @@ 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` | Project root | Storage root used when `DATA_DIR` is unset |
|
||||
| `VERITAS_DATA_DIR` | `.veritas-kanban/` | Directory for registry JSON file |
|
||||
|
||||
---
|
||||
|
||||
## File Format
|
||||
|
||||
The registry is stored as JSON at
|
||||
`<storage-root>/.veritas-kanban/agent-registry.json`:
|
||||
The registry is stored as JSON at `.veritas-kanban/agent-registry.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,279 +1,213 @@
|
|||
# Agent Guide and `AGENTS.md` Template
|
||||
# AGENTS.md Template — Veritas Kanban Self-Reporting Protocol
|
||||
|
||||
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.
|
||||
Use this template for agents that integrate with Veritas Kanban. Copy it into your agent's workspace and fill in the sections.
|
||||
|
||||
## 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`
|
||||
- Name: My Agent
|
||||
- Model: provider/model
|
||||
- Provider: external
|
||||
- Version: 1.0.0
|
||||
- **Agent ID:** `my-agent-id` _(unique, lowercase, dashes)_
|
||||
- **Name:** My Agent
|
||||
- **Model:** anthropic/claude-sonnet-4-5
|
||||
- **Provider:** anthropic
|
||||
- **Version:** 1.0.0
|
||||
|
||||
## Capabilities
|
||||
|
||||
- `code`: Write, review, and refactor code
|
||||
- `research`: Research and analysis
|
||||
- `review`: Code review and validation
|
||||
- `documentation`: Write and maintain documentation
|
||||
List what this agent can do. Used for task routing.
|
||||
|
||||
## Veritas Kanban external-agent protocol
|
||||
- `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
|
||||
|
||||
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.
|
||||
```
|
||||
## Registration
|
||||
|
||||
### Authentication
|
||||
|
||||
Set the VK endpoint and an agent-role API key outside source control:
|
||||
On startup, register with Veritas Kanban:
|
||||
|
||||
```bash
|
||||
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 '{
|
||||
curl -X POST http://localhost:3001/api/agents/register \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"id": "my-agent-id",
|
||||
"name": "My Agent",
|
||||
"model": "provider/model",
|
||||
"provider": "external",
|
||||
"model": "anthropic/claude-sonnet-4-5",
|
||||
"provider": "anthropic",
|
||||
"capabilities": [
|
||||
{"name": "code", "description": "Write and review code"},
|
||||
{"name": "research", "description": "Research and analysis"}
|
||||
{"name": "research", "description": "Deep research and analysis"}
|
||||
],
|
||||
"version": "1.0.0"
|
||||
}'
|
||||
```
|
||||
|
||||
### Heartbeat
|
||||
## Heartbeat
|
||||
|
||||
Send periodic heartbeats to stay registered (every 2-3 minutes):
|
||||
|
||||
```bash
|
||||
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 '{
|
||||
curl -X POST http://localhost:3001/api/agents/register/my-agent-id/heartbeat \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"status": "busy",
|
||||
"currentTaskId": "task_20260205_abc123",
|
||||
"currentTaskTitle": "Implement feature X"
|
||||
}'
|
||||
```
|
||||
|
||||
Valid states are `online`, `busy`, `idle`, and `offline`. VK marks an agent
|
||||
offline after its configured heartbeat timeout.
|
||||
### Status Values
|
||||
|
||||
### Task lifecycle
|
||||
| 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) |
|
||||
|
||||
For an external agent:
|
||||
## Deregistration
|
||||
|
||||
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:
|
||||
On shutdown, deregister cleanly:
|
||||
|
||||
```bash
|
||||
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}'
|
||||
curl -X DELETE http://localhost:3001/api/agents/register/my-agent-id
|
||||
```
|
||||
|
||||
Do not manually emit these events for a managed provider run.
|
||||
## Discovery
|
||||
|
||||
### Deregistration
|
||||
### List all agents
|
||||
|
||||
```bash
|
||||
curl -X DELETE "${VK_API_URL}/api/agents/register/my-agent-id" \
|
||||
-H "X-API-Key: ${VK_API_KEY}"
|
||||
curl http://localhost:3001/api/agents/register
|
||||
```
|
||||
|
||||
## Unmanaged MCP clients
|
||||
### Filter by status
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```bash
|
||||
curl http://localhost:3001/api/agents/register?status=online
|
||||
```
|
||||
|
||||
Codex CLI configuration:
|
||||
### 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
|
||||
|
||||
When picking up a task:
|
||||
|
||||
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
|
||||
|
||||
## 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:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
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).
|
||||
See [SOP-codex-integration.md](SOP-codex-integration.md) for the full Codex workflow.
|
||||
|
||||
## References
|
||||
## Telemetry Emission (MANDATORY)
|
||||
|
||||
- [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)
|
||||
The dashboard's **Success Rate**, **Token Usage**, and **Average Run Duration** graphs require `run.*` telemetry events. These are **NOT auto-captured** — your agent must emit them.
|
||||
|
||||
> Add these to your `AGENTS.md`. Without them, the dashboard graphs go blank.
|
||||
|
||||
### 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 | Auto? | Source |
|
||||
| --------------------- | ----- | --------------- |
|
||||
| `task.created` | ✅ | VK server |
|
||||
| `task.status_changed` | ✅ | VK server |
|
||||
| `task.archived` | ✅ | VK server |
|
||||
| `run.started` | ❌ | Agent must POST |
|
||||
| `run.completed` | ❌ | Agent must POST |
|
||||
| `run.tokens` | ❌ | 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).
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -64,13 +64,21 @@ curl http://localhost:3001/api/workflows
|
|||
"id": "feature-dev",
|
||||
"name": "Feature Development Workflow",
|
||||
"version": 2,
|
||||
"description": "End-to-end feature development pipeline"
|
||||
"description": "End-to-end feature development pipeline",
|
||||
"agentCount": 4,
|
||||
"stepCount": 7,
|
||||
"createdAt": "2026-02-09T12:00:00Z",
|
||||
"updatedAt": "2026-02-09T14:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": "security-audit",
|
||||
"name": "Security Audit & Remediation",
|
||||
"version": 1,
|
||||
"description": "Scan, prioritize, and fix security issues"
|
||||
"description": "Scan, prioritize, and fix security issues",
|
||||
"agentCount": 3,
|
||||
"stepCount": 5,
|
||||
"createdAt": "2026-02-09T10:00:00Z",
|
||||
"updatedAt": "2026-02-09T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
@ -165,49 +173,6 @@ 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.
|
||||
|
|
@ -557,14 +522,6 @@ 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
|
||||
|
|
@ -2141,14 +2098,12 @@ 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
|
||||
|
|
@ -2177,7 +2132,6 @@ 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?: {
|
||||
|
|
@ -2189,12 +2143,6 @@ 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,9 +6,8 @@ Codify what works (and what burns us) when running Veritas Kanban with humans +
|
|||
|
||||
## Do This
|
||||
|
||||
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.
|
||||
1. **Always track time**
|
||||
- Start timers with `vk begin` the moment you pick up a task.
|
||||
- If you forgot, add a manual entry with reason. Time data fuels estimation and billing.
|
||||
|
||||
2. **Use subtasks as living checklists**
|
||||
|
|
@ -27,9 +26,8 @@ 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 configured review requirements**
|
||||
- Run independent or cross-model review only when the task, governance
|
||||
policy, issue owner, or release owner requires it.
|
||||
7. **Respect cross-model review**
|
||||
- Treat it like CI. No code ships without the opposite model’s signoff.
|
||||
|
||||
8. **Mirror important artifacts to Brain/knowledge base**
|
||||
- Use `scripts/brain-write.sh` or equivalent so humans can find deliverables later.
|
||||
|
|
@ -54,8 +52,7 @@ 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**
|
||||
- Read summaries, inspect diffs, and enforce the review policy selected for
|
||||
the task.
|
||||
- Always read summaries, review diffs, enforce cross-model review.
|
||||
|
||||
5. **Letting prompts drift**
|
||||
- Keep prompt registry updated or agents will regress.
|
||||
|
|
|
|||
|
|
@ -1,471 +0,0 @@
|
|||
# Buzz Communication Adapter
|
||||
|
||||
Veritas Kanban can map a Buzz community channel to Squad Chat and move signed
|
||||
root messages and replies in both directions. The integration uses Buzz's
|
||||
native Nostr HTTP and WebSocket contracts. It does not spawn `buzz`,
|
||||
`buzz-acp`, or `buzz-agent` for delivery.
|
||||
|
||||
Buzz is a communication adapter, not an `AgentProvider`. It does not create a
|
||||
Veritas task, start an ACP agent, synchronize DMs or forums, or read Buzz
|
||||
Desktop's internal state. An operator may separately materialize selected
|
||||
public Buzz persona and team definitions as disabled Veritas profiles and
|
||||
roster members. Definition import is data-only and never starts a process.
|
||||
|
||||
Task execution is a separate seam. A disabled-by-default `buzz-agent`
|
||||
configuration uses provider `acp-stdio` and the generic ACP client. It never
|
||||
turns relay delivery into task completion and never launches `buzz-acp`.
|
||||
Selected task tools are exposed only through the provider-neutral
|
||||
`veritas-run` bridge, with an opaque task/attempt/catalog/manifest binding;
|
||||
native server credentials and the global Veritas MCP inventory are not passed
|
||||
to Buzz.
|
||||
See [Buzz Agent ACP](AGENT-PROVIDERS.md#buzz-agent-acp).
|
||||
|
||||
## Supported contract
|
||||
|
||||
The adapter is fixture-pinned to:
|
||||
|
||||
- Buzz release `0.4.24`
|
||||
- Buzz commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`
|
||||
- Veritas probe revision `1`
|
||||
- Relay software identity `https://github.com/block/buzz`
|
||||
- Required NIPs `11`, `29`, and `42`
|
||||
- Optional enforced relay membership advertised as NIP `43`
|
||||
|
||||
The initial event projection is:
|
||||
|
||||
| Buzz surface | Veritas behavior |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Kind `9` root message | Creates one Squad Chat message in the mapped target. |
|
||||
| Kind `9` reply | Creates one threaded Squad Chat reply using Buzz root/reply tags. |
|
||||
| Veritas root | Signs and publishes one kind `9` event with the mapped `h` channel tag. |
|
||||
| Veritas reply | Publishes a direct or nested reply with the exact Buzz root/reply markers. |
|
||||
| Kind `40003` edit | Records bounded audit metadata. Existing Squad Chat text is unchanged. |
|
||||
| Kind `9005` or NIP-09 kind `5` delete | Records bounded deletion metadata. Local content is not removed. |
|
||||
| Unknown or malformed kind | Ignores it with a redacted delivery audit entry. |
|
||||
| Reactions, files, canvas, forums, DMs | Not projected. |
|
||||
|
||||
An unknown Buzz version is `unsupported`. Veritas may still read public NIP-11
|
||||
metadata, but it will not connect the worker or send messages until the pinned
|
||||
compatibility contract passes.
|
||||
|
||||
## Identity and least privilege
|
||||
|
||||
Use a dedicated Buzz/Nostr identity. Add that public identity only to the
|
||||
community and channels that Veritas must bridge. Veritas does not need channel
|
||||
creation, moderation, desktop storage, or broad community administration.
|
||||
|
||||
Keep the private key in the Veritas server environment and store only its
|
||||
environment-variable reference:
|
||||
|
||||
```dotenv
|
||||
BUZZ_PRIVATE_KEY=<set outside source control>
|
||||
BUZZ_AUTH_TAG=<optional NIP-OA owner attestation>
|
||||
```
|
||||
|
||||
The signing key may be 64-character private-key hex or `nsec`. It must match
|
||||
the configured 64-character public-key hex. `BUZZ_AUTH_TAG` is needed only
|
||||
when an agent identity receives membership through a NIP-OA owner.
|
||||
|
||||
Never put an `nsec`, private-key hex, auth tag, token, authorization header, or
|
||||
raw signed event in a Settings field, API response, task, log, screenshot, or
|
||||
support packet.
|
||||
|
||||
## Configure and map a channel
|
||||
|
||||
In **Settings -> Notifications -> Buzz Connection**, configure:
|
||||
|
||||
- Relay HTTP URL, such as `https://community.example.com`
|
||||
- Optional matching WebSocket URL; Veritas derives it when omitted
|
||||
- Expected community host and optional non-default port
|
||||
- Buzz channel UUID to map to Squad Chat
|
||||
- Public-key hex
|
||||
- `env:BUZZ_PRIVATE_KEY`
|
||||
- Optional `env:BUZZ_AUTH_TAG`
|
||||
- Explicit localhost/private-network allowances when required
|
||||
- Optional `buzz`, `buzz-acp`, or `buzz-agent` executable for version
|
||||
diagnostics
|
||||
|
||||
HTTP and WebSocket endpoints must have the same host, port, path, and TLS
|
||||
posture. Credentials, query strings, and fragments are rejected. A configured
|
||||
path and non-default port are preserved because Buzz binds the community to
|
||||
the request authority.
|
||||
|
||||
The Settings save writes the reference-only adapter first, then writes the
|
||||
Squad Chat channel mapping. Changing a channel disables the old mapping before
|
||||
enabling the new one. Conflicting enabled mappings for the same target are
|
||||
rejected.
|
||||
|
||||
## API setup
|
||||
|
||||
`settings:write` is required to configure, map, send, reconcile, disable, or
|
||||
disconnect. `settings:read` can read adapters, mappings, health, and delivery
|
||||
history.
|
||||
|
||||
Configure the connection:
|
||||
|
||||
```bash
|
||||
curl -X PUT http://localhost:3001/api/integrations/communication/adapters/buzz-default \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-API-Key: <veritas-api-key>' \
|
||||
--data '{
|
||||
"kind": "buzz",
|
||||
"displayName": "Buzz",
|
||||
"enabled": true,
|
||||
"relayHttpUrl": "https://community.example.com",
|
||||
"expectedCommunity": "community.example.com",
|
||||
"publicKey": "<64-hex-public-key>",
|
||||
"credentialRef": "env:BUZZ_PRIVATE_KEY",
|
||||
"authTagRef": "env:BUZZ_AUTH_TAG"
|
||||
}'
|
||||
```
|
||||
|
||||
Map one Buzz channel to Squad Chat:
|
||||
|
||||
```bash
|
||||
CHANNEL_ID=123e4567-e89b-42d3-a456-426614174000
|
||||
|
||||
curl -X PUT \
|
||||
"http://localhost:3001/api/integrations/communication/adapters/buzz-default/buzz/channels/${CHANNEL_ID}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-API-Key: <veritas-api-key>' \
|
||||
--data '{
|
||||
"target": { "kind": "squad" },
|
||||
"enabled": true,
|
||||
"actor": "operator"
|
||||
}'
|
||||
```
|
||||
|
||||
Run the compatibility probe:
|
||||
|
||||
```bash
|
||||
curl \
|
||||
http://localhost:3001/api/integrations/communication/adapters/buzz-default/health \
|
||||
-H 'X-API-Key: <veritas-api-key>'
|
||||
|
||||
vk doctor --json
|
||||
```
|
||||
|
||||
`POST .../buzz-default/test` runs the same read-only probe. It never sends a
|
||||
message.
|
||||
|
||||
## Import persona and team definitions
|
||||
|
||||
In **Settings -> Agents -> Buzz Persona and Team Definitions**, an operator can
|
||||
list, preview, and explicitly import public Buzz definition heads:
|
||||
|
||||
- kind `30175` persona definitions keyed by `(author, kind, d tag)`;
|
||||
- kind `30176` team definitions keyed the same way; and
|
||||
- the deterministic NIP-33 head with the greatest `created_at`, using the
|
||||
lowest event ID to break a tie.
|
||||
|
||||
The importer reuses the signed, DNS-pinned Buzz `/query` transport and requires
|
||||
current healthy compatibility evidence. Every candidate has bounded Nostr
|
||||
shape, tags, content, JSON depth, arrays, strings, and batch size. Its
|
||||
signature is reconstructed and verified before it can appear in Settings.
|
||||
Invalid envelopes contribute only to a rejected count. A signature-valid
|
||||
current head with rejected content appears as a non-importable coordinate and
|
||||
field-level validation reason, so Veritas never silently falls back to an older
|
||||
definition. Unsafe source values are not echoed into the UI or logs.
|
||||
|
||||
Preview classifies each field before mutation:
|
||||
|
||||
| Buzz definition field | Import behavior |
|
||||
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| Persona `display_name` | Source-owned profile display name. |
|
||||
| Persona `system_prompt` | Source-owned profile prompt when present. |
|
||||
| Persona `avatar_url` | Validated public metadata only. Veritas does not fetch it. |
|
||||
| Persona `runtime`, `model`, `provider` | Source preferences only, never runtime evidence or active provider configuration. |
|
||||
| Persona `name_pool` | Bounded source metadata only. |
|
||||
| Persona reserved response fields | Source-only. Veritas does not apply them. |
|
||||
| Team `name`, `description` | Source-owned roster fields for create or refresh. |
|
||||
| Team `persona_ids` | Same-author persona slugs resolved to linked profiles and disabled roster members. |
|
||||
| Unknown fields | Ignored with a field-level forward-compatibility explanation. |
|
||||
| Secrets, environment, commands, paths, managed process state, MCP, hooks, skills, engrams | Rejected. |
|
||||
|
||||
The available actions are:
|
||||
|
||||
- `create`: use the deterministic `buzz-<slug>` profile ID or
|
||||
`buzz-team-<slug>` roster ID;
|
||||
- `link`: attach provenance to an explicitly selected existing profile or
|
||||
roster without replacing local fields;
|
||||
- `refresh`: replace only the saved source-owned fields after a new preview;
|
||||
and
|
||||
- `skip`: record no local mutation.
|
||||
|
||||
Create, link, and refresh require collision-free preview. A preview returns an
|
||||
optimistic local revision and exact source event ID. Import rejects a changed
|
||||
local target or replaced source, so the operator must review the current diff
|
||||
instead of overwriting concurrent edits. Native profile/roster fields remain
|
||||
authoritative; refresh preserves local-only fields and existing routing rules.
|
||||
|
||||
New persona profiles, new rosters, and imported roster members are disabled.
|
||||
Import never launches, enables, routes, installs, fetches, or writes back to
|
||||
Buzz. Removing or replacing a Buzz definition changes its linked-source status
|
||||
to `missing` or `changed`; it does not delete the materialized local object.
|
||||
|
||||
Definition API:
|
||||
|
||||
```text
|
||||
GET /api/integrations/communication/adapters/:adapterId/buzz/definitions
|
||||
GET /api/integrations/communication/adapters/:adapterId/buzz/definitions/links
|
||||
POST /api/integrations/communication/adapters/:adapterId/buzz/definitions/preview
|
||||
POST /api/integrations/communication/adapters/:adapterId/buzz/definitions/import
|
||||
```
|
||||
|
||||
Reads and preview require `settings:read`. Import requires `settings:write`.
|
||||
There is no continuous synchronization and no Buzz write-back endpoint.
|
||||
|
||||
## Trigger a Veritas workflow
|
||||
|
||||
A `buzz-workflow-trigger/v1` rule can bind one active channel mapping to one
|
||||
Veritas workflow. The first supported event is a root kind `9`
|
||||
`message.posted`. Replies, edits, deletes, reactions, adapter-originated
|
||||
echoes, disabled rules, and predicate mismatches do not launch a run.
|
||||
|
||||
Create a rule with the mapping ID returned by the channel-mapping API:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:3001/api/integrations/communication/adapters/buzz-default/buzz/workflow-triggers \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-API-Key: <veritas-api-key>' \
|
||||
--data '{
|
||||
"mappingId": "buzz_map_example",
|
||||
"workflowId": "triage-external-request",
|
||||
"contentIncludes": "help"
|
||||
}'
|
||||
```
|
||||
|
||||
An exact 64-character author public key may be supplied as `authorPubkey`.
|
||||
Content matching is a bounded, case-insensitive substring check. There is no
|
||||
regex, expression, code, shell, or arbitrary Nostr-kind filter.
|
||||
|
||||
Rule creation requires `settings:write` and execute permission on the
|
||||
destination workflow. The mapping alone does not grant workflow or task
|
||||
mutation rights.
|
||||
|
||||
Veritas persists the accepted causal key
|
||||
`buzz:{community}:{eventId}:{ruleId}` before dispatch through the
|
||||
provider-neutral `workflow.pre-external-trigger` hook. Workflow context
|
||||
retains the community, channel, event, author, mapping, rule, and sanitized
|
||||
message. A replay returns the existing run. After restart, Veritas searches
|
||||
the destination workflow's run context for the causal key before launching
|
||||
another run.
|
||||
|
||||
List or disable rules and inspect bounded disposition history:
|
||||
|
||||
```text
|
||||
GET /api/integrations/communication/adapters/:adapterId/buzz/workflow-triggers
|
||||
POST /api/integrations/communication/adapters/:adapterId/buzz/workflow-triggers/:ruleId/disable
|
||||
GET /api/integrations/communication/adapters/:adapterId/buzz/workflow-trigger-audits
|
||||
```
|
||||
|
||||
Disabling a rule retains its prior audits and linked workflow runs.
|
||||
|
||||
## Send roots and replies
|
||||
|
||||
Send a root and associate it with a local Squad Chat message:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:3001/api/integrations/communication/adapters/buzz-default/send \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-API-Key: <veritas-api-key>' \
|
||||
--data '{
|
||||
"target": {
|
||||
"kind": "squad",
|
||||
"squadMessageId": "msg_local_root"
|
||||
},
|
||||
"message": "Root message from Veritas",
|
||||
"actor": "VERITAS"
|
||||
}'
|
||||
```
|
||||
|
||||
Send a reply by identifying both the new local message and the local parent:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
http://localhost:3001/api/integrations/communication/adapters/buzz-default/send \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-API-Key: <veritas-api-key>' \
|
||||
--data '{
|
||||
"target": {
|
||||
"kind": "squad",
|
||||
"squadMessageId": "msg_local_reply"
|
||||
},
|
||||
"replyToSquadMessageId": "msg_local_root",
|
||||
"message": "Reply from Veritas",
|
||||
"actor": "VERITAS"
|
||||
}'
|
||||
```
|
||||
|
||||
The parent must already have a durable Buzz event mapping. A missing parent is
|
||||
blocked instead of publishing a detached root.
|
||||
|
||||
Each outbound event includes the mapped `h` tag, a `client=veritas-kanban`
|
||||
marker, and a stable `veritas-id` delivery marker. Veritas persists the signed
|
||||
event and event ID before submitting it to `/events`.
|
||||
|
||||
## Inbound subscription and replay
|
||||
|
||||
One supervised WebSocket worker runs per enabled, compatible Buzz connection.
|
||||
It:
|
||||
|
||||
1. resolves and pins the configured relay address through the outbound network
|
||||
policy;
|
||||
2. answers the NIP-42 challenge with kind `22242` and the optional NIP-OA auth
|
||||
tag;
|
||||
3. subscribes only to enabled mapped channel UUIDs and kinds `9`, `40003`,
|
||||
`9005`, and `5`;
|
||||
4. resumes from the persisted cursor with a five-second overlap;
|
||||
5. verifies each Nostr event signature, channel, kind, timestamp, and size;
|
||||
6. projects and audits the Squad Chat message; and
|
||||
7. commits the total-order cursor `(created_at, event_id)` only after the
|
||||
projection and audit are durable.
|
||||
|
||||
Deduplication uses `(community, event_id)`, never timestamp alone. Squad Chat
|
||||
uses the deterministic local ID `msg_buzz_<event-id>`, so a crash after the
|
||||
chat write but before adapter-state persistence replays safely. The original
|
||||
Buzz author public key, source timestamp, event kind, channel, community,
|
||||
event ID, and `buzz://message` link remain attached as external metadata.
|
||||
|
||||
An out-of-order reply is persisted but does not advance the cursor past its
|
||||
missing root. When the root arrives, queued replies are replayed in
|
||||
`(created_at, event_id)` order. A root that never arrives remains bounded
|
||||
pending state rather than becoming a detached Squad Chat message.
|
||||
|
||||
Adapter-originated event IDs are retained and ignored when echoed by the
|
||||
subscription, preventing a reply loop.
|
||||
|
||||
## Ambiguous delivery recovery
|
||||
|
||||
Network failure after a write can leave delivery status unknown. Veritas does
|
||||
not blindly retry:
|
||||
|
||||
1. the delivery remains visible as `delivery_unknown`;
|
||||
2. `POST .../buzz-default/poll` queries `/query` by the signed event ID;
|
||||
3. if the event exists, the original delivery becomes `success`;
|
||||
4. if the relay definitively reports absence, Veritas resubmits the exact
|
||||
persisted signed event; and
|
||||
5. if the query is inconclusive, the delivery remains unresolved.
|
||||
|
||||
Before query or resubmission, the persisted event is re-verified against its
|
||||
signature, configured public identity, mapped community/channel, and event ID.
|
||||
A corrupted record is failed and never retried.
|
||||
|
||||
## Health and audit
|
||||
|
||||
Health separates:
|
||||
|
||||
- compatibility and authorization checks;
|
||||
- relay transport connection;
|
||||
- active subscription state;
|
||||
- mapped-channel count;
|
||||
- reconnect attempts and last connection;
|
||||
- last inbound event;
|
||||
- cursor lag;
|
||||
- last send time and status; and
|
||||
- the latest redacted worker error.
|
||||
|
||||
Compatibility can be healthy while runtime status is `degraded`, such as when
|
||||
no channel is mapped or the subscription is still connecting. `canSend`
|
||||
requires an enabled adapter, current healthy compatibility evidence, and at
|
||||
least one mapped channel. `canReceiveReplies` additionally requires an active
|
||||
subscription.
|
||||
|
||||
Delivery history exposes `queued`, `success`, `delivery_unknown`, `replayed`,
|
||||
`ignored`, `failed`, `blocked`, and `skipped`. It retains bounded coordinates
|
||||
and redacted details, not credentials or raw authorization material.
|
||||
|
||||
## Network policy
|
||||
|
||||
Public HTTPS/WSS is the default. Plain HTTP/WS requires an explicit localhost
|
||||
or private-network allowance. Localhost and RFC1918/IPv6 ULA ranges are denied
|
||||
unless their matching setting is enabled. Link-local, cloud metadata, and
|
||||
CGNAT ranges remain blocked. DNS is resolved and pinned, redirects are
|
||||
disabled, payloads are bounded, and requests have fixed timeouts.
|
||||
|
||||
Enable only the narrow network class required by the relay.
|
||||
|
||||
## Credential-free release gate
|
||||
|
||||
Run the composed Buzz gate from the repository root:
|
||||
|
||||
```bash
|
||||
pnpm test:buzz:compatibility
|
||||
```
|
||||
|
||||
This command runs the existing credential-free fixtures for:
|
||||
|
||||
- pinned relay/community/identity compatibility and secret non-retention;
|
||||
- signed root/reply communication, reconnect replay, dedupe, loop prevention,
|
||||
and ambiguous delivery recovery;
|
||||
- `buzz-agent` through generic ACP;
|
||||
- the opaque run-scoped Veritas MCP bridge;
|
||||
- persona/team preview, import, refresh, provenance, and unsafe-source
|
||||
rejection;
|
||||
- the typed root-message workflow trigger and causal replay; and
|
||||
- the machine-readable compatibility record.
|
||||
|
||||
The canonical support record is still
|
||||
`GET /api/config/harness-compatibility`. Its Buzz entry pins release `0.4.24`,
|
||||
commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`, `buzz-agent 0.1.0`,
|
||||
provider probe revision, fixture revision, and every seam fixture path. A
|
||||
provider build, protocol, capability, probe, configuration, or fixture change
|
||||
invalidates prior certification.
|
||||
|
||||
The green aggregate gate means only the following:
|
||||
|
||||
| Capability | Disposition |
|
||||
| ----------------------------------------------------------------------- | ---------------------------------------------- |
|
||||
| Relay/community/identity diagnostics | Supported at the pinned baseline |
|
||||
| Mapped roots/replies, replay, dedupe, and loop prevention | Supported |
|
||||
| `buzz-agent` through generic ACP | Supported at the pinned ACP contract |
|
||||
| Run-scoped Veritas MCP | Supported through the provider-neutral bridge |
|
||||
| Public persona/team import | Supported as explicit one-way materialization |
|
||||
| One typed root message to a Veritas workflow | Supported |
|
||||
| `buzz-acp` as a Veritas provider | Rejected; it is the inverse Buzz-owned harness |
|
||||
| Buzz workflow definition execution and cross-system approvals | Deferred |
|
||||
| NIP-AE memory sync and automatic NIP-34 task mirroring | Deferred or rejected |
|
||||
| Desktop internals, DMs, forums, canvas, moderation, huddles, and mobile | Not implied by this gate |
|
||||
|
||||
An unknown or changed Buzz build remains unsupported or degraded until the
|
||||
baseline, evidence digest, fixtures, and documentation are explicitly
|
||||
updated.
|
||||
|
||||
### Optional live smoke
|
||||
|
||||
Live smoke is supplemental and must target the exact candidate build with a
|
||||
dedicated least-privilege identity. It is not normal CI:
|
||||
|
||||
1. Run `vk doctor --json` and retain only redacted public build/status fields.
|
||||
2. Publish one Veritas root to a dedicated mapped test channel.
|
||||
3. Reply from Buzz and verify the reply appears in the correct Squad Chat
|
||||
thread, not merely as an HTTP success or ACP `end_turn`.
|
||||
4. Trigger one allowlisted test workflow and verify its causal event/run link.
|
||||
5. Disable the test mapping, rule, and profile.
|
||||
|
||||
Do not upload raw events, private messages, authorization headers, auth tags,
|
||||
private keys, provider keys, or unredacted logs as evidence.
|
||||
|
||||
## Disable, upgrade, and rollback
|
||||
|
||||
Disabling a channel mapping closes and rebuilds the worker without deleting
|
||||
the mapping, cursor, event coordinates, or delivery audit. Disconnecting the
|
||||
adapter closes the worker and disables delivery while retaining reference-only
|
||||
configuration and recovery state.
|
||||
|
||||
Remove environment secrets separately only when retiring the identity.
|
||||
Veritas never removes relay membership, changes a Buzz community, or modifies
|
||||
Buzz Desktop state.
|
||||
|
||||
After a Buzz upgrade, run `vk doctor --json`. A version/build change
|
||||
invalidates prior compatibility evidence and must pass the pinned contract
|
||||
and `pnpm test:buzz:compatibility` before workers, sends, or ACP dispatch
|
||||
resume. A baseline change updates the release/commit constants, matrix
|
||||
evidence digest, fixtures, and this guide together.
|
||||
|
||||
If a candidate fails, keep the prior baseline and report the failing facet.
|
||||
Disable the affected adapter, mapping, profile, or trigger rule without
|
||||
deleting redacted configuration, mappings, cursors, import provenance, trigger
|
||||
audits, or workflow-run evidence.
|
||||
|
|
@ -19,8 +19,6 @@ 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)
|
||||
|
|
@ -447,33 +445,14 @@ vk project create "rubicon" --color "#7c3aed" --description "Main product"
|
|||
|
||||
Manage AI agents on code tasks.
|
||||
|
||||
| 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) |
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| `vk start <id>` | Start an agent; optionally require runtime capabilities |
|
||||
| `vk stop <id>` | Stop a run only when its persisted manifest supports stop |
|
||||
| `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:
|
||||
|
||||
|
|
@ -484,51 +463,6 @@ vk start TASK-001 --agent codex \
|
|||
--json
|
||||
```
|
||||
|
||||
Preview without dispatching, or compare a new launch with a parent attempt:
|
||||
|
||||
```bash
|
||||
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, 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
|
||||
conflict and the CLI exits non-zero when any capability is unsupported,
|
||||
|
|
@ -539,13 +473,6 @@ It overrides a task default and the legacy auto-commit setting. Omitting the
|
|||
flag keeps existing tasks compatible: commits are allowed but not required
|
||||
unless a task or legacy setting explicitly requires one.
|
||||
|
||||
Lifecycle commands fail closed from the persisted runtime manifest. Resume
|
||||
requires the exact source worktree; fork permits a compatible worktree at the
|
||||
same repository and base revision. `--fork-turn <id>` selects an optional
|
||||
provider-native history boundary. Unsupported controls preserve the server's
|
||||
reason, and a recorded operator message is never reported as delivered unless
|
||||
the adapter executed a verified native steering operation.
|
||||
|
||||
Use `vk agents:status TASK-001 --json` to inspect the persisted manifest and
|
||||
capability-derived `controls` set. `vk stop` does not infer support from the
|
||||
agent name. It resolves the current `attemptId` from status and includes it in
|
||||
|
|
@ -553,199 +480,6 @@ 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
|
||||
|
|
@ -852,51 +586,10 @@ Manage GitHub Issues bidirectional sync.
|
|||
| --------------------- | ------------------------------------------------------------------------------ |
|
||||
| `vk summary` | Project stats: status counts, project progress, high-priority items |
|
||||
| `vk summary standup` | Daily standup summary (`--yesterday`, `--date YYYY-MM-DD`, `--json`, `--text`) |
|
||||
| `vk doctor` | Validate API, routing, executable, and harness support readiness (`--json`) |
|
||||
| `vk notify <message>` | Create a notification (`--type`, `--title`, `--task` options) |
|
||||
| `vk notify:check` | Check for tasks that need notifications |
|
||||
| `vk notify:pending` | Get pending notifications formatted for Teams |
|
||||
|
||||
`vk doctor` reads the same redacted harness support projection shown in
|
||||
Settings. Enabled `degraded` or `unsupported` profiles fail the doctor check;
|
||||
enabled `configured` profiles warn until their installed build has current
|
||||
certification evidence. Use `vk doctor --json` for support-safe automation and
|
||||
diagnostics, including redacted readiness reasons, safe probe commands, and
|
||||
remediation.
|
||||
|
||||
For Claude Code, doctor reports the bounded version, auth-status, and agent
|
||||
discovery probes, plus separate bare-mode authentication readiness. A
|
||||
successful interactive OAuth status is diagnostic only because Veritas
|
||||
launches Claude Code with `--bare`; configure an explicit supported
|
||||
environment credential before enabling the profile.
|
||||
|
||||
For Codex app-server, doctor requires the exact `codex-cli 0.145.0` executable,
|
||||
`codex login status`, the system-owned strict-stdio launch contract, and the
|
||||
current provider build/probe evidence. Custom app-server arguments or version
|
||||
drift degrade the profile and block dispatch. `codex-cli`, `codex-sdk`, and
|
||||
`codex-app-server` are reported as separate profiles with separate capability
|
||||
manifests.
|
||||
|
||||
### Run-scoped Tool Servers
|
||||
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------- | ------------------------------------------------ |
|
||||
| `vk tool-servers list [--json]` | List registered definitions |
|
||||
| `vk tool-servers get <id> --json` | Read one definition |
|
||||
| `vk tool-servers create <definition.json>` | Create a validated definition |
|
||||
| `vk tool-servers update <id> <definition.json>` | Replace a definition |
|
||||
| `vk tool-servers delete <id>` | Delete a definition |
|
||||
| `vk tool-servers enable <id>` / `disable <id>` | Change launch eligibility |
|
||||
| `vk tool-servers version <id> <version>` | Change version identity and invalidate discovery |
|
||||
| `vk tool-servers discover <id> [--force] [--json]` | Refresh version-bound discovery |
|
||||
| `vk tool-servers catalog <taskId> <attemptId> --json` | Read an immutable run catalog |
|
||||
| `vk tool-servers call <taskId> <attemptId> <serverId> <tool> ...` | Invoke through policy, approval, and event gates |
|
||||
|
||||
Calls require `--arguments '<json-object>'`. Use a stable `--operation-id`
|
||||
when retrying transport failures. If the tool requires approval, the command
|
||||
returns the exact approval identity; retry with `--approval-id` after that
|
||||
request is approved. The `tools` command is an alias for `tool-servers`.
|
||||
|
||||
---
|
||||
|
||||
## Workflow Commands Deep Dive
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ Companion docs:
|
|||
|
||||
- [SOP: OpenAI Codex Integration](SOP-codex-integration.md)
|
||||
- [Codex Workflow Examples](EXAMPLES-codex-workflows.md)
|
||||
- [Optional Independent Code Review](SOP-cross-model-code-review.md)
|
||||
- [SOP: Cross-Model Code Review](SOP-cross-model-code-review.md)
|
||||
- [AGENTS.md Template](AGENTS-TEMPLATE.md)
|
||||
|
||||
## Product Goal
|
||||
|
|
@ -80,17 +80,7 @@ POST /api/github/codex/delegate
|
|||
|
||||
## Architecture Direction
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Expected long-term provider capabilities:
|
||||
|
||||
|
|
@ -103,7 +93,7 @@ Expected long-term provider capabilities:
|
|||
- optional `review`
|
||||
- optional `cloudDelegate`
|
||||
|
||||
The provider adapter interface supports:
|
||||
The provider abstraction should support:
|
||||
|
||||
- OpenClaw compatibility through an OpenClaw provider adapter.
|
||||
- Codex CLI through a local process provider.
|
||||
|
|
|
|||
|
|
@ -1,180 +0,0 @@
|
|||
# Credential Broker
|
||||
|
||||
Veritas Kanban keeps task credentials out of provider processes by separating
|
||||
metadata, leases, and value resolution.
|
||||
|
||||
The v6 foundation includes:
|
||||
|
||||
- `credential-definition/v1` metadata records;
|
||||
- `credential-lease/v1` run-bound leases;
|
||||
- opaque provider-safe handles persisted only as hashes;
|
||||
- exact task, attempt, immutable launch-manifest, scope, and action binding;
|
||||
- TTL, maximum-use, refresh, revocation, expiry, and reconciliation state;
|
||||
- metadata-only audit events; and
|
||||
- a controlled in-process callback that is the only API allowed to receive the
|
||||
resolved value.
|
||||
- `run-launch-credential-plan/v1` evidence that classifies provider boot
|
||||
authentication, task integration references, and high-risk compatibility
|
||||
passthrough without storing values.
|
||||
|
||||
The tool control plane compiles value-free credential boundary evidence into a
|
||||
run catalog and consumes leases only inside mediated tool calls. A handle in a
|
||||
prompt or provider environment is not itself a credential boundary:
|
||||
credential-bound native server injection remains omitted. A system-owned
|
||||
`veritas-run` MCP bridge receives only an opaque, in-memory authority bound to
|
||||
the exact task, attempt, catalog, launch manifest, and catalog/call methods.
|
||||
The provider never receives the task credential value.
|
||||
|
||||
## Credential classes
|
||||
|
||||
Treat these as separate:
|
||||
|
||||
1. **Harness boot authentication** starts the provider itself, such as native
|
||||
login state or a model-provider key required by the provider executable.
|
||||
2. **Task integration credentials** authorize a bounded HTTP, tool, or MCP
|
||||
action during a run. These are the broker target.
|
||||
3. **Compatibility passthrough** explicitly places a raw value in the provider
|
||||
environment. It is high risk and never counts as brokered.
|
||||
|
||||
Every newly compiled run launch manifest records those classes, delivery
|
||||
posture, boundary posture, and provider-runtime evidence in a deterministic
|
||||
credential plan. Known native provider authentication keys are classified as
|
||||
boot authentication. Unknown credential-like environment keys are classified
|
||||
as high-risk compatibility passthrough. Broker definition IDs are classified
|
||||
as task integration credentials and block launch while their controlled
|
||||
boundary is unavailable. Probe timestamp-only refreshes do not create material
|
||||
drift, but provider build, classification, mode, reference, delivery, boundary,
|
||||
or risk changes do.
|
||||
|
||||
## Register a definition
|
||||
|
||||
Definitions are admin-only:
|
||||
|
||||
```http
|
||||
POST /api/credential-broker
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "github-token",
|
||||
"name": "GitHub token",
|
||||
"enabled": true,
|
||||
"source": {
|
||||
"kind": "environment",
|
||||
"reference": "VK_GITHUB_TOKEN"
|
||||
},
|
||||
"scope": {
|
||||
"dispatchTypes": ["http"],
|
||||
"hosts": ["api.github.com"],
|
||||
"tools": [],
|
||||
"destinations": ["https://api.github.com"],
|
||||
"methods": ["GET"],
|
||||
"actions": ["issues.read"],
|
||||
"pathPrefixes": ["/repos/"]
|
||||
},
|
||||
"lease": {
|
||||
"ttlSeconds": 60,
|
||||
"maxUses": 1,
|
||||
"renewable": false
|
||||
},
|
||||
"approval": "not-required"
|
||||
}
|
||||
```
|
||||
|
||||
`source.reference` is an environment key name or external manager path, never a
|
||||
value. The initial local source can resolve an environment key at the internal
|
||||
dispatch boundary. Production deployments should use a future external
|
||||
secret-manager adapter instead of treating process environment as a vault.
|
||||
|
||||
Metadata that resembles an embedded token, authorization header, or
|
||||
`name=value` credential is rejected.
|
||||
|
||||
## Lease lifecycle
|
||||
|
||||
The internal broker issues a lease only when:
|
||||
|
||||
- the task has the requested active attempt;
|
||||
- the immutable run launch manifest digest matches;
|
||||
- that manifest declares the definition reference;
|
||||
- the definition is enabled;
|
||||
- the exact action is inside every configured scope; and
|
||||
- any required approval verifier authorizes the same action fingerprint.
|
||||
|
||||
The raw handle is returned once to the internal caller. Persistence contains
|
||||
only its SHA-256 hash. The lease records definition, scope, action, run, expiry,
|
||||
use-count, SHA-256 fingerprints of caller-supplied operation IDs, and optional
|
||||
approval fingerprints. Raw operation IDs are never persisted or audited.
|
||||
|
||||
Use is compare-and-set serialized. A consumer must present the same task,
|
||||
attempt, launch manifest, handle, canonical action, and a unique operation ID.
|
||||
A changed host, destination, method, path, tool, action, or arguments digest
|
||||
fails closed. Reusing an operation ID is rejected instead of replaying a
|
||||
credential-bearing action or refresh. Source resolution happens only after the
|
||||
use is claimed. Missing sources and callbacks that return, throw, or conceal
|
||||
credential material in accessors, custom objects, cycles, or excessively deep
|
||||
results produce credential-free errors. Binary callback results are rejected
|
||||
entirely because backing buffers can expose bytes outside a visible slice or
|
||||
mutate after inspection.
|
||||
|
||||
Completion, failure, interruption, and cancellation revoke the matching run
|
||||
leases after the terminal result is durably persisted. Duplicate terminal
|
||||
delivery retries revocation, so a transient broker failure can heal without
|
||||
rewriting the terminal result. Startup and one-minute periodic reconciliation:
|
||||
|
||||
- expires leases past their TTL;
|
||||
- blocks leases whose source is unavailable;
|
||||
- revokes leases whose definition changed or was disabled;
|
||||
- revokes leases whose run or manifest binding disappeared; and
|
||||
- leaves only currently valid active leases usable.
|
||||
|
||||
Manifest declarations and sandbox `brokerRefs` are exact definition IDs. Values
|
||||
such as `github-token=...` are invalid and never normalize to a valid reference.
|
||||
The broker state writer publishes complete owner-token lock metadata atomically
|
||||
and never auto-deletes an existing lock. Dead, malformed, or otherwise
|
||||
unverifiable ownership fails closed because portable filesystems cannot compare
|
||||
and unlink ownership atomically. After confirming that no Veritas process owns
|
||||
the state file, an operator may remove the adjacent `.lock` file and let
|
||||
reconciliation retry.
|
||||
|
||||
## Audit record
|
||||
|
||||
The broker stores bounded metadata events for definition changes, issue, use,
|
||||
denial, refresh, revoke, expiry, and reconciliation. Events contain IDs,
|
||||
fingerprints, decision reasons, and timestamps. They do not contain headers,
|
||||
request bodies, URLs with query strings, credential values, or callback errors.
|
||||
|
||||
The causal run-event journal will later project this metadata into the unified
|
||||
run stream. Broker correctness does not depend on that projection.
|
||||
|
||||
## Fail-closed provider posture
|
||||
|
||||
A required brokered sandbox preset needs `credential.broker: supported`.
|
||||
`advisory`, externally delegated, unknown, stale, or bypassable evidence is
|
||||
treated as unsupported and blocks launch.
|
||||
|
||||
Current executable providers classify their launch credentials consistently,
|
||||
but classification alone does not make them broker-capable. Controlled HTTP
|
||||
consumption belongs to the run-scoped egress gateway; controlled MCP/tool
|
||||
consumption belongs to the tool-server control plane. A task reference is
|
||||
reported as brokered only when an immutable run catalog contains the matching
|
||||
credential-definition and scope digests. Uncovered references still block
|
||||
launch. During a mediated call, the server-owned manifest digest and exact
|
||||
catalog action issue a lease; its source value exists only inside the
|
||||
downstream dispatch callback.
|
||||
|
||||
## Rotation and revocation
|
||||
|
||||
- Change the external source value without changing the definition to rotate
|
||||
future resolution.
|
||||
- Update or disable a definition to revoke its active leases.
|
||||
- Revoke a lease explicitly for an operator stop.
|
||||
- Do not delete a definition while an active lease exists; disable it first.
|
||||
- Never fall back from a failed brokered lease to raw environment passthrough.
|
||||
|
||||
## Rollback
|
||||
|
||||
Disable brokered sandbox selection and revoke active leases. Keep metadata-only
|
||||
definitions for operator review or delete them after no active leases remain.
|
||||
Rollback never copies a value into app configuration and never weakens a
|
||||
required preset into implicit passthrough.
|
||||
|
|
@ -30,23 +30,22 @@ customer data is processed.
|
|||
|
||||
## Data Classes
|
||||
|
||||
| Data class | Primary tables | Default retention | Export/delete policy |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Workspace identity and membership | `workspaces`, `users`, `workspace_memberships`, `workspace_invitations` | Until admin removal or workspace archival | Scoped exports include selected workspace, memberships, invitations, and member users only. |
|
||||
| Tasks and task metadata | `tasks` | Active/backlog until archived or deleted; archived until cleanup | Preview linked artifacts before deletion. Included in full and scoped exports. |
|
||||
| Task comments and discussion | `tasks` JSON | Follows parent task | Included with task export. Cleanup follows parent task unless comment-level delete is used. |
|
||||
| Uploads and attachment metadata | `task_attachments` | While linked to a task unless cleanup-eligible | Preview parent task, path, size, MIME type, and orphan status before deletion. |
|
||||
| Work products and versions | `work_products`, `work_product_versions`, `task_deliverables` | Until task/workspace cleanup or version retention | Preview source task/run, status, version count, and redaction state. |
|
||||
| Telemetry and metrics events | `telemetry_events` | 30 days by default | Purge only by explicit range/workspace preview. |
|
||||
| Workflow definitions, runs, and scheduled snapshots | `workflow_definitions`, `workflow_acls`, `workflow_audit_events`, `workflow_runs`, `scheduled_deliverables`, `scheduled_deliverable_runs` | Definitions until deleted; runs/snapshots by admin retention | Never delete active runs or current scheduled state silently. |
|
||||
| Notifications and subscriptions | `notifications`, `thread_subscriptions` | Until read/dismissed history cleanup | Preview delivered/read state, target category, source task, and age. |
|
||||
| Chat and squad messages | `chat_sessions`, `chat_messages`, `squad_messages` | Until session/workspace cleanup | Preview session, task link, message count, agents, and age. |
|
||||
| Audit, governance, and policy records | `activity_events`, `status_history`, `decision_records`, `governance_decision_traces`, `feedback_records`, `scoring_profiles`, `scoring_evaluations`, `drift_alerts`, `drift_baselines`, `audit_entries`, `agent_policies`, `tool_policies` | Longer-lived audit evidence | Do not silently delete through operational cleanup. |
|
||||
| Device sessions and API tokens | excluded from backup table exports | Until expiration or revocation | Revoke before deletion. Never print secret values or hashes. |
|
||||
| Credential broker metadata | `.veritas-kanban/credential-broker/state.json` | Definitions until deleted; lease records retained; audit ring capped at 5,000 events | Metadata only. Excluded from workspace SQLite exports until repository migration. Never export raw handles or credential values. |
|
||||
| Configuration and registries | `app_config_documents`, `managed_list_items`, `task_templates`, `prompt_templates`, `prompt_versions`, `prompt_usage` | Until changed or deleted | Full exports include them. Scoped exports exclude global app config. |
|
||||
| Backups, imports, and exports | filesystem bundles and manifests | Until admin removes files | Every export includes a manifest with data classes, row counts, and redaction state. |
|
||||
| Diagnostics and debug bundles | filesystem bundle output | Generated on demand | Redacted by default and includes an included-category manifest. |
|
||||
| Data class | Primary tables | Default retention | Export/delete policy |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| Workspace identity and membership | `workspaces`, `users`, `workspace_memberships`, `workspace_invitations` | Until admin removal or workspace archival | Scoped exports include selected workspace, memberships, invitations, and member users only. |
|
||||
| Tasks and task metadata | `tasks` | Active/backlog until archived or deleted; archived until cleanup | Preview linked artifacts before deletion. Included in full and scoped exports. |
|
||||
| Task comments and discussion | `tasks` JSON | Follows parent task | Included with task export. Cleanup follows parent task unless comment-level delete is used. |
|
||||
| Uploads and attachment metadata | `task_attachments` | While linked to a task unless cleanup-eligible | Preview parent task, path, size, MIME type, and orphan status before deletion. |
|
||||
| Work products and versions | `work_products`, `work_product_versions`, `task_deliverables` | Until task/workspace cleanup or version retention | Preview source task/run, status, version count, and redaction state. |
|
||||
| Telemetry and metrics events | `telemetry_events` | 30 days by default | Purge only by explicit range/workspace preview. |
|
||||
| Workflow definitions, runs, and scheduled snapshots | `workflow_definitions`, `workflow_acls`, `workflow_audit_events`, `workflow_runs`, `scheduled_deliverables`, `scheduled_deliverable_runs` | Definitions until deleted; runs/snapshots by admin retention | Never delete active runs or current scheduled state silently. |
|
||||
| Notifications and subscriptions | `notifications`, `thread_subscriptions` | Until read/dismissed history cleanup | Preview delivered/read state, target category, source task, and age. |
|
||||
| Chat and squad messages | `chat_sessions`, `chat_messages`, `squad_messages` | Until session/workspace cleanup | Preview session, task link, message count, agents, and age. |
|
||||
| Audit, governance, and policy records | `activity_events`, `status_history`, `decision_records`, `governance_decision_traces`, `feedback_records`, `scoring_profiles`, `scoring_evaluations`, `drift_alerts`, `drift_baselines`, `audit_entries`, `agent_policies`, `tool_policies` | Longer-lived audit evidence | Do not silently delete through operational cleanup. |
|
||||
| Device sessions and API tokens | excluded from backup table exports | Until expiration or revocation | Revoke before deletion. Never print secret values or hashes. |
|
||||
| Configuration and registries | `app_config_documents`, `managed_list_items`, `task_templates`, `prompt_templates`, `prompt_versions`, `prompt_usage` | Until changed or deleted | Full exports include them. Scoped exports exclude global app config. |
|
||||
| Backups, imports, and exports | filesystem bundles and manifests | Until admin removes files | Every export includes a manifest with data classes, row counts, and redaction state. |
|
||||
| Diagnostics and debug bundles | filesystem bundle output | Generated on demand | Redacted by default and includes an included-category manifest. |
|
||||
|
||||
The canonical machine-readable policy is exposed by:
|
||||
|
||||
|
|
@ -54,11 +53,6 @@ The canonical machine-readable policy is exposed by:
|
|||
GET /api/v1/sqlite/lifecycle-policy
|
||||
```
|
||||
|
||||
That endpoint describes the SQLite-backed data classes used by backup,
|
||||
retention, export, and delete workflows. The file-backed credential broker row
|
||||
above is an explicit exception until its repository migrates to SQLite, so it is
|
||||
not represented in the endpoint response or SQLite export manifests.
|
||||
|
||||
## Backup Manifest
|
||||
|
||||
SQLite export manifests use `formatVersion: 2` and include:
|
||||
|
|
@ -96,15 +90,6 @@ rules:
|
|||
telemetry ranges, notifications, and workflow snapshots
|
||||
- require explicit confirmation for destructive cleanup
|
||||
- preserve active worktrees and current run state by default
|
||||
- treat `.veritas-kanban/worktree-manifests/*.json` as ownership and recovery
|
||||
records, not disposable cache; a clean worktree is not proof that it is
|
||||
pushed, merged, unowned, or safe to remove
|
||||
- preview stale worktree candidates before removal and retain the exact blocked
|
||||
reasons; only reasoned overrides may bypass dirty, untracked, unpushed,
|
||||
unmerged, or external-hold checks, while an active run remains
|
||||
non-overrideable
|
||||
- preserve credential broker state during general runtime cleanup; revoke active
|
||||
leases before intentional broker-state removal
|
||||
- emit audit/activity records for admin cleanup, export, import, restore, and
|
||||
retention-setting changes
|
||||
|
||||
|
|
|
|||
|
|
@ -67,39 +67,17 @@ Data is persisted in a Docker named volume (`kanban-data`), so it survives conta
|
|||
|
||||
### Dockerfile Overview
|
||||
|
||||
The multi-stage Dockerfile enforces architecture-specific production image budgets:
|
||||
The multi-stage Dockerfile produces a minimal production image (< 200 MB):
|
||||
|
||||
| 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 |
|
||||
| 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 |
|
||||
|
||||
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.
|
||||
The production stage runs as a non-root user (`veritas`, UID 1001) for security.
|
||||
|
||||
**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.
|
||||
|
||||
|
|
@ -254,10 +232,10 @@ If you need to debug inside a container, use `docker exec` to inspect — don't
|
|||
|
||||
### Prerequisites
|
||||
|
||||
| Requirement | Version |
|
||||
| ----------- | --------------- |
|
||||
| Node.js | 22.22.1+ |
|
||||
| pnpm | 11.1.1 (pinned) |
|
||||
| Requirement | Version |
|
||||
| ----------- | ------- |
|
||||
| Node.js | 22.0.0+ |
|
||||
| pnpm | 11.1.1+ |
|
||||
|
||||
Install pnpm if not present:
|
||||
|
||||
|
|
@ -456,15 +434,18 @@ 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:** One volume at `DATA_DIR` persists tasks and runtime state:
|
||||
**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:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- kanban-data:/app/data # tasks/ plus .veritas-kanban/
|
||||
- kanban-data:/app/data # Task files
|
||||
- kanban-config:/app/.veritas-kanban # Config, sprints, enforcement gates
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
### systemd Service
|
||||
|
||||
|
|
@ -554,14 +535,13 @@ 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 |
|
||||
| `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 |
|
||||
| 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 |
|
||||
|
||||
### Prometheus metrics
|
||||
|
||||
|
|
@ -573,16 +553,16 @@ All variables are set in `server/.env` (or passed as environment variables in Do
|
|||
|
||||
### Data & Storage
|
||||
|
||||
| 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) |
|
||||
| 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) |
|
||||
|
||||
### Integration
|
||||
|
||||
|
|
@ -626,37 +606,20 @@ wscat -c "ws://localhost:3001/ws?api_key=<api-key>"
|
|||
|
||||
### Where Data Lives
|
||||
|
||||
| Path | Contents |
|
||||
| ------------------------------------- | ------------------------------------------------------------------ |
|
||||
| `tasks/active/` | Active task markdown files (YAML frontmatter + body) |
|
||||
| `tasks/archive/` | Archived task markdown files |
|
||||
| `.veritas-kanban/` | Internal config, logs, worktrees, agent requests |
|
||||
| `.veritas-kanban/config.json` | Application settings |
|
||||
| `.veritas-kanban/security.json` | JWT secret (if not using `VERITAS_JWT_SECRET` env var) |
|
||||
| `.veritas-kanban/logs/` | Application logs |
|
||||
| `.veritas-kanban/worktrees/` | Task and temporary integration worktree directories |
|
||||
| `.veritas-kanban/worktree-manifests/` | Durable worktree ownership, base, lifecycle, and override evidence |
|
||||
| `.veritas-kanban/agent-requests/` | Pending AI agent requests |
|
||||
| Path | Contents |
|
||||
| --------------------------------- | ------------------------------------------------------ |
|
||||
| `tasks/active/` | Active task markdown files (YAML frontmatter + body) |
|
||||
| `tasks/archive/` | Archived task markdown files |
|
||||
| `.veritas-kanban/` | Internal config, logs, worktrees, agent requests |
|
||||
| `.veritas-kanban/config.json` | Application settings |
|
||||
| `.veritas-kanban/security.json` | JWT secret (if not using `VERITAS_JWT_SECRET` env var) |
|
||||
| `.veritas-kanban/logs/` | Application logs |
|
||||
| `.veritas-kanban/worktrees/` | Git worktree metadata |
|
||||
| `.veritas-kanban/agent-requests/` | Pending AI agent requests |
|
||||
|
||||
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.
|
||||
In Docker, the `DATA_DIR` environment variable maps to `/app/data` by default inside the container.
|
||||
|
||||
**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.
|
||||
**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.
|
||||
|
||||
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:
|
||||
|
||||
|
|
@ -732,7 +695,7 @@ docker compose down
|
|||
docker run --rm \
|
||||
-v kanban-data:/data \
|
||||
-v $(pwd):/backup \
|
||||
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'
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/veritas-backup-20260129.tar.gz -C /data"
|
||||
|
||||
# Restart
|
||||
docker compose up -d
|
||||
|
|
@ -814,15 +777,9 @@ export VERITAS_DATA_DIR=/var/lib/veritas-kanban
|
|||
All services resolve runtime paths through `server/src/utils/paths.ts`, which
|
||||
respects `DATA_DIR` / `VERITAS_DATA_DIR` as the authoritative override (#774).
|
||||
|
||||
**Startup reconciliation:** Current agent launches persist a
|
||||
`run-supervisor/v1` record under the configured runtime data directory or in
|
||||
SQLite. After an unclean stop, startup validates the exact provider, launch,
|
||||
task-envelope, worktree, host, lease, and process/session identity before
|
||||
reattaching. A stale lease has one compare-and-set winner, verified process
|
||||
groups remain stoppable, event replay resumes after the durable cursor, and a
|
||||
terminal result is applied idempotently if the server crashed before task
|
||||
mutation. Unsafe or legacy runs move to `blocked` with a typed recovery reason
|
||||
and operator action instead of being restarted automatically (#781, #853).
|
||||
**Startup reconciliation:** If the server was stopped uncleanly while agents
|
||||
were running, the next startup automatically marks any orphaned `running`
|
||||
agent attempts as `failed` and reverts the owning task to `todo` (#781).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ This guide covers desktop packaging paths for macOS GA artifacts and
|
|||
Linux/Windows preview artifact scaffolding: unsigned PR artifacts, signed
|
||||
release artifacts, update metadata, and smoke testing.
|
||||
|
||||
For v6 GA, macOS is the only supported desktop release target. Linux and
|
||||
For v5 GA, macOS is the only supported desktop release target. Linux and
|
||||
Windows artifacts are unsigned preview artifacts for post-GA validation unless a
|
||||
later release guide explicitly promotes the platform.
|
||||
|
||||
|
|
@ -50,11 +50,8 @@ live repository install into a production-only dependency state.
|
|||
|
||||
## GitHub Workflows
|
||||
|
||||
`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:
|
||||
`Desktop Artifacts` runs on desktop/server/web/shared changes and on manual
|
||||
dispatch. It builds unsigned artifacts on:
|
||||
|
||||
- `macos-15`: DMG, ZIP, blockmap, and update YAML.
|
||||
- `ubuntu-24.04`: x64 AppImage, deb, rpm, blockmap, and update YAML.
|
||||
|
|
@ -62,17 +59,14 @@ workflow builds unsigned artifacts on:
|
|||
|
||||
Unsigned artifact jobs do not require platform signing credentials.
|
||||
|
||||
For v6 GA, the Linux and Windows jobs are preview signals only. They keep
|
||||
For v5 GA, the Linux and Windows jobs are preview signals only. They keep
|
||||
packaging paths and artifact names exercised, but they are not supported release
|
||||
deliverables and must not be linked from stable release notes as install
|
||||
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. 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.
|
||||
and publishes update metadata with the GitHub provider.
|
||||
|
||||
## Homebrew Cask
|
||||
|
||||
|
|
@ -137,13 +131,13 @@ and update policy requirements are promoted into the supported release path.
|
|||
|
||||
## Desktop Support Boundary
|
||||
|
||||
| Platform | v6 GA stance | Validation matrix | Artifact formats | Update stance |
|
||||
| Platform | v5 GA stance | Validation matrix | Artifact formats | Update stance |
|
||||
| -------- | ----------------------------- | ----------------------------------------------- | ---------------------------- | -------------------------------------------------- |
|
||||
| macOS | Supported desktop GA target | macOS 14+ Apple Silicon | signed DMG, ZIP | Supported through signed electron-updater metadata |
|
||||
| Linux | Preview only; not a GA target | Ubuntu 24.04 x64 and Fedora 40+ x64 smoke hosts | unsigned AppImage, deb, rpm | Deferred until Linux release policy is promoted |
|
||||
| Windows | Preview only; not a GA target | Windows 11 23H2+ x64 smoke host | unsigned NSIS installer, ZIP | Blocked until Windows code signing and smoke pass |
|
||||
|
||||
Linux and Windows support is post-GA. Do not mention Linux/Windows as v6 GA
|
||||
Linux and Windows support is post-GA. Do not mention Linux/Windows as v5 GA
|
||||
install targets until the corresponding release artifact has passed the smoke
|
||||
matrix below and the compatibility policy has been updated to promote the
|
||||
platform.
|
||||
|
|
@ -165,9 +159,9 @@ available, downloading, ready, failed, and unsupported states. The menu enables
|
|||
download only when an update is available and install only when an update has
|
||||
downloaded.
|
||||
|
||||
The full v6 channel, staged rollout, version-skew, stale-client, and rollback
|
||||
The full v5 channel, staged rollout, version-skew, stale-client, and rollback
|
||||
policy is tracked in
|
||||
[v6 Compatibility And Release Policy](V6-COMPATIBILITY-AND-RELEASE-POLICY.md).
|
||||
[v5 Compatibility And Release Policy](V5-COMPATIBILITY-AND-RELEASE-POLICY.md).
|
||||
|
||||
## Release Checklist
|
||||
|
||||
|
|
@ -180,34 +174,16 @@ policy is tracked in
|
|||
npm install/download shim.
|
||||
- Run `pnpm desktop:smoke:mac:local` to verify local packaging does not prune
|
||||
root dev tooling.
|
||||
- Run `pnpm desktop:test:readiness` and confirm the bounded readiness helper
|
||||
rejects a stale version and reports a useful timeout.
|
||||
- Run `pnpm desktop:package:mac:unsigned` and inspect artifact names.
|
||||
- Run `pnpm desktop:package:linux:unsigned` on Linux or the
|
||||
`Desktop Artifacts` Linux job and inspect preview artifact names. This is not
|
||||
a v6 GA release gate.
|
||||
a v5 GA release gate.
|
||||
- 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.
|
||||
- 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.
|
||||
not a v5 GA release gate.
|
||||
- Run `Desktop Artifacts` and download the uploaded DMG/ZIP/update metadata.
|
||||
- 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.
|
||||
|
|
@ -219,11 +195,8 @@ policy is tracked in
|
|||
matches the shipped onboarding labels and Maintenance import behavior.
|
||||
- Confirm update check, download, install, failed-download, and rollback paths
|
||||
on the selected channel.
|
||||
- For a Homebrew upgrade, confirm `open -a` followed by
|
||||
`pnpm desktop:wait:ready -- --expected-version <version>` tolerates normal
|
||||
startup delay and proves the packaged server owns `3001`.
|
||||
- Confirm `pnpm validate:release` passes and verifies root/shared/server/web,
|
||||
CLI, MCP, and desktop package versions plus required v6 release docs.
|
||||
CLI, MCP, and desktop package versions plus required v5 release docs.
|
||||
|
||||
## Smoke Tests
|
||||
|
||||
|
|
@ -248,7 +221,7 @@ Signed release artifact:
|
|||
|
||||
Linux preview unsigned artifact:
|
||||
|
||||
These steps generate post-GA readiness evidence only. They are not v6 GA
|
||||
These steps generate post-GA readiness evidence only. They are not v5 GA
|
||||
install instructions.
|
||||
|
||||
1. Download `veritas-kanban-linux-unsigned` from the workflow run.
|
||||
|
|
@ -267,7 +240,7 @@ install instructions.
|
|||
|
||||
Windows preview unsigned artifact:
|
||||
|
||||
These steps generate post-GA readiness evidence only. They are not v6 GA
|
||||
These steps generate post-GA readiness evidence only. They are not v5 GA
|
||||
install instructions.
|
||||
|
||||
1. Download `veritas-kanban-windows-unsigned` from the workflow run.
|
||||
|
|
@ -293,7 +266,7 @@ Rollback:
|
|||
|
||||
## Platform Notes
|
||||
|
||||
Linux and Windows packages are intentionally not v6 GA blockers. The post-GA
|
||||
Linux and Windows packages are intentionally not v5 GA blockers. The post-GA
|
||||
artifact jobs keep artifact naming and update-channel conventions portable, but
|
||||
Windows release and update support stay blocked until code signing and
|
||||
signed-installer smoke coverage are in place. Linux release and updater support
|
||||
|
|
|
|||
|
|
@ -44,13 +44,7 @@ When completing a task that changes user-facing behavior:
|
|||
|
||||
### Freshness Indicators
|
||||
|
||||
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:
|
||||
Each doc should include a freshness header:
|
||||
|
||||
```markdown
|
||||
<!-- doc-freshness: 2026-03-25 | v4.0.0 | @veritas -->
|
||||
|
|
@ -58,32 +52,23 @@ marker when repository reviewers find it useful:
|
|||
|
||||
Format: `date | version | last-updater`
|
||||
|
||||
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.
|
||||
When a doc is older than the current version, it may need review.
|
||||
|
||||
### Last Sweep
|
||||
|
||||
| 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 |
|
||||
| Date | Scope | Agent |
|
||||
| ---------- | ------------------------------------------------------------------- | ------- |
|
||||
| 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
|
||||
- Doc Freshness registry records, with optional source markers where useful
|
||||
- Freshness headers in docs
|
||||
- Agent instructions include "update docs" step
|
||||
|
||||
### Phase 2: Hook-Based
|
||||
|
|
@ -115,15 +100,12 @@ curl -X POST /api/hooks -d '{
|
|||
}'
|
||||
```
|
||||
|
||||
## Repo Rules
|
||||
## Repo Rules (CLAUDE.md Equivalent)
|
||||
|
||||
The root `AGENTS.md` is canonical. `docs/AGENTS-TEMPLATE.md` is the reusable
|
||||
project template, and harness-specific files only supplement the canonical
|
||||
rules. Key rules:
|
||||
VK's `docs/AGENTS-TEMPLATE.md` serves as the agent instruction file. Key rules:
|
||||
|
||||
1. **Always update docs alongside code** — no code-only PRs for user-facing changes
|
||||
2. **Track maintained living docs** — use the Doc Freshness registry; optional
|
||||
source headers are a reviewer aid, not the system of record
|
||||
2. **Use freshness headers** — every doc starts with `<!-- doc-freshness: ... -->`
|
||||
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. Run the task's configured review gate, if any.
|
||||
Implement markdown lessonsLearned field on tasks (UI + API). Include migration + docs. Cross-model review required.
|
||||
```
|
||||
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. Focused tests verify UI + API parity; add independent review when required.
|
||||
4. Cross-model review ensures UI + API parity.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -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 a focused security audit on the repository.
|
||||
**Goal:** Run cross-model audit on repo.
|
||||
|
||||
1. Task -> `type=security`, `project=veritas-kanban`.
|
||||
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.
|
||||
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/*`.
|
||||
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. **Focused review** when the task or configured governance policy requires it.
|
||||
5. **Cross-model review** if code/critical.
|
||||
6. **Lessons learned** field updated for systemic knowledge.
|
||||
|
||||
Use these recipes as seeds for your own automation playbooks.
|
||||
|
|
|
|||
|
|
@ -41,16 +41,15 @@ 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. Optional review:
|
||||
- If the task or governance policy requires independent review, create a
|
||||
review task and assign it to the requested reviewer.
|
||||
6. Review:
|
||||
- Create a cross-model review task.
|
||||
- Assign it to a non-Codex reviewer.
|
||||
|
||||
---
|
||||
|
||||
## 2. Optional Independent Review With Codex
|
||||
## 2. Codex Review Of A Claude-authored PR
|
||||
|
||||
**Goal:** Use Codex as an independent reviewer when a task or governance policy
|
||||
explicitly requires one. This is not a default delivery step.
|
||||
**Goal:** Use Codex as the opposite-model reviewer for a Claude-authored branch.
|
||||
|
||||
1. Keep the original implementation task `in-progress`.
|
||||
2. Trigger a Codex review action:
|
||||
|
|
@ -153,7 +152,7 @@ steps:
|
|||
agent: reviewer
|
||||
depends_on: [implement]
|
||||
input: |
|
||||
Run the configured independent review using docs/SOP-cross-model-code-review.md.
|
||||
Review Codex's implementation using docs/SOP-cross-model-code-review.md.
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
|
|
|
|||
398
docs/FEATURES.md
398
docs/FEATURES.md
|
|
@ -1,10 +1,10 @@
|
|||
# Features
|
||||
|
||||
Complete feature reference for Veritas Kanban, including the v6 release surfaces. Every feature, every API endpoint, every configuration option.
|
||||
Complete feature reference for Veritas Kanban, including the v5 release surfaces. Every feature, every API endpoint, every configuration option.
|
||||
|
||||
For a quick overview, see the [README](../README.md#-what-makes-veritas-kanban-different).
|
||||
For current release screenshots and retained v5 shell captures, see the
|
||||
[v6 Visual Tour](V6-VISUAL-TOUR.md). For troubleshooting, see
|
||||
For current v5 screenshots and GIFs, see the
|
||||
[v5 Visual Tour](V5-VISUAL-TOUR.md). For troubleshooting, see
|
||||
[TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
||||
|
||||
---
|
||||
|
|
@ -27,10 +27,8 @@ For current release screenshots and retained v5 shell captures, see the
|
|||
### AI Agents
|
||||
|
||||
- [Agent Integration](#agent-integration)
|
||||
- [Run-scoped Tools](#run-scoped-tools-v6)
|
||||
- [Team Roster & Capability Routing](#team-roster--capability-routing)
|
||||
- [OpenAI Codex Integration](#openai-codex-integration-v5)
|
||||
- [Claude Code Integration](#claude-code-integration-v6)
|
||||
- [Veritas Cutover & Hermes Support](#veritas-cutover--hermes-support)
|
||||
- [Multi-Agent System](#multi-agent-system)
|
||||
- [Squad Chat](#squad-chat)
|
||||
|
|
@ -82,7 +80,7 @@ For current release screenshots and retained v5 shell captures, see the
|
|||
- [Infrastructure & DevOps](#infrastructure--devops)
|
||||
- [Testing](#testing)
|
||||
- [Accessibility](#accessibility)
|
||||
- [v6 Visual Tour](V6-VISUAL-TOUR.md)
|
||||
- [v5 Visual Tour](V5-VISUAL-TOUR.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -117,10 +115,9 @@ 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 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
|
||||
- **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
|
||||
- **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 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
|
||||
- **Resizable Workbench** — Board Chat and Squad Chat live in a collapsible bottom panel that can be resized vertically for longer chat sessions
|
||||
- **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
|
||||
|
|
@ -256,12 +253,8 @@ Create reusable templates for consistent task creation. Added in v1.6.
|
|||
|
||||
Integrated git workflow from branch creation to merge.
|
||||
|
||||
- **Transactional Git worktrees** — Persist a versioned allocation and ownership lease before creating an isolated task worktree
|
||||
- **Exact remote bases** — Fetch and record the resolved base commit; offline fallback requires a reasoned stale-base acknowledgement
|
||||
- **Worktree status** — See path, branch, exact base, remote freshness, lifecycle state, and cleanup hazards in the Git tab
|
||||
- **Primary-checkout-safe integration** — Merge and push from a dedicated temporary integration worktree without switching or pulling the primary checkout
|
||||
- **Preview-first cleanup** — Block active runs and require audited overrides for dirty, untracked, unpushed, unmerged, or externally held worktrees
|
||||
- **Recoverable lifecycle state** — Keep partial create, rebase, push, and cleanup failures visible in `worktree-manifest/v1`
|
||||
- **Git worktree integration** — Create isolated worktrees per task, tied to dedicated branches
|
||||
- **Worktree status** — See active worktree path, branch, and base branch in the Git tab
|
||||
- **Git selection form** — Configure repository, branch name, and base branch when setting up a worktree
|
||||
- **Diff viewer** — Unified diff view with file tree navigation, hunk-by-hunk display, and line numbers
|
||||
- **File tree** — Collapsible file tree showing changed files with add/modify/delete indicators
|
||||
|
|
@ -284,11 +277,10 @@ First-class support for autonomous coding agents.
|
|||

|
||||
|
||||
- **Agent orchestration** — Start, stop, and monitor AI agents on code tasks from the UI or API
|
||||
- **Multi-agent support** — Ships with Codex CLI, Codex SDK, Codex app-server, Codex Cloud, Hermes Agent, Claude Code, a generic ACP stdio provider, Amp, Copilot, Gemini, Ollama Local, Ollama Cloud, LM Studio Local, and Veritas profiles; add completely custom agents via Settings → Agents
|
||||
- **Multi-agent support** — Ships with Codex, Codex SDK, Codex Cloud, Hermes Agent, Claude Code, Amp, Copilot, Gemini, Ollama Local, Ollama Cloud, LM Studio Local, and Veritas profiles; add completely custom agents via Settings → Agents
|
||||
- **Agent CRUD management** — Full Add/Edit/Remove for agents in Settings → Agents; add agent form with name, type slug (auto-generated), command, and args; inline edit via pencil icon; remove via trash icon with confirmation (blocked for the default agent); `AgentType` accepts any string slug, not just built-in names
|
||||
- **Agent request files** — Server writes structured requests to `.veritas-kanban/agent-requests/` for agent pickup
|
||||
- **Provider-owned task-envelope transports** — OpenClaw, Codex CLI, Codex SDK, Codex app-server, Claude Code, ACP stdio, and Hermes each render the immutable task contract through an adapter-owned request with explicit commit policy, bounded attributed profile/checkpoint context, workspace baseline, verification gates, and completion evidence requirements
|
||||
- **Provider-specific completion posture** — OpenClaw receives an attempt-bound completion callback; Codex CLI, Codex SDK, Codex app-server, Claude Code, and Hermes return terminal output through harness-supervised process or stream capture
|
||||
- **Completion callbacks** — Agents call the completion endpoint with success/failure status and summary
|
||||
- **Multiple attempts** — Retry tasks with different agents; full attempt history preserved with status (pending, running, complete, failed)
|
||||
- **Attempt history viewer** — Browse past attempts with agent name, status, and log output
|
||||
- **Time tracking** — Start/stop timer or add manual time entries per task; running timer display with live elapsed counter
|
||||
|
|
@ -296,78 +288,15 @@ First-class support for autonomous coding agents.
|
|||
- **Agent status indicator** — Header-level indicator showing global agent state (idle, working, sub-agent mode with count)
|
||||
- **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,
|
||||
immutable per-attempt catalogs, required and optional server posture,
|
||||
allow/deny/approval decisions, bounded supervised invocation, and native
|
||||
catalog injection for Codex app-server and Claude Code
|
||||
- **Send message to agent** — Send text messages to running agents
|
||||
- **Optional OpenClaw support** — Built-in integration with [OpenClaw](https://github.com/openclaw/openclaw) (formerly Clawdbot/Moltbot) via gateway URL when you want OpenClaw to execute or wake agents
|
||||
- **HermesAgent operating support** — v4.3 documents HermesAgent/Hermes Gateway as the active control plane, with Veritas tracking task truth, QA evidence, and GitHub delivery state
|
||||
- **OpenAI Codex support** — Local CLI attempts, SDK sessions, a pinned app-server v2 adapter, GitHub-native Codex Cloud delegation, workflow steps, review actions, Settings health checks, MCP setup, and fresh-install default routing
|
||||
- **Claude Code support** — First-class v2.1.218 bare-mode process adapter with
|
||||
static sandbox-derived permissions, bounded JSONL event ingestion, session
|
||||
persistence, usage/cost telemetry, artifact discovery, deterministic health
|
||||
diagnostics, and fail-closed unsupported lifecycle controls
|
||||
- **ACP provider support** — Any conforming stable ACP v1 stdio agent can run as
|
||||
an explicit provider with negotiated lifecycle capabilities, causal message,
|
||||
plan, and tool events, durable approval brokering, supervised cancellation,
|
||||
immutable launch evidence, and fail-closed run-scoped MCP injection
|
||||
- **ACP server view** — `vk acp serve --stdio` exposes a task-bound,
|
||||
provider-neutral ACP v1 view over Veritas conversation lifecycle, causal
|
||||
replay, tool events, and durable approvals without creating a parallel
|
||||
session store; disconnect leaves the supervised run active
|
||||
- **Buzz Agent ACP profile** — A disabled-by-default `buzz-agent` runtime uses
|
||||
the generic ACP provider, pins Buzz v0.4.24 compatibility evidence, exposes
|
||||
only its tested configuration and boot-authentication environment keys, and
|
||||
reports no persistent session loading or network MCP instead of inventing
|
||||
capabilities; selected run tools arrive only through the opaque
|
||||
system-owned `veritas-run` bridge
|
||||
- **Buzz composed compatibility gate** — One credential-free command verifies
|
||||
pinned relay diagnostics, communication/replay, generic ACP, run-scoped MCP,
|
||||
persona/team import, workflow-trigger, secret-safety, and matrix evidence
|
||||
without treating optional live credentials as certification authority
|
||||
- **GitHub Copilot CLI ACP profile** — A disabled-by-default `copilot` runtime
|
||||
uses the generic ACP provider with a system-owned stdio/public-preview
|
||||
baseline, exact v1.0.74 handshake evidence, bounded restrictive process
|
||||
policy, provider-managed authentication, and explicit source-provenance
|
||||
limitations
|
||||
- **OpenAI Codex support** — Local CLI attempts, SDK sessions, GitHub-native Codex Cloud delegation, workflow steps, review actions, Settings health checks, MCP setup, and fresh-install default routing
|
||||
- **Local LLM provider profiles** — Ollama Local, Ollama Cloud, and LM Studio Local profiles can be enabled, health-checked, and targeted by routing rules in the web app or macOS app
|
||||
- **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
|
||||
provider/model/profile/policy/sandbox combinations across repeated trials,
|
||||
retain immutable launch/runtime/event evidence references, assert governed
|
||||
outcomes, and fail CI or promotion on versioned pass-rate, variance, latency,
|
||||
token, or cost regression
|
||||
- **Task-envelope transports** — Provider-owned renderers for OpenClaw, Codex CLI, Codex SDK, Codex app-server, Claude Code, ACP stdio, and Hermes bind the exact task-envelope digest and commit policy to the launched request; the rendered request is fingerprinted in the run launch manifest and mismatched provider/adapter identities fail closed
|
||||
- **Sandbox policy presets** — Built-in and custom presets control filesystem scope, network egress, environment passthrough, and credential brokering for agent profiles, workflow agents, and per-run overrides
|
||||
- **Agent budget enforcement** — Workspace, agent, workflow, workflow-agent, and per-run budgets can cap tokens, provider cost, tool calls, runtime, retries, and workflow fan-out with warning, approval, downgrade, pause, or cancel actions
|
||||
- **Platform-agnostic REST API** — Any platform that can make HTTP calls can drive the full agent lifecycle
|
||||
|
|
@ -377,53 +306,9 @@ First-class support for autonomous coding agents.
|
|||
|
||||
---
|
||||
|
||||
## Run-scoped Tools (v6)
|
||||
## OpenAI Codex Integration (v5)
|
||||
|
||||
Veritas stores validated `tool-server-definition/v1` records for stdio and
|
||||
Streamable HTTP MCP servers. Discovery results are cached by the exact
|
||||
definition digest, including the declared server version. Each launch selects
|
||||
servers by profile, applies definition-level tool restrictions, and
|
||||
persists an immutable `run-tool-catalog/v1` digest in the launch manifest.
|
||||
|
||||
- Required server failures block launch; optional failures produce visible,
|
||||
audited degraded entries.
|
||||
- Codex app-server receives only the run catalog through thread-scoped
|
||||
`mcp_servers`; Claude Code receives it through `--strict-mcp-config` and an
|
||||
exact MCP `--allowedTools` list.
|
||||
- ACP stdio sessions receive a native catalog only when every discovered tool
|
||||
is allowed. Bridge-only profiles such as `buzz-agent` receive exactly one
|
||||
system-owned `veritas-run` descriptor so partial and approval-backed catalogs
|
||||
stay mediated; other partial native catalogs fail closed.
|
||||
- Profile-wide named-tool policies still fail closed when a provider cannot
|
||||
constrain its built-in tools alongside MCP; prompt instructions do not count
|
||||
as enforcement.
|
||||
- Denied and approval-required tools are not exposed in native provider
|
||||
configuration. Approval-required calls use Veritas REST, CLI, or MCP
|
||||
mediation and bind approval to the exact arguments and catalog digest.
|
||||
- Stdio processes run without a shell and with a minimal environment.
|
||||
Streamable HTTP calls use bounded JSON/SSE responses and run-scoped session
|
||||
identity.
|
||||
- Arguments, schemas, results, and errors are bounded. Causal tool events are
|
||||
redacted and deduplicated by the caller's stable operation ID.
|
||||
- Credential-bound entries compile only with enabled, scope-compatible broker
|
||||
definitions. Their definition/scope digests and safe target names enter the
|
||||
immutable catalog, discovery receives no source values, and native provider
|
||||
injection omits them. Mediated calls issue exact-action leases using the
|
||||
server-owned launch manifest, deliver values only inside one-shot downstream
|
||||
sessions, and reject replay, drift, approval mismatch, source failure, or
|
||||
credential-bearing results.
|
||||
- Credential-bound runs inject the same narrow `veritas-run` MCP bridge into
|
||||
Codex CLI/SDK, Codex app-server, Claude Code, and ACP stdio. The bridge
|
||||
carries an opaque run handle, never a task credential. Hermes and OpenClaw
|
||||
fail closed until their certified transports can enforce this contract.
|
||||
|
||||
See [Tool Control Plane v1](architecture/TOOL-CONTROL-PLANE-V1.md).
|
||||
|
||||
---
|
||||
|
||||
## OpenAI Codex Integration (v6)
|
||||
|
||||
v6 uses OpenAI Codex as the default fresh-install agent profile and supports local `codex exec` attempts, SDK-backed Codex sessions, the Codex app-server lifecycle adapter, GitHub-native Codex Cloud delegation, Codex-backed workflow-engine steps, Codex review actions, Settings health checks, and MCP setup through the existing Veritas task lifecycle.
|
||||
v5 uses OpenAI Codex as the default fresh-install agent profile and supports local `codex exec` attempts, SDK-backed Codex sessions, GitHub-native Codex Cloud delegation, Codex-backed workflow-engine steps, Codex review actions, Settings health checks, and MCP setup through the existing Veritas task lifecycle.
|
||||
|
||||
Implemented:
|
||||
|
||||
|
|
@ -431,11 +316,10 @@ Implemented:
|
|||
- **Codex agent defaults** — Fresh installs enable the OpenAI Codex CLI profile by default with `codex exec --sandbox workspace-write --json`; existing configs keep their selected default agent.
|
||||
- **Ollama and LM Studio profiles** — Adds disabled-by-default Ollama Local, Ollama Cloud, and LM Studio Local profiles with provider metadata and health probes.
|
||||
- **Codex SDK provider** — Uses `@openai/codex-sdk` to start durable local Codex threads, stream SDK events into attempt logs, persist `threadId` on attempts, and emit token telemetry from completed turns.
|
||||
- **Codex app-server provider** — Runs the exact v0.145.0 JSON-RPC v2 app-server over strict stdio, validates the pinned generated schemas, persists task-bound thread identity, streams item/usage/completion events, supports cooperative interruption, brokers command, file, permission, tool-question, and elicitation requests through exact method-specific response contracts, and injects only the immutable run-scoped MCP catalog. Inherited MCP and remote-control surfaces remain disabled.
|
||||
- **Codex Cloud delegation** — Creates scoped `@codex` GitHub issue/PR prompts, records cloud attempt metadata, and links the GitHub artifact back to the Veritas task.
|
||||
- **Workflow Codex steps** — Executes workflow-engine agent steps through Codex SDK streaming, writes step outputs, and stores Codex thread IDs in workflow session context.
|
||||
- **Codex review actions** — Reviews task branch diffs in read-only Codex SDK mode, maps structured findings to Veritas review comments, and stores review decisions.
|
||||
- **Codex Settings health** — Checks Codex CLI install/auth state, SDK availability, and Codex CLI/SDK/app-server/Cloud profile readiness from Settings.
|
||||
- **Codex Settings health** — Checks Codex CLI install/auth state, SDK availability, and Codex CLI/SDK/Cloud profile readiness from Settings.
|
||||
- **Config migration** — Existing configs receive the missing built-in Codex agent without overwriting customized agents.
|
||||
- **MCP setup guidance** — Documents `codex mcp add veritas-kanban` for local and API-key-backed deployments so Codex can use typed Veritas tools.
|
||||
|
||||
|
|
@ -447,46 +331,6 @@ Documentation:
|
|||
|
||||
---
|
||||
|
||||
## Claude Code Integration (v6)
|
||||
|
||||
The `claude-code` provider runs Claude Code v2.1.218 directly in the assigned
|
||||
worktree with `--bare`, `--print`, and `stream-json`. Veritas owns the task
|
||||
envelope, static permissions, environment allowlist, process lifecycle, causal
|
||||
event journal, terminal result, and completion normalization.
|
||||
|
||||
Implemented:
|
||||
|
||||
- **Bare-mode launch** — No shell, inherited settings, plugins, or MCP config;
|
||||
only the Veritas-owned run catalog is added. Chrome integration, slash
|
||||
commands, and permission bypass remain disabled.
|
||||
- **Static permissions** — Read tools are always available; writes, Bash, and
|
||||
web tools are derived from the effective filesystem and network sandbox.
|
||||
- **Explicit authentication** — OAuth/keychain status is diagnostic only;
|
||||
launch requires an allowlisted environment credential or supported cloud
|
||||
selector.
|
||||
- **Full stream capture** — Partial text/thinking, tool results, hooks,
|
||||
subagents, retries, usage/cost, artifacts, and terminal results map into
|
||||
`run-event/v1`.
|
||||
- **Drain-safe completion** — Veritas waits for queued output and parses a final
|
||||
unterminated record after process close. A successful exit without a
|
||||
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 16,
|
||||
authentication posture, and safe agent-discovery summary determine support
|
||||
status.
|
||||
- **Capability truth** — The shared approval broker is available, but this
|
||||
Claude adapter still uses static `dontAsk` permissions and reports
|
||||
interactive approval and elicitation as unsupported. Exact-session resume
|
||||
and native fork are supported; steering remains unsupported. Run-scoped MCP
|
||||
injection is supported, while approval-required tools stay on the mediated
|
||||
Veritas call path.
|
||||
|
||||
See [Agent Providers](AGENT-PROVIDERS.md#claude-code-v21218) for setup,
|
||||
credentials, arguments, permissions, and limitations.
|
||||
|
||||
---
|
||||
|
||||
## Team Roster & Capability Routing
|
||||
|
||||
Workspace-level routing metadata for agent teams and delegated work intake. Added in v5.2.
|
||||
|
|
@ -582,7 +426,7 @@ Workspace-scoped live collaboration for active task runs. Added in v5.1.
|
|||
|
||||
- **View-only shares** — Create stable `/runs/shared/:id` links from task detail so permitted workspace members can watch live output, tool-call/status events, artifacts, and share history without refresh.
|
||||
- **Co-drive access** — Upgrade a share to edit access or revoke it from task detail. Editors can send messages into the active run, and the server records the editor as the actor instead of the original operator.
|
||||
- **Mobile-safe approvals** — Paired mobile/PWA clients can respond only when the exact provider request is marked mobile-safe and its action class is allowlisted on the share; unsafe, stale, or changed requests fail closed at the API layer.
|
||||
- **Mobile-safe approvals** — Paired mobile/PWA clients can respond only to approval classes marked safe for that share; unsafe classes fail closed at the API layer.
|
||||
- **Fork isolation** — Fork shares create a new linked task with redacted parent context and run excerpt. The fork does not inherit worktrees, thread IDs, credentials, or other local-only handles, and it does not change parent run state.
|
||||
- **Live delivery** — The `run-sessions` WebSocket channel fans out share, message, approval, revoke, and fork events to authorized same-workspace clients.
|
||||
|
||||
|
|
@ -605,15 +449,6 @@ 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
|
||||
|
||||
|
|
@ -634,11 +469,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
|
||||
- **Tracked metadata** — Registry records store review dates, owners, paths, thresholds, and tags without rewriting source documents
|
||||
- **Freshness headers** — YAML frontmatter with `fresh-days`, `owner`, `last-verified` fields
|
||||
- **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 registry review → scheduled checks → CI integration
|
||||
- **3-phase automation** — Manual → scheduled checks → CI integration
|
||||
- **Inspired by** @mvoutov's BoardKit Orchestrator ("stale docs = hallucinating AI")
|
||||
|
||||
---
|
||||
|
|
@ -652,7 +487,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 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
|
||||
- **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
|
||||
- **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
|
||||
|
|
@ -664,31 +499,21 @@ Real-time agent-to-agent communication channel for multi-agent collaboration. Sh
|
|||
- **Squad Chat Webhook** — Optional outbound delivery for chat messages; supports generic HTTP and OpenClaw Direct modes
|
||||
- **OpenClaw Direct gateway wake** — Optional real-time Squad Chat events pushed to OpenClaw gateway for agent orchestration
|
||||
- **Human reply adapters** — Configure Teams reply posture, run health checks and test sends, store external thread mappings, and ingest audited human replies back into the correct Squad Chat thread
|
||||
- **Buzz channel bridge** — Map a Buzz community channel to Squad Chat, publish and ingest signed roots/replies exactly once, retain source author/timestamp/deep links, resume through a durable cursor with overlap dedupe, and reconcile ambiguous sends before retry
|
||||
- **Buzz workflow trigger** — Bind an allowlisted mapped-channel root message to one workflow with bounded author/content predicates, a durable causal key, provider-neutral pre-dispatch hooks, replay/echo suppression, and restart reconciliation
|
||||
- **Buzz persona/team import** — List signature-verified NIP-33 persona and team heads, preview mapped/source-only/ignored/rejected fields, resolve collisions explicitly, and create, link, or refresh disabled profile/roster materializations with provenance and optimistic local revisions
|
||||
- **Searchable history** — Browse and search past squad chat messages
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| ---------------------------------------------------------------------------------- | -------- | ------------------------------------------------- |
|
||||
| `/api/chat/squad` | POST | Send a squad chat message |
|
||||
| `/api/chat/squad` | GET | Retrieve squad chat history |
|
||||
| `/api/chat/squad/search` | GET | Search redacted snippets |
|
||||
| `/api/chat/squad/unread` | GET | Get actor-scoped unread state |
|
||||
| `/api/chat/squad/read` | POST | Mark messages read for an actor |
|
||||
| `/api/chat/squad/:messageId/thread` | GET | Read a compact thread |
|
||||
| `/api/chat/squad/:messageId/pin` | POST | Pin/unpin or mark/unmark a decision |
|
||||
| `/api/chat/squad/:messageId/react` | POST | Add a lightweight reaction or acknowledgement |
|
||||
| `/api/integrations/communication/adapters/:adapterId/replies` | POST | Ingest an external human reply into Squad Chat |
|
||||
| `/api/integrations/communication/adapters/:adapterId/send` | POST | Publish a mapped Teams/Buzz communication message |
|
||||
| `/api/integrations/communication/adapters/:adapterId/buzz/channels/:channelId` | PUT | Map a Buzz channel to Squad Chat |
|
||||
| `/api/integrations/communication/adapters/:adapterId/buzz/workflow-triggers` | GET/POST | List or create root-message workflow rules |
|
||||
| `/api/integrations/communication/adapters/:adapterId/buzz/workflow-trigger-audits` | GET | Read bounded trigger disposition history |
|
||||
| `/api/integrations/communication/adapters/:adapterId/buzz/definitions` | GET | List validated Buzz persona/team heads |
|
||||
| `/api/integrations/communication/adapters/:adapterId/buzz/definitions/preview` | POST | Preview field mappings, diffs, and collisions |
|
||||
| `/api/integrations/communication/adapters/:adapterId/buzz/definitions/import` | POST | Explicitly create, link, refresh, or skip import |
|
||||
| Endpoint | Method | Description |
|
||||
| ------------------------------------------------------------- | ------ | ---------------------------------------------- |
|
||||
| `/api/chat/squad` | POST | Send a squad chat message |
|
||||
| `/api/chat/squad` | GET | Retrieve squad chat history |
|
||||
| `/api/chat/squad/search` | GET | Search redacted snippets |
|
||||
| `/api/chat/squad/unread` | GET | Get actor-scoped unread state |
|
||||
| `/api/chat/squad/read` | POST | Mark messages read for an actor |
|
||||
| `/api/chat/squad/:messageId/thread` | GET | Read a compact thread |
|
||||
| `/api/chat/squad/:messageId/pin` | POST | Pin/unpin or mark/unmark a decision |
|
||||
| `/api/chat/squad/:messageId/react` | POST | Add a lightweight reaction or acknowledgement |
|
||||
| `/api/integrations/communication/adapters/:adapterId/replies` | POST | Ingest an external human reply into Squad Chat |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -989,16 +814,6 @@ 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
|
||||
|
||||
|
|
@ -1115,15 +930,8 @@ All three types are backward-compatible — substring matching was the original
|
|||
|
||||
Every workflow run persists its state to disk, enabling:
|
||||
|
||||
- **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
|
||||
- **Server restart recovery** — Runs can resume from last checkpoint
|
||||
- **Retry with exponential backoff** — Configurable `retry_delay_ms` prevents rapid retry loops
|
||||
- **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
|
||||
|
|
@ -1180,55 +988,15 @@ 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 and denied hosts, methods and paths,
|
||||
private/loopback/metadata protection, scoped approval eligibility, environment
|
||||
passthrough keys, credential mode, and broker references.
|
||||
- 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.
|
||||
- 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
|
||||
TTL/use-count enforcement, and terminal-run reconciliation. Required
|
||||
brokered mode rejects advisory or externally delegated capability evidence.
|
||||
- Broker leases remain internal and are consumed only through a reviewed
|
||||
non-bypassable boundary. The current system-owned `veritas-run` bridge
|
||||
mediates exact catalog actions for Codex CLI/SDK, Codex app-server, Claude
|
||||
Code, and ACP stdio; Hermes and OpenClaw fail closed for credential-bound
|
||||
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, ACP stdio harnesses, Hermes, and OpenClaw
|
||||
execution behavior. Gateway capability evidence is invalidated by provider
|
||||
runtime probe revision 16.
|
||||
- Provider capability checks currently distinguish Codex CLI, Codex SDK, Hermes, and OpenClaw execution behavior.
|
||||
|
||||
### Session Isolation
|
||||
|
||||
|
|
@ -1443,8 +1211,6 @@ Notification and broadcast features provide local visibility and optional delive
|
|||
- **Broadcast messages** — Durable system-wide messages at `/api/broadcasts` with `info`, `action-required`, and `urgent` priorities
|
||||
- **External delivery boundary** — Local notifications, broadcasts, and Squad Chat can work while external webhook delivery is disabled
|
||||
- **Human reply adapter health** — Settings -> Notifications shows Teams reply posture, redacted webhook state, recent delivery audit, test send, and disconnect controls
|
||||
- **Buzz communication adapter** — Reference-only relay setup verifies community identity, NIP-98 authentication, membership, and read capability before enabling signed root/reply delivery, supervised subscriptions, durable cursor replay, loop prevention, and delivery-unknown reconciliation
|
||||
- **Buzz public definitions** — One-way, operator-confirmed persona/team materialization keeps source preferences as metadata, requires same-author team references, creates disabled local objects, preserves local-only fields on refresh, and never launches a process or writes back
|
||||
|
||||
### API Endpoints
|
||||
|
||||
|
|
@ -1681,7 +1447,7 @@ Generate daily standup summary reports via API or CLI.
|
|||
|
||||
Real-time project metrics and telemetry.
|
||||
|
||||
Current v6 visual references live in [v6 Visual Tour](V6-VISUAL-TOUR.md).
|
||||
Current v5 visual references live in [v5 Visual Tour](V5-VISUAL-TOUR.md).
|
||||
|
||||
### Dashboard Widgets
|
||||
|
||||
|
|
@ -1753,10 +1519,6 @@ 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
|
||||
|
||||
|
|
@ -1766,7 +1528,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`, and metadata-only `network.egress` decisions for tracking execution and network policy outcomes
|
||||
- **Event types** — `run.started`, `run.completed`, `run.tokens` for tracking agent execution lifecycle
|
||||
- **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
|
||||
|
|
@ -1852,26 +1614,14 @@ 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 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) |
|
||||
| Command | Description |
|
||||
| ----------------------------------------------------------------------------- | --------------------------------------------------- |
|
||||
| `vk start <id>` | Start an agent on a code task (`--agent` to choose) |
|
||||
| `vk stop <id>` | Stop a running agent |
|
||||
| `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
|
||||
|
||||
|
|
@ -1946,7 +1696,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.). 42 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.). 36 tools across task management, agent orchestration, automation, notifications, summaries, sprint management, comments, and projects.
|
||||
|
||||
### Tools
|
||||
|
||||
|
|
@ -1960,7 +1710,6 @@ 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 |
|
||||
|
|
@ -2032,28 +1781,6 @@ 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
|
||||
|
|
@ -2206,7 +1933,6 @@ 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) |
|
||||
|
|
@ -2343,13 +2069,13 @@ TRUST_PROXY=true
|
|||
|
||||
## Storage & Architecture
|
||||
|
||||
Deep storage modules decouple business logic from filesystem and SQLite details.
|
||||
Abstract storage layer that decouples business logic from the filesystem.
|
||||
|
||||
- **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.
|
||||
- **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
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -2371,10 +2097,9 @@ 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
|
||||
- **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
|
||||
- **Pipeline jobs** — Lint and warning budget, type check, workspace unit tests, production build, and security audit
|
||||
- **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
|
||||
- **Release validation** — `pnpm validate:release` checks root/shared/server/web/CLI/MCP/desktop versions, required v5 release docs, built artifacts, and optional GitHub tag/release state
|
||||
- **pnpm caching** — Dependency cache for faster CI runs
|
||||
|
||||
### Development
|
||||
|
|
@ -2398,14 +2123,12 @@ Production-ready deployment and development tooling.
|
|||
|
||||
## Testing
|
||||
|
||||
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.
|
||||
Multi-layer testing strategy.
|
||||
|
||||
### Unit Tests (Vitest)
|
||||
|
||||
- **Workspace coverage** — Server, web, CLI, MCP, shared contracts, and desktop packages are included in the canonical release gate.
|
||||
- **Server coverage includes:**
|
||||
- **119 test files** · **1,699 tests passing** across server and frontend
|
||||
- **Server (105 files, 1,570 tests):**
|
||||
- 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)
|
||||
|
|
@ -2414,7 +2137,7 @@ proof.
|
|||
- 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 coverage includes:**
|
||||
- **Frontend (14 files, 129 tests):**
|
||||
- API client helpers and task operations
|
||||
- Custom hooks: useWebSocket, useKeyboard (keyboard shortcuts)
|
||||
- Components: KanbanBoard, TaskCard, ErrorBoundary, AgentStatusIndicator, WebSocketIndicator
|
||||
|
|
@ -2423,8 +2146,9 @@ proof.
|
|||
|
||||
### End-to-End Tests (Playwright)
|
||||
|
||||
- **Chromium and WebKit projects** cover critical user flows at declared QA and release milestones.
|
||||
- **Test suites include:**
|
||||
- **7 spec files** covering critical user flows
|
||||
- **19/19 tests passing**
|
||||
- **Test suites:**
|
||||
- Health check
|
||||
- Settings management
|
||||
- Task creation
|
||||
|
|
@ -2525,9 +2249,7 @@ Define scoring profiles with weighted criteria and evaluate agent outputs agains
|
|||
|
||||
**Key capabilities:**
|
||||
|
||||
- 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
|
||||
- Four scorer types: `RegexMatch`, `KeywordContains`, `NumericRange`, `CustomExpression`
|
||||
- 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.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+**. |
|
||||
| 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. |
|
||||
| (Optional) Docker | `docker --version` | Needed only if you prefer containers. |
|
||||
|
||||
That's it. No database, no extra services.
|
||||
|
|
@ -190,12 +190,8 @@ This section is optional. Agents interact through HTTP + WebSocket; nothing is h
|
|||
```
|
||||
7. **Agent workflow** (example prompt to an agent runner):
|
||||
```
|
||||
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.
|
||||
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.
|
||||
```
|
||||
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
|
||||
|
|
@ -253,7 +249,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 # Optional independent review
|
||||
├── cross-model-review.md # Claude ↔ GPT review gate
|
||||
├── feature-development.md # E2E feature implementation
|
||||
├── bug-triage.md # Investigation and fix
|
||||
├── research-report.md # Deep research deliverable
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
# Harness Compatibility
|
||||
|
||||
Veritas publishes one reviewed compatibility record for Buzz, Grok Build,
|
||||
OpenAI Codex app-server, Claude Code, and GitHub Copilot CLI. The canonical
|
||||
machine-readable form is:
|
||||
|
||||
```text
|
||||
GET /api/config/harness-compatibility
|
||||
```
|
||||
|
||||
Settings -> Agents and `vk doctor --json` consume that record. Run telemetry
|
||||
stores the same profile capability digest beside the existing support tier,
|
||||
provider version/build, runtime-manifest digest, and failure class.
|
||||
|
||||
## Reviewed matrix
|
||||
|
||||
| Harness | Profile | Tested build | Transport | Source availability | Important limitation |
|
||||
| ------------------ | ------------------------- | ----------------------------------------------------------------------------------- | ---------------------- | ---------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| Buzz | `buzz-agent` | Buzz v0.4.24, commit `710ed9fff57878a1d69f809b80a6ee0416c53fc4`; `buzz-agent 0.1.0` | ACP v1 stdio | Open source | Task execution uses `buzz-agent`; relay, identity, community, and workflow checks remain separate. |
|
||||
| Grok Build | `grok-build` | v0.2.111, build `94172f2aa4e5` | ACP v1 stdio | Partial source lineage | The released artifact self-reports alpha and is not fully traceable to the public source tree. |
|
||||
| OpenAI Codex | `openai-codex-app-server` | `codex-cli 0.145.0` | app-server JSON-RPC v2 | Open source | Experimental methods remain excluded until pinned schemas and behavior are reviewed. |
|
||||
| Claude Code | `claude-code` | `2.1.218 (Claude Code)` | stream-json process | Partial source | The complete CLI implementation is not public; some host enforcement remains provider-dependent. |
|
||||
| GitHub Copilot CLI | `github-copilot-cli` | v1.0.74, commit `2b809c84e87dbcc88f897cb4f3fb97c43b77af95` | ACP v1 stdio | Partial source | ACP is public preview and provider-managed authentication has no non-consuming status probe. |
|
||||
|
||||
The API is authoritative for the full capability list, reviewed evidence URLs,
|
||||
fixture paths, platform coverage, limitations, live readiness, and matrix
|
||||
digest. This table is an operator summary, not a substitute for current probe
|
||||
evidence.
|
||||
|
||||
## Support tiers
|
||||
|
||||
| Tier | Definition |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `certified` | Installed build, configuration, runtime manifest, probe revision, and deterministic fixtures match passing evidence. |
|
||||
| `configured` | The executable adapter can dispatch, but current deterministic certification evidence is absent. |
|
||||
| `detected` | The executable is installed, but the profile is disabled. |
|
||||
| `degraded` | A readiness, compatibility, policy, or certification check failed. |
|
||||
| `unsupported` | The platform or configured provider has no safe executable adapter for the profile. |
|
||||
|
||||
These definitions are emitted in the matrix response. CLI, API, web, and
|
||||
telemetry must not maintain provider-specific alternatives.
|
||||
|
||||
## Certification and invalidation
|
||||
|
||||
Each reviewed profile includes a deterministic fixture set, fixture revision,
|
||||
capability digest, evidence paths, and current status. Certification is
|
||||
invalidated by any change to:
|
||||
|
||||
- provider version or build;
|
||||
- profile configuration digest;
|
||||
- runtime probe revision;
|
||||
- transport protocol version;
|
||||
- capability digest; or
|
||||
- fixture revision.
|
||||
|
||||
Credential-gated smoke evidence is supplemental only. It can add exact-build
|
||||
runtime evidence, but it cannot replace or overwrite a deterministic failure.
|
||||
Raw observations retain launch-manifest, runtime-manifest, task, attempt, and
|
||||
event references through `harness-conformance-result/v1`.
|
||||
|
||||
Buzz additionally has a composed credential-free gate:
|
||||
|
||||
```bash
|
||||
pnpm test:buzz:compatibility
|
||||
```
|
||||
|
||||
Its matrix record names the relay compatibility, communication/replay, ACP,
|
||||
run-scoped MCP, persona/team import, and workflow-trigger fixtures. Live Buzz
|
||||
smoke is supplemental and never replaces a deterministic failure. See the
|
||||
[Buzz integration guide](BUZZ-INTEGRATION.md#credential-free-release-gate).
|
||||
|
||||
## Operating each harness
|
||||
|
||||
Detailed installation, authentication, configuration, permissions, MCP,
|
||||
worktree, upgrade, degraded-state, and troubleshooting guidance is maintained
|
||||
with each provider:
|
||||
|
||||
- [Buzz Agent ACP](AGENT-PROVIDERS.md#buzz-agent-acp)
|
||||
- [Grok Build ACP](AGENT-PROVIDERS.md#grok-build-acp)
|
||||
- [OpenAI Codex app-server](AGENT-PROVIDERS.md#openai-codex-app-server-v01450)
|
||||
- [Claude Code](AGENT-PROVIDERS.md#claude-code-v21218)
|
||||
- [GitHub Copilot CLI ACP](AGENT-PROVIDERS.md#github-copilot-cli-acp-public-preview)
|
||||
|
||||
The common operating contract is:
|
||||
|
||||
1. Install the exact tested build.
|
||||
2. Authenticate through the provider's login or an allowlisted boot credential.
|
||||
3. Enable the built-in profile without adding provider-owned launch bypasses.
|
||||
4. Assign a Veritas sandbox, permission, and approval policy.
|
||||
5. Run `vk doctor --json`; fix any degraded or unsupported evidence.
|
||||
6. Re-run deterministic certification after an upgrade or evidence revision.
|
||||
|
||||
Veritas launches all five harnesses in the assigned task worktree. MCP access is
|
||||
limited to the immutable task catalog plus the system-owned `veritas-run`
|
||||
bridge. Unsafe launch flags, unexpected versions, stale certification, and
|
||||
adapter/profile mismatches fail closed before an attempt is created.
|
||||
|
|
@ -34,10 +34,7 @@ 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, 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.
|
||||
lifecycle policy metadata, and work-product preview metadata.
|
||||
- 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,23 +1,18 @@
|
|||
# SOP: Agent Task Workflow (Create → Work → Complete)
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
> 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).
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Roles
|
||||
|
||||
| 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. |
|
||||
| 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). |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -30,7 +25,7 @@ summaries, and telemetry.
|
|||
| 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 | Run an independent or cross-model review when the task or governance policy requires it. | Conditional |
|
||||
| 5. Review | Trigger cross-model review if code touched or risk level ≥ medium. | ✅ for code |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -198,8 +193,7 @@ 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 the task or configured governance policy requires independent review,
|
||||
queue the review before marking done.
|
||||
5. If you touched code, queue cross-model review task before marking done.
|
||||
```
|
||||
|
||||
Store this under `prompt-registry/agent-task-workflow.md` so every agent run is consistent.
|
||||
|
|
@ -246,11 +240,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, and keep the assigned reviewer informed. |
|
||||
| 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. |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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 an independent review when the task or governance policy requires one. |
|
||||
| **Reviewer Agent** | Performs cross-model review when Codex authored code or reviewed another agent. |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -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 | 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 |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -95,8 +95,7 @@ redacted governance trace.
|
|||
- `error`
|
||||
6. Append human-readable attempt logs.
|
||||
7. Preserve final response as the completion summary.
|
||||
8. Let Veritas project lifecycle telemetry and provider-reported token usage;
|
||||
do not emit duplicate events from the managed Codex run.
|
||||
8. Emit telemetry and token usage when available.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -215,23 +214,25 @@ codex mcp add openaiDeveloperDocs --url https://developers.openai.com/mcp
|
|||
|
||||
---
|
||||
|
||||
## AGENTS.md managed-run snippet
|
||||
## AGENTS.md Codex Snippet
|
||||
|
||||
Use the harness-neutral managed-run block from
|
||||
[AGENTS-TEMPLATE.md](AGENTS-TEMPLATE.md). Codex does not need a separate VK
|
||||
lifecycle protocol:
|
||||
Add this to a repository where Codex will work with Veritas:
|
||||
|
||||
```md
|
||||
## Veritas Kanban managed-run protocol
|
||||
## Veritas Kanban Protocol
|
||||
|
||||
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.
|
||||
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.
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -254,14 +255,16 @@ If `autoTelemetry` is enabled, avoid double-emitting lifecycle events. Token usa
|
|||
|
||||
---
|
||||
|
||||
## Optional review rules
|
||||
## Review Rules
|
||||
|
||||
Independent review is not a default completion gate. Enable it only when the
|
||||
task, configured review gate, issue owner, or release owner requires it.
|
||||
| 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 |
|
||||
|
||||
When required, follow
|
||||
[SOP-cross-model-code-review.md](SOP-cross-model-code-review.md) for scoring,
|
||||
findings, and final gate handling.
|
||||
Follow [SOP-cross-model-code-review.md](SOP-cross-model-code-review.md) for scoring, findings, and final gate handling.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,18 @@
|
|||
# Optional Independent Code Review Playbook
|
||||
# SOP: Cross-Model Code Review (Claude ↔ GPT)
|
||||
|
||||
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.
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
## When to Trigger
|
||||
|
||||
| Work Type | Review Required? |
|
||||
| -------------------------------- | ------------------------ |
|
||||
| Application code, infra, scripts | When explicitly required |
|
||||
| Docs/content | When explicitly required |
|
||||
| Research summaries | When explicitly required |
|
||||
| Work Type | Review Required? |
|
||||
| -------------------------------- | ----------------------------------- |
|
||||
| Application code, infra, scripts | ✅ Always |
|
||||
| Docs/content | ⚠️ Only if accuracy/safety critical |
|
||||
| Research summaries | Optional (human discretion) |
|
||||
|
||||
If no review requirement is present, use focused self-verification and the
|
||||
normal human/CI review path.
|
||||
If in doubt, review.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -28,7 +24,7 @@ normal human/CI review path.
|
|||
- Type: `code`
|
||||
- Sprint/project identical
|
||||
- Description includes acceptance criteria + diff link(s)
|
||||
3. **Assign an independent reviewer** (human or configured agent):
|
||||
3. **Assign to opposite model** (via OpenClaw or other orchestrator):
|
||||
```
|
||||
Hey Codex, review PR for task_1234. Checklist below.
|
||||
```
|
||||
|
|
@ -48,8 +44,7 @@ normal human/CI review path.
|
|||
## Verdict
|
||||
Changes required.
|
||||
```
|
||||
7. **Audit trail**: Record the reviewer and outcome in the task or pull request
|
||||
when attribution is required by the configured policy.
|
||||
7. **Audit trail**: Update commit message or PR description with `[author: claude-sonnet-4-5][reviewed-by: gpt-5.1-codex]`.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -70,7 +65,7 @@ Adapt per task type.
|
|||
## Prompt Template (Reviewer)
|
||||
|
||||
```
|
||||
You are the independent reviewer. Apply the checklist:
|
||||
You are the cross-model reviewer. The code was authored by <model>. 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.
|
||||
|
|
@ -102,16 +97,15 @@ Store in `prompt-registry/cross-model-review.md`.
|
|||
|
||||
## Review Gates (Veritas Kanban Enforcement)
|
||||
|
||||
VK's built-in enforcement gates can make this optional playbook a structural
|
||||
requirement for selected workspaces or tasks:
|
||||
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:
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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`
|
||||
- Independent reviewer runs the configured review checklist
|
||||
- Reviewer (Model B) runs the cross-model 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
|
||||
|
|
@ -127,13 +121,10 @@ requirement for selected workspaces or tasks:
|
|||
|
||||
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 `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.
|
||||
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.
|
||||
|
||||
7. Full documentation: See [Enforcement Gates](enforcement.md) for all available gates, configuration options, and API reference.
|
||||
|
||||
---
|
||||
|
||||
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.
|
||||
This SOP preserved a 91% accuracy rate in RF-002. Keep following it.
|
||||
|
|
|
|||
|
|
@ -21,18 +21,13 @@ AI agents rely on documentation to understand context, conventions, and constrai
|
|||
|
||||
Every project should maintain these files:
|
||||
|
||||
| File | Purpose | Update Cadence |
|
||||
| ------------------------ | -------------------------------------- | ------------------------------------ |
|
||||
| `AGENTS.md` | Canonical agent rules and architecture | After toolchain/architecture changes |
|
||||
| `CLAUDE.md` | Claude-specific supplement | When Claude behavior differs |
|
||||
| `docs/BEST-PRACTICES.md` | Team patterns and anti-patterns | Monthly or after post-mortems |
|
||||
| `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.
|
||||
| File | Purpose | Update Cadence |
|
||||
| ------------------------ | -------------------------------------- | ----------------------------- |
|
||||
| `CLAUDE.md` | Agent rules, patterns, lessons learned | After every mistake/discovery |
|
||||
| `AGENTS.md` | Agent personality, escalation rules | When workflow changes |
|
||||
| `docs/BEST-PRACTICES.md` | Team patterns and anti-patterns | Monthly or after post-mortems |
|
||||
| `prompt-registry/*.md` | Workflow prompts | When prompts drift or improve |
|
||||
| `README.md` | Project overview, quick start | After major releases |
|
||||
|
||||
### Optional Model-Specific Files
|
||||
|
||||
|
|
@ -48,13 +43,9 @@ do not need synthetic freshness headers.
|
|||
|
||||
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. **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
|
||||
1. **A bug was caused by missing context** — Add the context to CLAUDE.md
|
||||
2. **Cross-model review catches a pattern** — Document the pattern
|
||||
3. **A workaround is discovered** — Add to Troubleshooting or CLAUDE.md
|
||||
4. **API behavior changes** — Update relevant docs
|
||||
|
||||
### Scheduled Updates
|
||||
|
|
@ -93,8 +84,7 @@ Run this monthly or after major releases:
|
|||
|
||||
- [ ] Prompts reference current API endpoints
|
||||
- [ ] No prompts for removed features
|
||||
- [ ] Optional review prompts match the current checklist and are not described
|
||||
as default delivery gates
|
||||
- [ ] Cross-model review prompt matches current checklist
|
||||
|
||||
### README.md
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,9 @@ 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>`.
|
||||
- Managed workers rely on VK-owned attempt timing. Only human or external
|
||||
self-reporting workers use `vk begin`/`vk done`.
|
||||
- Make sure each worker uses `vk begin/done` so timers stay accurate.
|
||||
5. **Review**:
|
||||
- Run independent or cross-model review only when required by the task or
|
||||
configured governance policy.
|
||||
- Run cross-model review before marking tasks done.
|
||||
- Request fixes via subtasks or comments.
|
||||
6. **Report**:
|
||||
- Post updates in task comments and daily standups (`vk summary standup --text`).
|
||||
|
|
@ -42,11 +40,11 @@ Task: <ID> — <Title>
|
|||
Context: <link to research/requirements>
|
||||
Deliverable: <clear definition of done>
|
||||
Steps:
|
||||
1. Treat the Veritas task envelope as authoritative.
|
||||
1. Run vk begin <id>.
|
||||
2. Complete subtasks in order. Leave notes if deviations occur.
|
||||
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.
|
||||
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.
|
||||
Uploads: <where to store artifacts>
|
||||
```
|
||||
|
||||
|
|
@ -118,8 +116,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 the Codex worker with the managed-run handoff template.
|
||||
5. **Codex**: Executes and returns its completion summary through the managed run.
|
||||
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.
|
||||
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,26 +10,12 @@ The Scoring Framework lets you define profiles with weighted criteria that evalu
|
|||
|
||||
**Scorer types:**
|
||||
|
||||
| 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.
|
||||
| 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 |
|
||||
|
||||
**Composite methods:**
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ Example frontmatter:
|
|||
id: cross-model-review
|
||||
name: Cross Model Review
|
||||
category: evaluation
|
||||
description: Optional independent review checklist
|
||||
description: Opposite-model review checklist
|
||||
---
|
||||
|
||||
# Cross Model Review
|
||||
|
|
|
|||
|
|
@ -134,17 +134,13 @@ Publish shared resources as a package:
|
|||
|
||||
## What to Share
|
||||
|
||||
### Common Shared Resources
|
||||
### Always Share
|
||||
|
||||
- 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
|
||||
|
|
@ -240,7 +236,7 @@ Fixture contracts live in
|
|||
```markdown
|
||||
## Instructions
|
||||
|
||||
Follow the configured independent review process.
|
||||
Follow the standard code 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: Review Policy | docs | medium | Optional review criteria. |
|
||||
| US-1605: Cross-Model Review | docs | medium | Opposite model gate. |
|
||||
| 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,9 +174,7 @@ 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 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.
|
||||
**`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.
|
||||
|
||||
**Health endpoint returns 500:** The metrics service or agent registry service failed to initialize. Check the server startup logs.
|
||||
|
||||
|
|
|
|||
|
|
@ -722,20 +722,6 @@ CREATE TABLE telemetry_events (
|
|||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE run_events (
|
||||
event_id TEXT PRIMARY KEY,
|
||||
workspace_id TEXT NOT NULL DEFAULT 'local' REFERENCES workspaces(id),
|
||||
task_id TEXT NOT NULL,
|
||||
attempt_id TEXT NOT NULL,
|
||||
sequence INTEGER NOT NULL CHECK (sequence > 0),
|
||||
provider_event_id TEXT,
|
||||
dedupe_key TEXT,
|
||||
received_at TEXT NOT NULL,
|
||||
event_json TEXT NOT NULL,
|
||||
UNIQUE (workspace_id, task_id, attempt_id, sequence),
|
||||
UNIQUE (workspace_id, task_id, attempt_id, dedupe_key)
|
||||
);
|
||||
|
||||
CREATE TABLE traces (
|
||||
id TEXT PRIMARY KEY,
|
||||
workspace_id TEXT NOT NULL REFERENCES workspaces(id),
|
||||
|
|
@ -757,8 +743,6 @@ CREATE INDEX idx_audit_workspace_created ON audit_events(workspace_id, timestamp
|
|||
CREATE INDEX idx_notifications_target ON notifications(workspace_id, target_agent, delivered, created_at DESC);
|
||||
CREATE INDEX idx_telemetry_type_created ON telemetry_events(workspace_id, type, created_at DESC);
|
||||
CREATE INDEX idx_telemetry_task_created ON telemetry_events(task_id, created_at DESC);
|
||||
CREATE INDEX idx_run_events_task_attempt_sequence ON run_events(workspace_id, task_id, attempt_id, sequence);
|
||||
CREATE INDEX idx_run_events_received_at ON run_events(workspace_id, received_at);
|
||||
CREATE INDEX idx_traces_attempt ON traces(attempt_id, created_at DESC);
|
||||
```
|
||||
|
||||
|
|
@ -1065,23 +1049,16 @@ 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. |
|
||||
| `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. |
|
||||
| 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. |
|
||||
|
||||
`ActivityService`, `StatusHistoryService`, `TelemetryService`,
|
||||
`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.
|
||||
`ActivityService`, `StatusHistoryService`, and `TelemetryService` 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.
|
||||
|
||||
Dashboard metric aggregation uses the same active storage backend. In SQLite
|
||||
mode, `/metrics/all`, `/metrics/trends`, agent comparison, task cost, and
|
||||
|
|
@ -1298,45 +1275,44 @@ Manual recovery is intentionally boring:
|
|||
|
||||
## File-Backed Parity Matrix
|
||||
|
||||
| Current source | SQLite destination | Notes |
|
||||
| ------------------------------------------------ | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tasks/active/*.md` | `tasks`, task detail tables, `task_search` | Preserve task IDs, created/updated timestamps, frontmatter, Markdown description, and file path in `migrated_from_path`. |
|
||||
| `tasks/archive/*.md` | `tasks.archived_at`, task detail tables | Archive status must remain restorable. |
|
||||
| `tasks/backlog/*.md` | `tasks.status` or backlog marker in `tasks` | If backlog remains distinct, model it as a status or queue field before provider work. |
|
||||
| `tasks/attachments/*` | `task_attachments` plus file blobs on disk | v5 stores attachment metadata in SQLite, not binary blobs. |
|
||||
| `.veritas-kanban/work-products.json` | `work_products`, `work_product_versions`, `work_product_search` | Preserve source task/run provenance, redaction metadata, typed render payload, and bounded versions. |
|
||||
| `.veritas-kanban/config.json` | `app_settings`, `repositories`, `agent_configs`, `agent_routing_rules`, `integrations` | Split security-sensitive integration secrets into `secret_refs_json`. |
|
||||
| `.veritas-kanban/activity.json` | `activity_events` | Existing newest-first array becomes append/query table. |
|
||||
| `.veritas-kanban/agent-status.json` | `app_settings` and `status_history` | Current status can be a setting; transitions remain history records. |
|
||||
| `.veritas-kanban/agent-registry.json` | future `agent_registry` table or `app_settings` namespace | Prefer a dedicated table when #413 pipeline modeling lands. |
|
||||
| `.veritas-kanban/credential-broker/state.json` | future `credential_definitions`, `credential_leases`, `credential_broker_audit_events` | The current repository is file-backed. Preserve digests and run bindings, but never migrate raw handles or credential values. |
|
||||
| `.veritas-kanban/notifications.json` | `notifications` | Preserve delivered state and timestamps. |
|
||||
| `.veritas-kanban/thread-subscriptions.json` | `thread_subscriptions` | Preserve task-agent subscription uniqueness. |
|
||||
| `.veritas-kanban/projects.json` | `managed_lists(kind='project')` | Map description and color columns. |
|
||||
| `.veritas-kanban/sprints.json` | `managed_lists(kind='sprint')` | Map description and order. |
|
||||
| `.veritas-kanban/task-types.json` | `managed_lists(kind='task_type')` | Map icon and color. |
|
||||
| `.veritas-kanban/templates/*.md` | `task_templates`, template child tables | Preserve YAML frontmatter data. |
|
||||
| `.veritas-kanban/prompt-templates/*.md` | `prompt_templates`, `prompt_search` | Preserve content and variables. |
|
||||
| `.veritas-kanban/prompt-versions/*.md` | `prompt_versions` | Preserve version numbers and changelogs. |
|
||||
| `.veritas-kanban/prompt-usage/*.json` | `prompt_usage` | Rendered prompts may contain sensitive task context. |
|
||||
| `.veritas-kanban/workflows/*.yml` | `workflows`, `workflow_agents`, `workflow_steps` | Store original YAML in `definition_yaml` for round-trip export. |
|
||||
| `.veritas-kanban/workflows/.acl.json` | `workflow_acls` | Preserve owner/editor/viewer/executor lists. |
|
||||
| `.veritas-kanban/workflows/.audit.jsonl` | `workflow_audit_events` | Append-only history remains ordered by import sequence and timestamp. |
|
||||
| `.veritas-kanban/workflow-runs/*/run.json` | `workflow_runs`, `workflow_run_steps` | Preserve context JSON and step state. |
|
||||
| `.veritas-kanban/workflow-runs/*/progress.md` | `workflow_run_outputs` or retained file path | Store text content when reasonably sized; otherwise keep file path. |
|
||||
| `.veritas-kanban/workflow-runs/*/step-outputs/*` | `workflow_run_outputs` | Preserve output filename and content type. |
|
||||
| `.veritas-kanban/tool-policies/*.json` | `tool_policies` | Role remains the natural unique key per workspace. |
|
||||
| `.veritas-kanban/governance-traces/*.json` | `governance_decision_traces` | Preserve redacted policy, permission, routing, and workflow gate explanations. |
|
||||
| `.veritas-kanban/storage/policies/*.json` | `policy_profiles` | Keep full rules JSON until policy schema is formalized. |
|
||||
| `.veritas-kanban/storage/drift/alerts/*` | `drift_alerts` | Preserve raw payload. |
|
||||
| `.veritas-kanban/storage/drift/baselines/*` | `drift_baselines` | Preserve raw payload. |
|
||||
| `.veritas-kanban/telemetry/*.ndjson*` | `telemetry_events` | Import gzip and plain NDJSON; preserve event IDs when present. |
|
||||
| `.veritas-kanban/traces/*.json` | `traces` | Apply retention and redaction rules. |
|
||||
| `.veritas-kanban/audit/audit-*.log` | `audit_events` | Preserve hash-chain fields and recompute `entry_hash` on import. |
|
||||
| `.veritas-kanban/broadcasts/*.md` | `broadcasts` | Preserve frontmatter and Markdown body. |
|
||||
| `storage/decisions/*` | `decisions` | Preserve decision content and status. |
|
||||
| `storage/scoring/*` and `storage/evaluations/*` | future scoring tables or `app_settings` namespace | Not in the first repository parity slice unless scoring endpoints are moved to storage abstraction. |
|
||||
| `docs/`, `prompt-registry/*.md` | `docs_search` and seed data | Do not make repo docs mutable app data unless user-created docs are imported later. |
|
||||
| Current source | SQLite destination | Notes |
|
||||
| ------------------------------------------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `tasks/active/*.md` | `tasks`, task detail tables, `task_search` | Preserve task IDs, created/updated timestamps, frontmatter, Markdown description, and file path in `migrated_from_path`. |
|
||||
| `tasks/archive/*.md` | `tasks.archived_at`, task detail tables | Archive status must remain restorable. |
|
||||
| `tasks/backlog/*.md` | `tasks.status` or backlog marker in `tasks` | If backlog remains distinct, model it as a status or queue field before provider work. |
|
||||
| `tasks/attachments/*` | `task_attachments` plus file blobs on disk | v5 stores attachment metadata in SQLite, not binary blobs. |
|
||||
| `.veritas-kanban/work-products.json` | `work_products`, `work_product_versions`, `work_product_search` | Preserve source task/run provenance, redaction metadata, typed render payload, and bounded versions. |
|
||||
| `.veritas-kanban/config.json` | `app_settings`, `repositories`, `agent_configs`, `agent_routing_rules`, `integrations` | Split security-sensitive integration secrets into `secret_refs_json`. |
|
||||
| `.veritas-kanban/activity.json` | `activity_events` | Existing newest-first array becomes append/query table. |
|
||||
| `.veritas-kanban/agent-status.json` | `app_settings` and `status_history` | Current status can be a setting; transitions remain history records. |
|
||||
| `.veritas-kanban/agent-registry.json` | future `agent_registry` table or `app_settings` namespace | Prefer a dedicated table when #413 pipeline modeling lands. |
|
||||
| `.veritas-kanban/notifications.json` | `notifications` | Preserve delivered state and timestamps. |
|
||||
| `.veritas-kanban/thread-subscriptions.json` | `thread_subscriptions` | Preserve task-agent subscription uniqueness. |
|
||||
| `.veritas-kanban/projects.json` | `managed_lists(kind='project')` | Map description and color columns. |
|
||||
| `.veritas-kanban/sprints.json` | `managed_lists(kind='sprint')` | Map description and order. |
|
||||
| `.veritas-kanban/task-types.json` | `managed_lists(kind='task_type')` | Map icon and color. |
|
||||
| `.veritas-kanban/templates/*.md` | `task_templates`, template child tables | Preserve YAML frontmatter data. |
|
||||
| `.veritas-kanban/prompt-templates/*.md` | `prompt_templates`, `prompt_search` | Preserve content and variables. |
|
||||
| `.veritas-kanban/prompt-versions/*.md` | `prompt_versions` | Preserve version numbers and changelogs. |
|
||||
| `.veritas-kanban/prompt-usage/*.json` | `prompt_usage` | Rendered prompts may contain sensitive task context. |
|
||||
| `.veritas-kanban/workflows/*.yml` | `workflows`, `workflow_agents`, `workflow_steps` | Store original YAML in `definition_yaml` for round-trip export. |
|
||||
| `.veritas-kanban/workflows/.acl.json` | `workflow_acls` | Preserve owner/editor/viewer/executor lists. |
|
||||
| `.veritas-kanban/workflows/.audit.jsonl` | `workflow_audit_events` | Append-only history remains ordered by import sequence and timestamp. |
|
||||
| `.veritas-kanban/workflow-runs/*/run.json` | `workflow_runs`, `workflow_run_steps` | Preserve context JSON and step state. |
|
||||
| `.veritas-kanban/workflow-runs/*/progress.md` | `workflow_run_outputs` or retained file path | Store text content when reasonably sized; otherwise keep file path. |
|
||||
| `.veritas-kanban/workflow-runs/*/step-outputs/*` | `workflow_run_outputs` | Preserve output filename and content type. |
|
||||
| `.veritas-kanban/tool-policies/*.json` | `tool_policies` | Role remains the natural unique key per workspace. |
|
||||
| `.veritas-kanban/governance-traces/*.json` | `governance_decision_traces` | Preserve redacted policy, permission, routing, and workflow gate explanations. |
|
||||
| `.veritas-kanban/storage/policies/*.json` | `policy_profiles` | Keep full rules JSON until policy schema is formalized. |
|
||||
| `.veritas-kanban/storage/drift/alerts/*` | `drift_alerts` | Preserve raw payload. |
|
||||
| `.veritas-kanban/storage/drift/baselines/*` | `drift_baselines` | Preserve raw payload. |
|
||||
| `.veritas-kanban/telemetry/*.ndjson*` | `telemetry_events` | Import gzip and plain NDJSON; preserve event IDs when present. |
|
||||
| `.veritas-kanban/traces/*.json` | `traces` | Apply retention and redaction rules. |
|
||||
| `.veritas-kanban/audit/audit-*.log` | `audit_events` | Preserve hash-chain fields and recompute `entry_hash` on import. |
|
||||
| `.veritas-kanban/broadcasts/*.md` | `broadcasts` | Preserve frontmatter and Markdown body. |
|
||||
| `storage/decisions/*` | `decisions` | Preserve decision content and status. |
|
||||
| `storage/scoring/*` and `storage/evaluations/*` | future scoring tables or `app_settings` namespace | Not in the first repository parity slice unless scoring endpoints are moved to storage abstraction. |
|
||||
| `docs/`, `prompt-registry/*.md` | `docs_search` and seed data | Do not make repo docs mutable app data unless user-created docs are imported later. |
|
||||
|
||||
## Security and Redaction Requirements
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ Common issues and solutions for Veritas Kanban. Can't find your issue? [Open a D
|
|||
## Table of Contents
|
||||
|
||||
- [Installation & Build](#installation--build)
|
||||
- [macOS Desktop Upgrade](#macos-desktop-upgrade)
|
||||
- [Authentication & Rate Limiting](#authentication--rate-limiting)
|
||||
- [Networking & Proxies](#networking--proxies)
|
||||
- [WebSocket Issues](#websocket-issues)
|
||||
|
|
@ -20,55 +19,6 @@ Common issues and solutions for Veritas Kanban. Can't find your issue? [Open a D
|
|||
|
||||
---
|
||||
|
||||
## macOS Desktop Upgrade
|
||||
|
||||
### Homebrew upgraded the app but port 3001 refuses connections
|
||||
|
||||
`brew upgrade --cask` replaces the app bundle but does not launch it. After
|
||||
`open -a "Veritas Kanban"`, LaunchServices can return several seconds before
|
||||
Electron starts the bundled server. One immediate failed health request is
|
||||
therefore not a startup verdict.
|
||||
|
||||
From a Veritas Kanban checkout, require the server to report the version in the
|
||||
installed app bundle:
|
||||
|
||||
```bash
|
||||
EXPECTED_VERSION="$(defaults read "/Applications/Veritas Kanban.app/Contents/Info" CFBundleShortVersionString)"
|
||||
open -a "Veritas Kanban"
|
||||
pnpm desktop:wait:ready -- --expected-version "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
For a Homebrew-only machine, use the bounded built-in-tools block in
|
||||
[Web To Mac Desktop Migration](WEB-TO-MAC-DESKTOP-MIGRATION.md#wait-for-the-desktop-server).
|
||||
It waits up to 30 seconds and rejects a stale server with the wrong version.
|
||||
|
||||
If the bounded wait times out:
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:3001 -sTCP:LISTEN
|
||||
ps -o pid,ppid,pgid,command -p "$(lsof -tiTCP:3001 -sTCP:LISTEN)"
|
||||
pgrep -ifl 'Veritas Kanban|veritas-kanban|server/dist/index.js'
|
||||
tail -n 80 "$HOME/Library/Application Support/@veritas-kanban/desktop/profiles/default/workspaces/local/logs/server.log"
|
||||
```
|
||||
|
||||
- No desktop process means app launch failed; open the app and inspect macOS
|
||||
Console plus the desktop log.
|
||||
- An old source `node`, `tsx`, `vite`, or `pnpm dev` process on `3001` is a
|
||||
competing writer. Stop it and its watchdog before relaunching the app.
|
||||
- A desktop process on another loopback port means `3001` was occupied during
|
||||
launch. The renderer may work, but migration and upgrade verification should
|
||||
fail until the packaged server owns `3001`.
|
||||
- If a quit is immediately followed by a reopen, wait for both port `3001` and
|
||||
the Electron main process to exit first. LaunchServices can ignore a reopen
|
||||
request while the prior instance is still shutting down.
|
||||
- Pause reopen heartbeats before quitting or upgrading. Resume them only after
|
||||
exact-version readiness succeeds.
|
||||
|
||||
The complete data-preserving procedure is in
|
||||
[Routine Mac Desktop Upgrade](V6-UPGRADE-INSTALL-ADMIN-GUIDE.md#routine-mac-desktop-upgrade).
|
||||
|
||||
---
|
||||
|
||||
## Dev Reliability (ports, hangs, and restarts)
|
||||
|
||||
### Agent note: planning is NOT a status
|
||||
|
|
@ -141,23 +91,22 @@ pnpm install
|
|||
pnpm build
|
||||
```
|
||||
|
||||
If errors persist, check your Node.js version. **Node 22.22.1+** is required:
|
||||
If errors persist, check your Node.js version — **Node 22+** is required:
|
||||
|
||||
```bash
|
||||
node -v # Must be v22.22.1 or higher
|
||||
node -v # Should be v22.x or higher
|
||||
```
|
||||
|
||||
### `pnpm` not found
|
||||
|
||||
Veritas Kanban uses pnpm workspaces. Activate the repository-pinned version:
|
||||
Veritas Kanban uses pnpm workspaces. Install it first:
|
||||
|
||||
```bash
|
||||
corepack enable
|
||||
corepack prepare pnpm@11.1.1 --activate
|
||||
npm install -g pnpm
|
||||
# or
|
||||
corepack enable && corepack prepare pnpm@latest --activate
|
||||
```
|
||||
|
||||
Do not install this workspace with npm, Yarn, or Bun.
|
||||
|
||||
### Port already in use
|
||||
|
||||
```bash
|
||||
|
|
@ -556,7 +505,7 @@ After restarting, confirm that Veritas Kanban was successfully discovered and al
|
|||
openclaw mcp list
|
||||
|
||||
# Expected output should include:
|
||||
# veritas-kanban | 41 tools | http://localhost:3001
|
||||
# veritas-kanban | 36 tools | http://localhost:3001
|
||||
|
||||
# View available tools from Veritas Kanban
|
||||
openclaw mcp tools veritas-kanban
|
||||
|
|
@ -572,7 +521,7 @@ openclaw mcp describe veritas-kanban vk_list_tasks
|
|||
- Build the MCP server if missing: `cd mcp && pnpm build`
|
||||
- Check OpenClaw logs for startup errors: `~/.openclaw/logs/mcp.log`
|
||||
|
||||
**If the tool count is wrong (not 41 tools):**
|
||||
**If the tool count is wrong (not 36 tools):**
|
||||
|
||||
- MCP server may have started but failed to initialize properly
|
||||
- Check VK API is accessible: `curl http://localhost:3001/api/health`
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
# v5 Compatibility And Release Policy
|
||||
|
||||
> Historical v5 policy. Current compatibility and release policy is in
|
||||
> [v6 Compatibility And Release Policy](V6-COMPATIBILITY-AND-RELEASE-POLICY.md).
|
||||
|
||||
This document defines the v5 release compatibility contract across the desktop
|
||||
app, bundled server, SQLite schema, CLI, MCP, mobile/PWA clients, workflow
|
||||
engine, WebSocket sync, migration tooling, and updater metadata.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
# Veritas Kanban v5 GA Checklist
|
||||
|
||||
> Historical v5 checklist. Current release gates are in
|
||||
> [v6 GA Checklist](V6-GA-CHECKLIST.md).
|
||||
|
||||
v5.0.0 stable was published and the final v5 source line is v5.2.5, including the
|
||||
v5.0.0 stable is published. The current source line is v5.2.5, including the
|
||||
post-v5.1 backlog train, the July 2026 desktop and responsive UI audit, the
|
||||
runtime security artifact gate, draggable startup/auth surfaces, and the
|
||||
non-destructive existing-desktop-data setup path. This checklist remains the
|
||||
|
|
@ -11,7 +8,7 @@ operator reference for release-gate review, follow-up evidence debt, and future
|
|||
v5 patch candidates. The GitHub release and release issues remain the source of
|
||||
scheduling truth.
|
||||
|
||||
For retained v5 candidate evidence, use
|
||||
For future candidates, use
|
||||
[v5 Release Candidate Evidence Packet](V5-RC-EVIDENCE-PACKET.md) as the single
|
||||
evidence target. The packet is the place to link command output, workflow runs,
|
||||
signed artifact URLs, checksums, notarization proof, load results, mobile/PWA
|
||||
|
|
@ -131,7 +128,6 @@ pnpm install --frozen-lockfile
|
|||
pnpm typecheck
|
||||
pnpm lint:budget
|
||||
pnpm test:unit
|
||||
pnpm desktop:test:readiness
|
||||
pnpm build
|
||||
pnpm validate:release
|
||||
pnpm smoke:cli-mcp
|
||||
|
|
|
|||
|
|
@ -1,34 +1,25 @@
|
|||
# v5 Release Evidence Packets
|
||||
|
||||
> Historical v5 evidence. Current release evidence is in
|
||||
> [v6 Release Candidate Evidence Packet](V6-RC-EVIDENCE-PACKET.md).
|
||||
|
||||
## v5.2.5 Release Evidence Packet
|
||||
|
||||
This section records the desktop existing-data upgrade fix, migration runbook,
|
||||
release validation, signed artifacts, and Homebrew publication for v5.2.5.
|
||||
and release-candidate validation for v5.2.5. Signed artifacts and Homebrew
|
||||
publication are added only after those independent gates pass.
|
||||
|
||||
### Release scope
|
||||
### Candidate scope
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| Release version | 5.2.5 |
|
||||
| Release issue | <https://github.com/BradGroux/veritas-kanban/issues/914> |
|
||||
| Desktop setup issue | <https://github.com/BradGroux/veritas-kanban/issues/901> |
|
||||
| Desktop setup PR | <https://github.com/BradGroux/veritas-kanban/pull/902> |
|
||||
| Migration docs issue | <https://github.com/BradGroux/veritas-kanban/issues/899> |
|
||||
| Migration docs PR | <https://github.com/BradGroux/veritas-kanban/pull/900> |
|
||||
| Dependency audit issue | <https://github.com/BradGroux/veritas-kanban/issues/903> |
|
||||
| Dependency audit PR | <https://github.com/BradGroux/veritas-kanban/pull/908> |
|
||||
| Release PR | <https://github.com/BradGroux/veritas-kanban/pull/922> |
|
||||
| Release commit | `55e621d14756799ee9b2d1ab4467013186776ae9` |
|
||||
| Annotated tag | `v5.2.5`; object `c1390f24ad892a2149118645965d7ab3f7691f17` |
|
||||
| GitHub release | <https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.5> |
|
||||
| Desktop Release run | <https://github.com/BradGroux/veritas-kanban/actions/runs/30019865167> |
|
||||
| Homebrew cask PR | <https://github.com/BradGroux/homebrew-tap/pull/33>; merge `d05cb0ad567eb1df239a2e99d8095398a8d46c37` |
|
||||
| Release state | Published, signed/notarized, independently verified, and available through Homebrew |
|
||||
| Release branch | `release/v5.2.5-914` |
|
||||
| Evidence date | 2026-07-23 |
|
||||
| Field | Value |
|
||||
| ---------------------- | -------------------------------------------------------- |
|
||||
| Release version | 5.2.5 |
|
||||
| Release issue | <https://github.com/BradGroux/veritas-kanban/issues/914> |
|
||||
| Desktop setup issue | <https://github.com/BradGroux/veritas-kanban/issues/901> |
|
||||
| Desktop setup PR | <https://github.com/BradGroux/veritas-kanban/pull/902> |
|
||||
| Migration docs issue | <https://github.com/BradGroux/veritas-kanban/issues/899> |
|
||||
| Migration docs PR | <https://github.com/BradGroux/veritas-kanban/pull/900> |
|
||||
| Dependency audit issue | <https://github.com/BradGroux/veritas-kanban/issues/903> |
|
||||
| Dependency audit PR | <https://github.com/BradGroux/veritas-kanban/pull/908> |
|
||||
| Release branch | `release/v5.2.5-914` |
|
||||
| Evidence date | 2026-07-23 |
|
||||
|
||||
### Upgrade contract
|
||||
|
||||
|
|
@ -82,50 +73,11 @@ Copilot reviewer supplied the available external review gate on PR #922.
|
|||
|
||||
### Publication evidence
|
||||
|
||||
The annotated tag peels to release commit
|
||||
`55e621d14756799ee9b2d1ab4467013186776ae9`. Desktop Release run
|
||||
[#30019865167](https://github.com/BradGroux/veritas-kanban/actions/runs/30019865167)
|
||||
completed successfully in 10m38s and published all seven expected assets.
|
||||
Independent downloads matched both the release-attached checksum files and the
|
||||
GitHub asset digests:
|
||||
|
||||
| Artifact | Bytes | SHA-256 |
|
||||
| --------------------------------------------- | ----------- | ------------------------------------------------------------------ |
|
||||
| `Veritas-Kanban-5.2.5-mac-arm64.dmg` | 268,133,388 | `056d448ad20adf03fe0a32ac0dc12bf3e4b384c1b7a6c8419edffc803322f688` |
|
||||
| `Veritas-Kanban-5.2.5-mac-arm64.dmg.blockmap` | 279,312 | `88ee282ac0980231c92097d00ac69c19423df889bf1a865bbb11803d401d75bf` |
|
||||
| `Veritas-Kanban-5.2.5-mac-arm64.dmg.sha256` | 101 | `59892a48108017cf8d3c7217f908e0f4638e00e86ce457e0f876ef82f488dbe2` |
|
||||
| `Veritas-Kanban-5.2.5-mac-arm64.zip` | 271,243,007 | `3f1816de7ae46d5e0541209f8acd65ffa32a47bd5caeedd9546358557e66fced` |
|
||||
| `Veritas-Kanban-5.2.5-mac-arm64.zip.blockmap` | 279,514 | `ecf30e86035a9b2e531f8632401bf28033019c82215b17cb32ee321dd44f456f` |
|
||||
| `Veritas-Kanban-5.2.5-mac-arm64.zip.sha256` | 101 | `7399226b582c5e776d8ab5096dd94a20453309b053f8395bb2d9288ff1bbfb93` |
|
||||
| `latest-mac.yml` | 530 | `1a88552cc0887e03acd25db3d6af30bb764b05892a4a0d8fc2ba94acec73b94e` |
|
||||
|
||||
`latest-mac.yml` reports version 5.2.5, names the published ZIP and DMG, and
|
||||
matches their byte sizes and SHA-512 values. The extracted ZIP app and DMG both
|
||||
pass strict code-signing verification, Gatekeeper acceptance as Notarized
|
||||
Developer ID, and stapled-ticket validation. The app is signed by Developer ID
|
||||
Application: Digital Meld, Inc. (`RLBHD62MPW`) with hardened runtime.
|
||||
|
||||
The downloaded signed app launched under an isolated profile and returned
|
||||
version 5.2.5 from `/api/health`. The setup path rendered, **Agent Ready**
|
||||
remained clickable, and dragging the non-control setup header physically moved
|
||||
the window. It then quit cleanly through the native app menu.
|
||||
|
||||
Homebrew issue
|
||||
[#32](https://github.com/BradGroux/homebrew-tap/issues/32) and PR
|
||||
[#33](https://github.com/BradGroux/homebrew-tap/pull/33) publish the ZIP checksum
|
||||
above. The registered tap passed `brew style --cask`,
|
||||
`brew audit --cask --strict --online`, `brew install --cask --dry-run`, and
|
||||
`brew livecheck`. A stopped-app laptop upgrade from 5.2.4 to 5.2.5 preserved a
|
||||
host-specific empty board with `PRAGMA quick_check` returning `ok` and matching
|
||||
0/0/0/0/0 task, Squad Chat, telemetry, workflow-definition, and workflow-run
|
||||
counts. The pre-upgrade snapshot is retained under the desktop workspace's
|
||||
`backups/pre-upgrade-20260723-103736` directory. After launch, port 3001 was
|
||||
owned by the packaged application and bundled `Resources/server/dist/index.js`,
|
||||
and `/api/health` returned 5.2.5.
|
||||
|
||||
Source merge, GitHub release publication, signed runtime verification, Homebrew
|
||||
availability, and a host-specific data check are separate gates; none
|
||||
substitutes for another.
|
||||
The release commit, tag object, Desktop Release run, signed artifact URLs and
|
||||
SHA-256 values, `latest-mac.yml` verification, notarization/stapling checks, and
|
||||
Homebrew tap PR are filled from the published v5.2.5 artifacts. Source merge,
|
||||
GitHub release publication, signed runtime verification, and Homebrew
|
||||
availability are separate gates; none substitutes for another.
|
||||
|
||||
## v5.2.2 Release Evidence Packet
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
# v5 Release Notes
|
||||
|
||||
> Historical v5 record. Current release notes are in
|
||||
> [v6 Release Notes](V6-RELEASE-NOTES.md).
|
||||
These notes describe the Veritas Kanban v5 stable release line.
|
||||
|
||||
- Final v5 source version: `v5.2.5`
|
||||
- Final published v5 GitHub release:
|
||||
[Veritas Kanban v5.2.5](https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.5)
|
||||
- Current source version: `v5.2.5`
|
||||
- Latest published GitHub release:
|
||||
[Veritas Kanban v5.2.4](https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.4)
|
||||
- Supported packaged install:
|
||||
`brew tap BradGroux/tap && brew install --cask veritas-kanban`
|
||||
- Manual macOS install:
|
||||
[Veritas-Kanban-5.2.5-mac-arm64.zip](https://github.com/BradGroux/veritas-kanban/releases/download/v5.2.5/Veritas-Kanban-5.2.5-mac-arm64.zip)
|
||||
[Veritas-Kanban-5.2.4-mac-arm64.zip](https://github.com/BradGroux/veritas-kanban/releases/download/v5.2.4/Veritas-Kanban-5.2.4-mac-arm64.zip)
|
||||
|
||||
## v5.2.5 Patch
|
||||
## v5.2.5 Patch Candidate
|
||||
|
||||
The v5.2.5 release makes the signed desktop upgrade path unambiguous
|
||||
The v5.2.5 source candidate makes the signed desktop upgrade path unambiguous
|
||||
for operators whose data is already in the desktop SQLite workspace. It also
|
||||
publishes the provider runtime contract and SQLite safety work merged since
|
||||
v5.2.4.
|
||||
prepares the provider runtime contract and SQLite safety work merged since
|
||||
v5.2.4 for publication.
|
||||
|
||||
### Desktop startup and existing data
|
||||
|
||||
|
|
@ -362,14 +361,15 @@ Manual installation is also supported from the stable GitHub release ZIP.
|
|||
|
||||
## Release Artifacts
|
||||
|
||||
The
|
||||
[v5.2.5 GitHub release](https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.5)
|
||||
contains the signed/notarized macOS ZIP and DMG assets, `latest-mac.yml`,
|
||||
blockmaps, and SHA-256 sidecars. The release-attached `.sha256` files are the
|
||||
checksum source of truth.
|
||||
When the v5.2.5 release is published, its GitHub release must contain the
|
||||
signed/notarized macOS ZIP and DMG assets, `latest-mac.yml`, blockmaps, and
|
||||
SHA-256 sidecars. The release-attached `.sha256` files are the checksum source
|
||||
of truth after publication.
|
||||
|
||||
The v5.2.4, v5.2.3, and v5.2.2 desktop assets remain available for provenance
|
||||
and rollback.
|
||||
The current stable
|
||||
[v5.2.4 GitHub release](https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.4)
|
||||
and the v5.2.3 and v5.2.2 desktop assets remain available for installation,
|
||||
provenance, and rollback until v5.2.5 publication completes.
|
||||
|
||||
The v5.2.1 desktop assets remain available for provenance, but the application
|
||||
bundle is not a supported rollback target because its emitted Electron main
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
# v5 Upgrade, Install, Remote, And Admin Guide
|
||||
|
||||
> Historical v5 guide. Current install and upgrade guidance is in
|
||||
> [v6 Upgrade, Install, Remote, And Admin Guide](V6-UPGRADE-INSTALL-ADMIN-GUIDE.md).
|
||||
|
||||
This guide is the retained release-facing entry point for v5 operators. It links the
|
||||
This guide is the release-facing entry point for v5 operators. It links the
|
||||
existing detailed docs and keeps the happy path separate from optional
|
||||
automation layers.
|
||||
|
||||
For release-safe dummy screenshots and GIFs captured from the v5 desktop shell,
|
||||
For current release-safe dummy screenshots and GIFs of the v5 desktop shell,
|
||||
resizable Workbench, agent provider settings, task work view, Maintenance
|
||||
Center, and mobile/PWA shell, see
|
||||
[v5 Visual Tour](V5-VISUAL-TOUR.md). Release evidence, when needed for a future
|
||||
|
|
@ -36,13 +33,13 @@ candidate, belongs in the reusable
|
|||
```
|
||||
|
||||
Manual install is also supported from the
|
||||
[final v5 GitHub release](https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.5)
|
||||
by downloading `Veritas-Kanban-5.2.5-mac-arm64.zip`, unzipping it, and
|
||||
[current stable GitHub release](https://github.com/BradGroux/veritas-kanban/releases/tag/v5.2.4)
|
||||
by downloading `Veritas-Kanban-5.2.4-mac-arm64.zip`, unzipping it, and
|
||||
moving `Veritas Kanban.app` into `/Applications`.
|
||||
|
||||
The signed release workflow, checksums, notarization checks, and Homebrew
|
||||
publication are recorded in
|
||||
[the evidence packet](V5-RC-EVIDENCE-PACKET.md).
|
||||
The v5.2.5 filename and release URL become valid only after the signed
|
||||
release workflow and publication checks in
|
||||
[the evidence packet](V5-RC-EVIDENCE-PACKET.md) pass.
|
||||
|
||||
2. Launch normally. A stable release should not show a Gatekeeper warning.
|
||||
3. Pick the first-run path:
|
||||
|
|
@ -85,71 +82,6 @@ both an already-populated desktop database and a file-backed
|
|||
`tasks/`/`.veritas-kanban/` source, including one-writer cutover, backups,
|
||||
watchdogs, record counts, first-launch selection, rollback, and packaged auth.
|
||||
|
||||
## Routine Mac Desktop Upgrade
|
||||
|
||||
Homebrew replaces the app bundle, but it does not launch the app or wait for
|
||||
the bundled server. macOS also returns from `open -a` before that server is
|
||||
necessarily listening. Use this sequence for an existing desktop workspace:
|
||||
|
||||
1. Create a governed backup in Settings -> Maintenance. Confirm the completed
|
||||
report before quitting.
|
||||
2. Pause any heartbeat, LaunchAgent, or other automation that reopens Veritas
|
||||
Kanban. Otherwise it can race the upgrade.
|
||||
3. Quit the app and confirm the preferred port no longer has a listener:
|
||||
|
||||
```bash
|
||||
osascript -e 'quit app "Veritas Kanban"' 2>/dev/null || true
|
||||
APP_MAIN_PATTERN='^/Applications/Veritas Kanban[.]app/Contents/MacOS/veritas-kanban$'
|
||||
DESKTOP_STOPPED=0
|
||||
|
||||
for ATTEMPT in {1..20}; do
|
||||
if ! lsof -nP -iTCP:3001 -sTCP:LISTEN >/dev/null 2>&1 &&
|
||||
! pgrep -f "$APP_MAIN_PATTERN" >/dev/null 2>&1; then
|
||||
DESKTOP_STOPPED=1
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if test "$DESKTOP_STOPPED" -ne 1; then
|
||||
echo "Veritas Kanban did not finish quitting; inspect it before upgrading." >&2
|
||||
lsof -nP -iTCP:3001 -sTCP:LISTEN
|
||||
pgrep -ifl "$APP_MAIN_PATTERN" || true
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
4. Refresh the tap, upgrade, and read the installed bundle version:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
brew list --cask --versions veritas-kanban
|
||||
EXPECTED_VERSION="$(defaults read "/Applications/Veritas Kanban.app/Contents/Info" CFBundleShortVersionString)"
|
||||
printf 'Installed Veritas Kanban %s\n' "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
5. Launch and use the bounded exact-version readiness gate from
|
||||
[Web To Mac Desktop Migration](WEB-TO-MAC-DESKTOP-MIGRATION.md#wait-for-the-desktop-server).
|
||||
From a current checkout:
|
||||
|
||||
```bash
|
||||
open -a "Veritas Kanban"
|
||||
pnpm desktop:wait:ready -- --expected-version "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
Operators without a checkout should use the Homebrew-only block in that
|
||||
linked section. Do not use an immediate `curl` or a fixed `sleep`.
|
||||
|
||||
6. Confirm the process listening on `3001` resolves through
|
||||
`/Applications/Veritas Kanban.app`, then verify the board and Settings ->
|
||||
Maintenance.
|
||||
7. Resume the heartbeat only after readiness and the app/health version match.
|
||||
|
||||
If the app already contains the migrated data, this routine upgrade does not
|
||||
require an import, restore, or first-run path change. Preserve the same desktop
|
||||
profile and select **Use Existing Data** only if setup is shown.
|
||||
|
||||
## v4 To v5 Upgrade
|
||||
|
||||
For a complete operator runbook that upgrades an existing file-backed
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
# Veritas Kanban v5 Visual Tour
|
||||
|
||||
> Historical v5 visual record. Current release visual guidance is in
|
||||
> [v6 Visual Tour](V6-VISUAL-TOUR.md).
|
||||
|
||||
This page is the v5 documentation media index. The screenshots and GIFs use
|
||||
release-safe dummy content captured from the current app surfaces so the docs
|
||||
can show realistic workflows without exposing real tasks, prompts, logs, paths,
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
# Veritas Kanban v6 Compatibility And Release Policy
|
||||
|
||||
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-08-24 for Veritas Kanban 6.1.2.
|
||||
|
||||
## Harness Support Tiers
|
||||
|
||||
| Tier | Meaning | Dispatch consequence |
|
||||
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| Certified | Installed version/build, profile, runtime manifest, probe revision, capabilities, and deterministic fixtures match passing evidence. | Allowed when all requested controls are supported. |
|
||||
| Configured | The explicit executable adapter is enabled and ready, but current certification evidence is absent. | Allowed with a warning unless policy requires certification. |
|
||||
| Detected | The executable is installed but the built-in profile is disabled. | Not selected automatically. |
|
||||
| Degraded | Installation, authentication, compatibility, policy, or certification evidence is unhealthy or stale. | Enabled profiles block before attempt creation. |
|
||||
| Unsupported | No safe executable adapter or current-platform contract exists. | Always blocks before attempt creation. |
|
||||
|
||||
Settings, `vk doctor --json`, API diagnostics, telemetry, and dispatch must use
|
||||
these same definitions. Provider-name shortcuts and implicit OpenClaw fallback
|
||||
are incompatible with v6.
|
||||
|
||||
## Compatibility Matrix
|
||||
|
||||
| Component | Supported v6 combination | Detection/evidence | Fail-closed boundary |
|
||||
| -------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 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. |
|
||||
| Grok Build | v0.2.111 build `94172f2aa4e5`; generic ACP v1. | Exact version/build and `x.ai` capability handshake. | Unknown version, approval bypass, leader/plugin/endpoint injection, or unsupported controls blocks. |
|
||||
| Codex CLI | Explicit `codex-cli` adapter with exact runtime probe. | `codex --version`, `codex login status`, runtime manifest. | Missing adapter evidence or requested unsupported controls blocks. |
|
||||
| Codex SDK | `@openai/codex-sdk 0.144.3`. | SDK import plus Codex authentication and runtime manifest. | Missing SDK/authentication or unsupported controls blocks. |
|
||||
| Codex app-server | `codex-cli 0.145.0`, upstream commit `25af12f7e61572b0bc18ddb1008be543b91519b0`. | Generated v2 schemas, exact version, remote control disabled, deterministic and optional live smoke. | Schema drift, remote control, inherited plugins/apps/hooks/browser/computer tools, or unsandboxed shell method blocks. |
|
||||
| Claude Code | `2.1.218 (Claude Code)`. | Version, auth status, agent discovery, stream fixtures, optional live smoke. | Permission bypass, inherited config/plugins, unsupported lifecycle request, bad stream, or missing authoritative result blocks. |
|
||||
| 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.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. |
|
||||
|
||||
## Certification And Invalidation
|
||||
|
||||
Deterministic conformance runs reset a seeded fixture for each repetition and
|
||||
persist `harness-conformance-result/v1`. Certification is invalidated when the
|
||||
provider version/build, profile digest, runtime probe revision, transport
|
||||
version, capability digest, fixture revision, or required policy changes.
|
||||
|
||||
Credential-gated live smoke is supplemental. It may prove authentication and
|
||||
one bounded inference on the exact build, but it cannot override a deterministic
|
||||
failure. If credentials, subscription, quota, binary, or upstream service are
|
||||
unavailable, the live tier and evidence must say so.
|
||||
|
||||
The credential-free Buzz release gate is:
|
||||
|
||||
```bash
|
||||
pnpm test:buzz:compatibility
|
||||
```
|
||||
|
||||
Provider-specific opt-in smoke commands are documented in
|
||||
[Agent Providers](AGENT-PROVIDERS.md).
|
||||
|
||||
## Security And Authority Boundaries
|
||||
|
||||
- Veritas owns tasks, attempts, worktrees, launch manifests, approvals,
|
||||
credentials, run tools, causal events, and completion.
|
||||
- Providers own only their documented transport and native session identifiers.
|
||||
- Buzz communication owns signed relay delivery, not task completion.
|
||||
- Credentials enter a provider only through an allowlisted boot-authentication
|
||||
key or a run-scoped brokered call. Raw values are never release evidence.
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
| Channel | Purpose | Promotion gate |
|
||||
| -------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `dev` | Local packaged testing and controlled development metadata. | Local build and smoke only; never promoted to users. |
|
||||
| `beta` | Prerelease candidate testing. | Full CI, deterministic harness suites, unsigned desktop packaging, migration and runtime smoke, documented blockers. |
|
||||
| `stable` | Supported macOS arm64 release. | Merged release PR, annotated tag, published release, signed/notarized assets, checksums, updater metadata, downloaded-app launch, and live Homebrew validation. |
|
||||
|
||||
Linux and Windows preview artifacts do not become stable merely because their
|
||||
packaging workflows pass.
|
||||
|
||||
## Version Negotiation
|
||||
|
||||
1. The server, web, CLI, MCP, shared library, and desktop release versions must
|
||||
be identical.
|
||||
2. CLI and MCP write smoke compares the local package version with
|
||||
`/api/health.version`. Unreviewed skew is unsupported.
|
||||
3. A provider upgrade invalidates its old support record even when the provider
|
||||
name is unchanged.
|
||||
4. Desktop local mode uses one bundled server/web payload from the same release
|
||||
commit.
|
||||
5. A stale web/PWA client must refresh before writing. v6 does not queue
|
||||
offline API writes.
|
||||
|
||||
## Rollback Policy
|
||||
|
||||
| Asset | Supported rollback | Limit |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| macOS app bundle | Reinstall the previous signed release or correct updater metadata. | Safe only when current data/profile records remain compatible with the older app. |
|
||||
| Bundled server/web | Roll back with the complete app bundle. | Never mix payloads from different commits. |
|
||||
| Self-hosted server | Install the prior release and restore the pre-upgrade backup when required. | No automatic destructive schema down-migration promise. |
|
||||
| Desktop SQLite/profile | Restore the stopped-writer v5.2.5 backup. | Do not copy over a live database or treat app downgrade as data rollback. |
|
||||
| Provider profile | Disable the changed profile or restore reviewed v5 configuration from backup. | Do not re-enable an ambiguous legacy profile or bypass support evidence. |
|
||||
| Buzz connection | Disable trigger rules, mapping, and adapter while retaining audits and local Squad Chat data. | Veritas does not delete remote Buzz events or write imported definitions back. |
|
||||
|
||||
Follow [v6 Upgrade, Install, Remote, And Admin Guide](V6-UPGRADE-INSTALL-ADMIN-GUIDE.md)
|
||||
and [Migration Recovery](MIGRATION-RECOVERY.md) for the concrete recovery path.
|
||||
|
|
@ -1,336 +0,0 @@
|
|||
# Veritas Kanban v6 GA Checklist
|
||||
|
||||
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-08-24 for Veritas Kanban 6.1.2.
|
||||
|
||||
## 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.
|
||||
- [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.2.
|
||||
- [x] `AGENTS.md`, README badge, health, CLI, MCP, desktop bundle, artifact
|
||||
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
|
||||
credential-free compatibility gate at the pinned commit and fixture
|
||||
revision.
|
||||
- [x] Grok Build v0.2.111 build `94172f2aa4e5` passes exact-version ACP,
|
||||
restrictive-policy, source-limitation, and deterministic fixtures.
|
||||
- [x] Codex app-server 0.145.0 passes exact generated schemas, disabled remote
|
||||
control, lifecycle, approval, event, completion, and deterministic
|
||||
fixtures.
|
||||
- [x] Codex CLI and `@openai/codex-sdk 0.144.3` pass their provider-runtime,
|
||||
launch, tool, event, credential, and completion gates.
|
||||
- [x] Claude Code 2.1.218 passes bare-mode launch, permission, environment,
|
||||
stream, lifecycle, MCP, event, completion, and deterministic fixtures.
|
||||
- [x] GitHub Copilot CLI 1.0.74 passes exact ACP handshake, restrictive launch,
|
||||
preview/source-limit, and deterministic fixtures.
|
||||
- [x] Hermes v2026.7.7.2 and OpenClaw v2026.6.11 retain truthful existing
|
||||
support and explicit unsupported controls.
|
||||
- [x] Settings, API, `vk doctor --json`, dispatch, and telemetry report the same
|
||||
Detected, Configured, Certified, Degraded, or Unsupported state.
|
||||
- [x] Every Certified claim has exact runtime/build and passing deterministic
|
||||
evidence. Credential-gated smoke is recorded separately; unavailable
|
||||
credentials, quota, subscriptions, binaries, and upstream services are
|
||||
reported rather than inferred.
|
||||
|
||||
## Security, Migration, And Data Preservation
|
||||
|
||||
- [x] Legacy provider profiles normalize only through exact built-in
|
||||
type/command identity and never fall through to OpenClaw.
|
||||
- [x] Claude Code permission bypass is absent and unsafe custom launch controls
|
||||
fail closed.
|
||||
- [x] Approval decisions bind to exact action, attempt, reviewer, expiry, and
|
||||
authentication freshness; replay and drift tests pass.
|
||||
- [x] Credential definitions, leases, run catalogs, the `veritas-run` bridge,
|
||||
logs, telemetry, fixtures, and APIs remain value-free outside the
|
||||
one-shot downstream call.
|
||||
- [x] Provider protocol frames, stdout/stderr, events, retries, timeouts, and
|
||||
retained payloads are bounded and redacted.
|
||||
- [x] Required unsupported sandbox, network, tool, MCP, credential, lifecycle,
|
||||
and provider controls block before attempt mutation.
|
||||
- [x] A populated v5.2.5 desktop workspace upgrades through an isolated copy
|
||||
with matching representative counts, `PRAGMA quick_check=ok`, preserved
|
||||
owner/profile metadata, preserved registry absence, and a working v6
|
||||
runtime. A separate exact-format v5.2.5 registry fixture verifies populated
|
||||
registry compatibility.
|
||||
- [x] File-backed migration and restore paths pass their seven-test portability
|
||||
fixture.
|
||||
- [x] Rollback guidance has been tested against the actual schema/profile
|
||||
posture and does not promise destructive down migration.
|
||||
- [x] Secret and tracked-runtime-security scans pass.
|
||||
|
||||
## Application And Runtime
|
||||
|
||||
- [x] A clean dependency install and build produce new shared, server, web, CLI,
|
||||
MCP, and desktop outputs without reused `dist`, staged desktop payloads,
|
||||
or prior release artifacts.
|
||||
- [x] An isolated in-app Browser run verifies first-run, login, provider and Buzz
|
||||
Settings, degraded states, themes, command-palette focus, and a clean
|
||||
console. Deterministic and E2E suites verify task launch, causal events,
|
||||
approvals, cancellation, completion, MCP, worktrees, telemetry, and Buzz
|
||||
mapping/replay under the maintainer-approved release-evidence split
|
||||
recorded in the evidence packet.
|
||||
- [x] Dark/light themes, keyboard flow, labels, and focus are inspected in the
|
||||
in-app Browser; compact layout and recoverable error states pass the E2E
|
||||
matrix under the same evidence split.
|
||||
- [x] A fresh native macOS build verifies one instance, visible window, bundled
|
||||
server/web health, menus, shortcuts, window restoration, update check,
|
||||
clean close, reopen, and quit.
|
||||
- [x] The unsigned DMG/ZIP is inspected and the packaged app launches from the
|
||||
packaged artifact with an isolated profile.
|
||||
|
||||
## Mantine component-system cleanup gate
|
||||
|
||||
- [x] Run `pnpm --filter @veritas-kanban/web build` before the bundle check.
|
||||
- [x] Run `pnpm qa:mantine`.
|
||||
- [x] Run `pnpm test:e2e -- e2e/mantine-qa-gate.spec.ts`.
|
||||
- [x] Retain visual and accessibility evidence for current routes, dark/light
|
||||
themes, compact widths, keyboard navigation, focus, labels, and touch
|
||||
targets.
|
||||
- [x] Track planned but unavailable surfaces as temporary holdouts instead of
|
||||
claiming coverage.
|
||||
- [x] No active feature imports legacy primitive compatibility wrappers outside
|
||||
documented internals.
|
||||
- [x] No direct shadcn/Radix dependency or vendor-radix bundle returns.
|
||||
- [x] Bundle sizes remain within the recorded QA budgets or have an explicit
|
||||
release-risk acceptance.
|
||||
|
||||
## Historical 6.0.2 Final Release Validation Commands
|
||||
|
||||
Run from the clean release-candidate worktree:
|
||||
|
||||
```bash
|
||||
node --version
|
||||
pnpm --version
|
||||
git --version
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm check:pnpm-settings
|
||||
pnpm audit --prod --audit-level=high
|
||||
pnpm lint
|
||||
pnpm lint:budget
|
||||
pnpm qa:mantine
|
||||
pnpm typecheck
|
||||
pnpm build
|
||||
pnpm test:unit
|
||||
pnpm test:e2e
|
||||
pnpm smoke:cli-mcp
|
||||
pnpm test:buzz:compatibility
|
||||
pnpm desktop:test
|
||||
pnpm desktop:build
|
||||
pnpm desktop:check:electron-artifacts
|
||||
pnpm desktop:smoke:mac:local
|
||||
pnpm desktop:package:mac:unsigned
|
||||
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
|
||||
their test counts and exact fixture baselines separately. Run credential-gated
|
||||
provider smoke only when the exact binary, authentication, subscription, and
|
||||
quota are available.
|
||||
|
||||
## Historical 6.0.2 Distribution And Post-Publication
|
||||
|
||||
- [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.
|
||||
- [x] Independent downloads match GitHub digests, sidecars, updater metadata,
|
||||
byte sizes, and SHA-256 values.
|
||||
- [x] DMG and ZIP app signatures, hardened runtime, Gatekeeper, and notarization
|
||||
stapling pass.
|
||||
- [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.
|
||||
- [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.
|
||||
- [x] The evidence packet contains release/workflow/asset/Homebrew links,
|
||||
exact hashes, runtime results, limitations, and deferred v6.x issues.
|
||||
- [x] The release tracker closes only after every distribution surface above is
|
||||
independently verified.
|
||||
|
|
@ -1,536 +0,0 @@
|
|||
# Veritas Kanban v6 Release Candidate Evidence Packet
|
||||
|
||||
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.
|
||||
|
||||
Veritas Kanban 6.1.2 is the supported stable v6 release. Do not use 6.0.0 for
|
||||
installation or upgrade validation.
|
||||
|
||||
Documentation freshness: 2026-08-24 for the published Veritas Kanban 6.1.2 release.
|
||||
|
||||
## 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
|
||||
|
||||
### Support, task, launch, credential, and worktree foundations
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
|
||||
| [First-class harness support profile](https://github.com/BradGroux/veritas-kanban/issues/919) | [#925](https://github.com/BradGroux/veritas-kanban/pull/925) | One support tier and fail-closed onboarding contract |
|
||||
| [Immutable run launch manifest](https://github.com/BradGroux/veritas-kanban/issues/854) | [#926](https://github.com/BradGroux/veritas-kanban/pull/926) | Attempt-bound compiled launch evidence |
|
||||
| [Provider-owned task transports](https://github.com/BradGroux/veritas-kanban/issues/892) | [#928](https://github.com/BradGroux/veritas-kanban/pull/928) | Immutable task request rendered per adapter |
|
||||
| [Idempotent completion results](https://github.com/BradGroux/veritas-kanban/issues/893) | [#930](https://github.com/BradGroux/veritas-kanban/pull/930) | One authoritative provider-neutral terminal result |
|
||||
| [Credential registry and leases](https://github.com/BradGroux/veritas-kanban/issues/931) | [#933](https://github.com/BradGroux/veritas-kanban/pull/933) | Value-free definitions and exact run/action leases |
|
||||
| [Transactional worktrees](https://github.com/BradGroux/veritas-kanban/issues/858) | [#934](https://github.com/BradGroux/veritas-kanban/pull/934) | Remote-safe allocation, ownership, recovery, and cleanup |
|
||||
|
||||
### Durable shared runtime
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
||||
| [Causal run-event journal](https://github.com/BradGroux/veritas-kanban/issues/850) | [#949](https://github.com/BradGroux/veritas-kanban/pull/949), [#950](https://github.com/BradGroux/veritas-kanban/pull/950) | Replayable redacted events and CI repair |
|
||||
| [Interactive approval broker](https://github.com/BradGroux/veritas-kanban/issues/852) | [#953](https://github.com/BradGroux/veritas-kanban/pull/953) | Exact-action durable approvals |
|
||||
| [Durable run supervisor](https://github.com/BradGroux/veritas-kanban/issues/853) | [#954](https://github.com/BradGroux/veritas-kanban/pull/954) | Restart ownership, recovery, and terminal reconciliation |
|
||||
| [Conversation lifecycle](https://github.com/BradGroux/veritas-kanban/issues/856) | [#958](https://github.com/BradGroux/veritas-kanban/pull/958) | Capability-gated resume, follow-up, fork, steer, and close |
|
||||
| [Run-scoped tool control plane](https://github.com/BradGroux/veritas-kanban/issues/857) | [#959](https://github.com/BradGroux/veritas-kanban/pull/959) | Versioned discovery/catalog and governed invocation |
|
||||
| [Generic ACP stdio provider](https://github.com/BradGroux/veritas-kanban/issues/870) | [#961](https://github.com/BradGroux/veritas-kanban/pull/961) | Provider-neutral ACP v1 client adapter |
|
||||
| [ACP server view](https://github.com/BradGroux/veritas-kanban/issues/960) | [#967](https://github.com/BradGroux/veritas-kanban/pull/967) | `vk acp serve` and status over Veritas-owned sessions |
|
||||
| [Launch credential classification](https://github.com/BradGroux/veritas-kanban/issues/932) | [#963](https://github.com/BradGroux/veritas-kanban/pull/963) | Boot, brokered, and high-risk passthrough evidence |
|
||||
| [Credential boundary catalog](https://github.com/BradGroux/veritas-kanban/issues/968) | [#971](https://github.com/BradGroux/veritas-kanban/pull/971) | Exact value-free broker boundary |
|
||||
| [Mediated lease consumption](https://github.com/BradGroux/veritas-kanban/issues/969) | [#972](https://github.com/BradGroux/veritas-kanban/pull/972) | One-shot downstream MCP credential resolution |
|
||||
| [System-owned provider bridge](https://github.com/BradGroux/veritas-kanban/issues/970) | [#973](https://github.com/BradGroux/veritas-kanban/pull/973) | Narrow `veritas-run` injection or fail-closed block |
|
||||
| [Runtime hook bus](https://github.com/BradGroux/veritas-kanban/issues/874) | [#977](https://github.com/BradGroux/veritas-kanban/pull/977) | Bounded in-process pre/post runtime hooks |
|
||||
| [Harness conformance suites](https://github.com/BradGroux/veritas-kanban/issues/859) | [#975](https://github.com/BradGroux/veritas-kanban/pull/975) | Seeded deterministic repeated evaluation |
|
||||
|
||||
### Harness adapters and Buzz delivery
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------- |
|
||||
| [Grok Build ACP profile](https://github.com/BradGroux/veritas-kanban/issues/920) | [#966](https://github.com/BradGroux/veritas-kanban/pull/966) | Exact v0.2.111 restrictive ACP profile |
|
||||
| [Codex app-server v2](https://github.com/BradGroux/veritas-kanban/issues/921) | [#952](https://github.com/BradGroux/veritas-kanban/pull/952) | Pinned JSON-RPC v2 lifecycle adapter |
|
||||
| [Claude Code stream adapter](https://github.com/BradGroux/veritas-kanban/issues/916) | [#951](https://github.com/BradGroux/veritas-kanban/pull/951) | Bare-mode supervised stream-json execution |
|
||||
| [GitHub Copilot CLI ACP profile](https://github.com/BradGroux/veritas-kanban/issues/917) | [#965](https://github.com/BradGroux/veritas-kanban/pull/965) | Public-preview exact v1.0.74 restrictive profile |
|
||||
| [Buzz compatibility diagnostics](https://github.com/BradGroux/veritas-kanban/issues/905) | [#946](https://github.com/BradGroux/veritas-kanban/pull/946) | Reference-only relay/identity evidence |
|
||||
| [Buzz Squad Chat adapter](https://github.com/BradGroux/veritas-kanban/issues/906) | [#947](https://github.com/BradGroux/veritas-kanban/pull/947) | Signed bidirectional roots/replies and replay |
|
||||
| [Buzz Agent ACP profile](https://github.com/BradGroux/veritas-kanban/issues/907) | [#964](https://github.com/BradGroux/veritas-kanban/pull/964) | Exact Buzz v0.4.24 generic ACP profile |
|
||||
| [Buzz run-scoped MCP](https://github.com/BradGroux/veritas-kanban/issues/909) | [#974](https://github.com/BradGroux/veritas-kanban/pull/974) | System-owned `veritas-run` bridge for Buzz |
|
||||
| [Buzz persona/team import](https://github.com/BradGroux/veritas-kanban/issues/910) | [#948](https://github.com/BradGroux/veritas-kanban/pull/948) | Signed preview-first one-way import |
|
||||
| [Buzz workflow triggers](https://github.com/BradGroux/veritas-kanban/issues/911) | [#978](https://github.com/BradGroux/veritas-kanban/pull/978) | Allowlisted root-message triggers with replay protection |
|
||||
| [Buzz compatibility gate](https://github.com/BradGroux/veritas-kanban/issues/912) | [#979](https://github.com/BradGroux/veritas-kanban/pull/979) | Composed 50-test credential-free gate |
|
||||
| [Cross-harness matrix](https://github.com/BradGroux/veritas-kanban/issues/918) | [#976](https://github.com/BradGroux/veritas-kanban/pull/976) | One matrix for API, doctor, Settings, telemetry, and dispatch |
|
||||
|
||||
### Release-blocking native fixes
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------- |
|
||||
| [Lazy run-supervisor storage binding](https://github.com/BradGroux/veritas-kanban/issues/981) | [#982](https://github.com/BradGroux/veritas-kanban/pull/982) | Packaged SQLite startup initializes storage before lookup |
|
||||
| [Reject desktop updater downgrade metadata](https://github.com/BradGroux/veritas-kanban/issues/983) | [#984](https://github.com/BradGroux/veritas-kanban/pull/984) | v6 refuses published v5 metadata after stable channel selection |
|
||||
|
||||
### Release engineering
|
||||
|
||||
| Issue | Pull request | Outcome |
|
||||
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------- |
|
||||
| [Tier changed-file and full CI](https://github.com/BradGroux/veritas-kanban/issues/955) | [#957](https://github.com/BradGroux/veritas-kanban/pull/957) | Focused pull-request gates with explicit full-workspace lanes |
|
||||
| [Bounded desktop upgrade readiness](https://github.com/BradGroux/veritas-kanban/issues/927) | [#929](https://github.com/BradGroux/veritas-kanban/pull/929) | Version-matched installed-app startup and diagnostic readiness |
|
||||
|
||||
## Reviewed Provider Baselines
|
||||
|
||||
| Harness | Version/build | Deterministic posture | Credential-gated posture |
|
||||
| ------------------ | ----------------------------------------- | ----------------------- | -------------------------------------------------- |
|
||||
| Buzz Agent | v0.4.24 / `710ed9...`; `buzz-agent 0.1.0` | Pass: 7 files, 50 tests | Binary unavailable; no live claim |
|
||||
| Grok Build | v0.2.111 / `94172f2aa4e5` | Pass in workspace gate | Installed 0.2.22 is stale and unauthenticated |
|
||||
| Codex app-server | 0.145.0 / `25af12...` | Pass in workspace gate | Pass: exact authenticated 0.145.0, 1 smoke test |
|
||||
| Codex SDK | 0.144.3 | Pass in workspace gate | Deterministic evidence only |
|
||||
| Claude Code | 2.1.218 | Pass in workspace gate | Binary unavailable; no live claim |
|
||||
| GitHub Copilot CLI | 1.0.74 / `2b809c...` tag commit | Pass in workspace gate | Exact binary installed; authentication unavailable |
|
||||
| Hermes | v2026.7.7.2 | Pass in workspace gate | Binary unavailable; retained non-Certified tier |
|
||||
| OpenClaw | v2026.6.11 | Pass in workspace gate | Installed 2026.6.1 is stale; no live gateway claim |
|
||||
|
||||
No row may be changed to a live passing claim without exact command/runtime
|
||||
evidence. A skipped credential gate remains skipped, not passed.
|
||||
|
||||
### Observed isolated-runtime support tiers
|
||||
|
||||
The packaged 6.0.0 candidate returned the configured-profile rows below through
|
||||
the redacted `harness-compatibility-matrix/v1` status path from the copied
|
||||
v5.2.5 profile. The OpenClaw row applies the same normalized tier contract at
|
||||
the host executable boundary because that copied profile had no OpenClaw entry.
|
||||
Deterministic fixture results do not replace these live tiers.
|
||||
|
||||
| Profile | Observed tier | Certification | Evidence date | Runtime evidence |
|
||||
| ----------------------- | ------------- | ------------- | ------------- | -------------------------------------------------------------------- |
|
||||
| OpenAI Codex CLI | Configured | Not current | 2026-07-24 | `codex-cli 0.145.0`; executable and authentication detected |
|
||||
| OpenAI Codex SDK | Detected | Not current | 2026-07-24 | `codex-cli 0.145.0`; disabled profile |
|
||||
| OpenAI Codex app-server | Detected | Not current | 2026-07-24 | `codex-cli 0.145.0`; disabled profile |
|
||||
| GitHub Copilot CLI | Detected | Not current | 2026-07-24 | GitHub Copilot CLI 1.0.74; disabled profile; auth not inferred |
|
||||
| Grok Build | Detected | Not current | 2026-07-24 | 0.2.22 build `967574cb117`; disabled and older than reviewed 0.2.111 |
|
||||
| Claude Code | Degraded | Not current | 2026-07-24 | Executable not installed |
|
||||
| Buzz Agent | Degraded | Not current | 2026-07-24 | Executable not installed |
|
||||
| Hermes | Degraded | Not current | 2026-07-24 | Executable not installed |
|
||||
| OpenClaw | Detected | Not current | 2026-07-24 | 2026.6.1 executable found; no enabled profile or live gateway claim |
|
||||
|
||||
No profile reported Certified because persisted certification evidence was
|
||||
not current in this isolated profile. OpenClaw is Detected at the host boundary
|
||||
because the executable exists while no profile is enabled; its stale
|
||||
2026.6.1 build is not a live gateway claim. The exact authenticated app-server
|
||||
smoke remains separate command evidence, not a support-tier mutation.
|
||||
|
||||
## Verification Matrix
|
||||
|
||||
Results are filled from the clean release candidate. Counts and elapsed time are
|
||||
recorded when the tool reports them.
|
||||
|
||||
| Gate | Environment | Result | Evidence or limitation |
|
||||
| --------------------------------------------------------- | ------------------------------- | ------- | --------------------------------------------------------------------------------- |
|
||||
| Toolchain versions | macOS 26.5.2 arm64 | Pass | Node 26.5.0; pnpm 11.1.1; Git 2.55.0 |
|
||||
| `pnpm install --frozen-lockfile` | clean release worktree | Pass | 1,116 packages in 4.8 s; lockfile pinned to pnpm 11.1.1 |
|
||||
| `pnpm check:pnpm-settings` | clean release worktree | Pass | Package manager and instruction versions agree |
|
||||
| `pnpm audit --prod --audit-level=high` | production dependencies | Pass | 0 high/critical; 1 low and 1 moderate retained |
|
||||
| `pnpm lint` and `pnpm lint:budget` | all tracked source/docs | Pass | 0 errors; 595 warnings within the 600-warning budget |
|
||||
| `pnpm qa:mantine` | built web bundle | Pass | Initial JS 238.9 KiB gzip; CSS 51.4 KiB gzip |
|
||||
| `pnpm typecheck` | all workspaces | Pass | 11.97 s |
|
||||
| `pnpm build` | all workspaces | Pass | 16.53 s |
|
||||
| `pnpm test:unit` | sequential workspaces | Pass | 3,217 passed, 4 skipped; 329 files; 48.74 s |
|
||||
| `pnpm test:e2e` | Playwright matrix | Pass | 36/36; Chromium, mobile Chromium, mobile WebKit; 79.76 s |
|
||||
| `pnpm smoke:cli-mcp` | release output | Limited | Static versions/builds pass; live read/write skipped without test key |
|
||||
| `pnpm test:buzz:compatibility` | credential-free fixtures | Pass | 7 files, 50/50 tests; 4.30 s Vitest |
|
||||
| Provider/lifecycle/approval/MCP/credential suites | credential-free fixtures | Pass | Focused release integration set: 4 files, 83/83 tests, 2.76 s |
|
||||
| `pnpm desktop:test` | macOS arm64 | Pass | 14 files, 61 tests, 344 ms |
|
||||
| `pnpm desktop:build` | macOS arm64 | Pass | 1.20 s; Electron artifact check 4/4 |
|
||||
| `pnpm desktop:check:electron-artifacts` | fresh desktop output | Pass | Main and preload artifacts accepted |
|
||||
| `pnpm desktop:smoke:mac:local` | isolated unsigned package | Pass | 25.28 s |
|
||||
| `pnpm desktop:package:mac:unsigned` | macOS arm64 | Pass | Final post-blocker DMG/ZIP package in 57.49 s |
|
||||
| v5.2.5 populated-profile upgrade | isolated copy | Pass | Matching counts, integrity, owner summary, profile; registry absence preserved |
|
||||
| v5.2.5 registry compatibility | exact-format deterministic test | Pass | 1/1; identity, provider, capabilities, metadata, status, and session preserved |
|
||||
| File-backed migration and restore | isolated deterministic fixture | Pass | 7/7; tasks, config, workflows, chat, telemetry, backup, and restore |
|
||||
| In-app Browser runtime | isolated server/web | Pass | First run, login, provider/Buzz settings, themes, palette/focus; 0 console errors |
|
||||
| Local browser automation | Playwright matrix | Pass | 36-route/interaction E2E matrix |
|
||||
| Native desktop runtime | isolated profile | Pass | One instance, window, health, menus, reopen, update, clean quit |
|
||||
| `pnpm validate:release -- --version 6.0.0` | clean release worktree | Pass | 0.60 s |
|
||||
| `pnpm validate:release -- --version 6.0.0 --docker-build` | production target | Pass | 116.39 s; `veritas-kanban:validate-6.0.0` |
|
||||
| Focused standards review | diff from `398fe7f6...` | Pass | GPT-5.6 standards axis; no remaining findings; cross-model review waived |
|
||||
| Focused spec/security claim review | release tracker and goal prompt | Pass | GPT-5.6 spec axis; findings corrected; secret/security scan passed |
|
||||
|
||||
### Accepted browser verification split
|
||||
|
||||
On 2026-07-24 the maintainer directed a sustainable verification cadence:
|
||||
focused behavior tests for issue pull requests, changed-file CI per pull
|
||||
request, and one complete workspace suite at the final release gate. Applying
|
||||
that direction, the in-app Browser pass covered rendered and interactive
|
||||
behavior that benefits from manual inspection: first run, login, provider and
|
||||
Buzz Settings, degraded states, dark/light themes, command-palette focus,
|
||||
labels, and console health. Stateful task launch, causal events, approvals,
|
||||
cancellation, Buzz replay, MCP, worktrees, telemetry, completion, compact
|
||||
layout, and recoverable errors remained in the passing deterministic and E2E
|
||||
automation instead of being duplicated manually in the in-app Browser.
|
||||
|
||||
This is an explicit release-evidence split, not a claim that those scripted
|
||||
flows were manually clicked in the in-app Browser.
|
||||
|
||||
## Migration And Data Preservation
|
||||
|
||||
Completed isolated drill:
|
||||
|
||||
- source: latest signed v5.2.5 desktop workspace copy under an isolated
|
||||
`codex-v525-smoke` profile;
|
||||
- no database writer before copy or after final quit;
|
||||
- `PRAGMA quick_check=ok` before and after;
|
||||
- matching counts before and after: 2 tasks, 3 Squad Chat messages,
|
||||
1 telemetry event, 1 workflow definition, 1 workflow run,
|
||||
1 app-config document, and 1 user;
|
||||
- agent registry absent before and after; no implicit provider enablement or
|
||||
OpenClaw fallback;
|
||||
- a separate automated fixture using the exact v5.2.5 registry shape preserved
|
||||
agent identity, provider, capabilities, metadata, status, and session
|
||||
reference in 1/1 focused test;
|
||||
- the file-backed portability suite passed 7/7 for tasks, configuration,
|
||||
workflows, chats, telemetry, backup, failure recovery, and restore;
|
||||
- onboarding detected and summarized the populated database, v6 health
|
||||
reported 6.0.0, the packaged renderer opened, provider and Buzz settings
|
||||
rendered, reopen/single-instance/menu/update/quit checks passed;
|
||||
- the final updater check reported 5.2.5 as older with downgrade disallowed,
|
||||
and download/install actions remained disabled;
|
||||
- rollback remains restore-first: retain the stopped-writer v5.2.5 backup
|
||||
because destructive schema down-migration is not promised.
|
||||
|
||||
The signed profile contained no agent registry, so the native drill proves that
|
||||
absence remains absence while the exact-format automated fixture covers a
|
||||
populated v5 registry. The signed fixture's two task rows were deliberately
|
||||
minimal direct-SQL migration records, so the onboarding count is authoritative
|
||||
evidence; their incomplete legacy JSON is not claimed as a rendered task-card
|
||||
test.
|
||||
|
||||
No private data, credential value, raw provider conversation, or unrestricted
|
||||
runtime profile is retained in this packet.
|
||||
|
||||
## 6.1.2 Publication Evidence
|
||||
|
||||
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 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 |
|
||||
|
||||
Source CI, tag creation, asset upload, signing, notarization, downloaded runtime,
|
||||
and Homebrew availability are independent gates.
|
||||
|
||||
## Deferred v6.x Work And Accepted Limits
|
||||
|
||||
- [Run-scoped egress gateway](https://github.com/BradGroux/veritas-kanban/issues/855)
|
||||
is deferred for fine-grained HTTP method/path/domain enforcement. Required
|
||||
rules unsupported by current provider evidence already block launch.
|
||||
- Grok Build and GitHub Copilot CLI retain documented source-provenance limits.
|
||||
- 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
|
||||
remains a documented text contract.
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
# Veritas Kanban 6.1.2 Release Notes
|
||||
|
||||
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.
|
||||
|
||||
> 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.
|
||||
|
||||
## Audit Outcomes And Traceability
|
||||
|
||||
| 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 |
|
||||
|
||||
## Persistence And Runtime Paths
|
||||
|
||||
`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.
|
||||
|
||||
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.
|
||||
|
||||
## Provider Runtime And Frontend API
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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 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).
|
||||
|
||||
## Documentation And Evidence
|
||||
|
||||
- [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,239 +0,0 @@
|
|||
# Veritas Kanban v6 Upgrade, Install, Remote, And Admin Guide
|
||||
|
||||
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-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
|
||||
|
||||
Install the signed/notarized stable app:
|
||||
|
||||
```bash
|
||||
brew tap BradGroux/tap
|
||||
brew install --cask veritas-kanban
|
||||
```
|
||||
|
||||
Manual installation uses
|
||||
`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.
|
||||
|
||||
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.1.2.
|
||||
4. Create a governed backup before adding external credentials or relay
|
||||
mappings.
|
||||
|
||||
The default desktop workspace remains:
|
||||
|
||||
```text
|
||||
~/Library/Application Support/@veritas-kanban/desktop/profiles/default/workspaces/local/
|
||||
```
|
||||
|
||||
Keep the authoritative SQLite database on the normal local Application Support
|
||||
filesystem. NAS, SMB, NFS, FUSE, iCloud, Dropbox, OneDrive, and other
|
||||
synchronized/remote filesystems are unsupported database locations.
|
||||
|
||||
## v5 To v6 Upgrade
|
||||
|
||||
The supported source is the latest signed v5.2.5 desktop release or an
|
||||
equivalent v5.2.5 self-hosted workspace.
|
||||
|
||||
1. In v5.2.5, create a governed export/backup in Settings -> Maintenance and
|
||||
confirm the report completed.
|
||||
2. Record representative counts for tasks, Squad Chat messages, telemetry,
|
||||
workflow definitions/runs, provider profiles, and agent registry entries.
|
||||
3. Pause any heartbeat, LaunchAgent, or watchdog that can reopen Veritas.
|
||||
4. Quit every desktop/source server writer. Confirm the desktop process and
|
||||
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.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.1.2
|
||||
pnpm desktop:wait:ready -- --expected-version "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
9. Verify `PRAGMA quick_check`, the representative counts, owner metadata,
|
||||
provider profile normalization, board/search/task detail, workflows, Squad
|
||||
Chat, Maintenance, and `/api/health.version`.
|
||||
10. Run `vk doctor --json`. Review each enabled profile's support tier,
|
||||
version/build, authentication posture, capability evidence, and
|
||||
remediation. Do not assume a v5 profile remains Certified.
|
||||
11. Resume automation only after the app and health versions match and the
|
||||
board is accepted.
|
||||
|
||||
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
|
||||
and command identity match.
|
||||
- Provider-less, ambiguous, adapter/profile-mismatched, or unknown records do
|
||||
not silently become OpenClaw.
|
||||
- New Buzz, Grok Build, GitHub Copilot CLI, Codex app-server, Claude Code, and
|
||||
generic ACP profiles are disabled by default.
|
||||
- Unknown provider builds invalidate certification until the reviewed profile
|
||||
and deterministic fixture evidence are updated.
|
||||
- The former Claude Code permission-bypass default is removed. Do not restore
|
||||
it through custom arguments.
|
||||
|
||||
## Routine Mac Desktop Upgrade
|
||||
|
||||
For any later v6 patch:
|
||||
|
||||
1. Create and verify a governed backup.
|
||||
2. Pause auto-reopen automation.
|
||||
3. Quit Veritas and confirm its app process and port are stopped.
|
||||
4. Run:
|
||||
|
||||
```bash
|
||||
brew update
|
||||
brew upgrade --cask bradgroux/tap/veritas-kanban
|
||||
brew list --cask --versions veritas-kanban
|
||||
```
|
||||
|
||||
5. Read `CFBundleShortVersionString`, launch the app, and use
|
||||
`pnpm desktop:wait:ready -- --expected-version <version>`.
|
||||
6. Verify the listener belongs to the packaged application, then check the
|
||||
board and Maintenance.
|
||||
7. Resume automation only after exact-version readiness passes.
|
||||
|
||||
Homebrew replacement does not launch the app or wait for its bundled server.
|
||||
Do not substitute an immediate `curl` or fixed sleep for the readiness gate.
|
||||
|
||||
## Harness Installation And Authentication
|
||||
|
||||
Veritas never installs a third-party harness. Install the exact tested build
|
||||
from its official distribution, authenticate using the provider's supported
|
||||
flow, then enable its built-in profile in Settings -> Agents.
|
||||
|
||||
| Harness | Install/verify | Authentication names accepted by the v6 profile | Safe default |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Buzz Agent | Build/install Buzz v0.4.24 `buzz-agent`; verify ACP identity `buzz-agent 0.1.0`. | `ANTHROPIC_API_KEY`, `OPENAI_COMPAT_API_KEY`, or `DATABRICKS_TOKEN`. | Disabled; no resume; only the system-owned `veritas-run` MCP bridge. |
|
||||
| Grok Build | Install v0.2.111; run `grok --version`. | Existing `GROK_HOME`, `XAI_API_KEY`, `GROK_CODE_XAI_API_KEY`, or `GROK_DEPLOYMENT_KEY`. | Disabled; dedicated `agent --no-leader ... stdio`; restrictive policy only. |
|
||||
| Codex CLI/app-server | Install the reviewed Codex CLI; run `codex login status`. App-server certification requires 0.145.0. | Existing Codex login or `OPENAI_API_KEY` where supported. | Workspace-write task sandbox; app-server plugins, apps, hooks, browser/computer tools, remote control, and unsandboxed shell command disabled. |
|
||||
| Codex SDK | Installed with Veritas as `@openai/codex-sdk 0.144.3`. | Existing Codex login or `OPENAI_API_KEY`. | Shared manifest, sandbox, tool, event, and completion controls. |
|
||||
| Claude Code | Install 2.1.218; run `claude --version` and `claude auth status`. | `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, Foundry, bounded Bedrock keys, or explicit Vertex credential file reference. | Disabled; `--bare`, `dontAsk`, strict MCP, credential scrubbing; no permission bypass. |
|
||||
| GitHub Copilot CLI | Install v1.0.74; run `copilot --version` and provider login. | `COPILOT_GITHUB_TOKEN`, `COPILOT_PROVIDER_API_KEY`, `COPILOT_PROVIDER_BEARER_TOKEN`, `GH_TOKEN`, or `GITHUB_TOKEN`. | Disabled; ACP public preview, remote/plugins/custom instructions/experimental features off. |
|
||||
| Hermes Agent | Install v2026.7.7.2; run `hermes --version`. | `HERMES_API_KEY` or the documented model-provider key. | Disabled; one-shot scripted execution. |
|
||||
| OpenClaw | Run v2026.6.11 gateway and verify tool policy. | `OPENCLAW_GATEWAY_TOKEN` when required. | Explicit gateway URL; `sessions_spawn` and `sessions_send` must be allowed. |
|
||||
|
||||
Credential values belong in the operator environment or the platform's login
|
||||
store. Settings, docs, task bodies, screenshots, logs, issue comments, and
|
||||
support packets must contain only environment key names or opaque references.
|
||||
|
||||
After setup:
|
||||
|
||||
```bash
|
||||
vk doctor --json
|
||||
```
|
||||
|
||||
Fix Degraded or Unsupported evidence before dispatch. Detected means the
|
||||
executable exists but the profile is disabled. Configured is usable adapter
|
||||
evidence without current certification. Certified requires matching
|
||||
deterministic evidence; live authentication and quota remain separate facts.
|
||||
|
||||
## Buzz Setup
|
||||
|
||||
Buzz communication and Buzz Agent execution are independent:
|
||||
|
||||
- Configure the relay, public key, `env:BUZZ_PRIVATE_KEY`, optional
|
||||
`env:BUZZ_AUTH_TAG`, and one explicit channel mapping under Settings ->
|
||||
Notifications.
|
||||
- Run the read-only compatibility probe before enabling delivery.
|
||||
- Import public persona/team definitions only through preview and an explicit
|
||||
create, link, refresh, or skip action. Imports remain disabled and never
|
||||
launch a provider.
|
||||
- Create only bounded root `message.posted` workflow triggers. Replies, edits,
|
||||
deletes, reactions, echoes, replays, and predicate mismatches do not start a
|
||||
workflow.
|
||||
- Configure the separate disabled `buzz-agent` profile only when ACP task
|
||||
execution is required.
|
||||
|
||||
See [Buzz Integration](BUZZ-INTEGRATION.md) for API examples, channel mapping,
|
||||
Nostr identity, trigger rules, replay, and rollback.
|
||||
|
||||
## Remote And Multi-User Administration
|
||||
|
||||
Remote mode remains a trusted-host setup. Serve web, `/api`, `/ws`, health
|
||||
routes, manifest, service worker, and assets from one HTTPS origin. Keep
|
||||
`VERITAS_AUTH_ENABLED=true` and
|
||||
`VERITAS_AUTH_LOCALHOST_BYPASS=false`.
|
||||
|
||||
Use scoped device sessions or API tokens for remote browsers, CLI, MCP, agents,
|
||||
and automation. Never share owner/admin credentials with providers. Approval
|
||||
reviewer identity, authentication freshness, workspace membership, and exact
|
||||
action binding are enforced server-side.
|
||||
|
||||
Use [Self-Hosting Guide](guides/SELF_HOST.md), the v5 remote security ADR, and
|
||||
[Identity And RBAC](IDENTITY-RBAC.md) for unchanged remote/server foundations.
|
||||
|
||||
## Backup And Recovery
|
||||
|
||||
Before upgrade, provider-profile changes, or Buzz setup:
|
||||
|
||||
1. Stop or quiesce every writer.
|
||||
2. Create a governed backup/export and retain its report.
|
||||
3. Copy the complete workspace after SQLite checkpoint and integrity checks.
|
||||
4. Store backups away from the live database.
|
||||
5. Test restore into an isolated profile, never over a running workspace.
|
||||
|
||||
Rollback from v6 means reinstalling the prior signed app only when its data
|
||||
contracts remain compatible. Otherwise restore the stopped-writer v5.2.5
|
||||
backup. There is no promise of destructive schema down-migration.
|
||||
|
||||
For Buzz rollback, disable trigger rules first, then the channel mapping, then
|
||||
the adapter. Audit records and local Squad Chat content remain. Veritas never
|
||||
deletes remote Buzz events or writes imported definitions back.
|
||||
|
||||
For a provider rollback, disable the changed profile and restore the reviewed
|
||||
profile configuration from backup. Do not lower support evidence, bypass
|
||||
permissions, or route an ambiguous record through another adapter.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Use:
|
||||
|
||||
```bash
|
||||
vk doctor --json
|
||||
curl http://127.0.0.1:3001/api/health
|
||||
curl http://127.0.0.1:3001/health/ready
|
||||
```
|
||||
|
||||
Compare app, server, CLI, MCP, and updater versions. Review redacted
|
||||
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.
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
# Veritas Kanban v6 Visual Tour
|
||||
|
||||
This tour points to release-safe v6 runtime views for provider support, Buzz
|
||||
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.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
|
||||
|
||||
Settings -> Agents shows the normalized support tier, installed version/build,
|
||||
configuration readiness, certification freshness, limitations, and
|
||||
remediation from the same `harness-support-profile/v1` and
|
||||
`harness-compatibility-matrix/v1` records used by `vk doctor --json`, API
|
||||
diagnostics, dispatch, and telemetry.
|
||||
|
||||
The release capture must use dummy profile names and contain no login state,
|
||||
environment values, provider output, private paths, or credentials.
|
||||
|
||||

|
||||
|
||||
Expected visible behavior:
|
||||
|
||||
- Buzz, Grok Build, Codex app-server, Claude Code, and GitHub Copilot CLI show
|
||||
their reviewed exact build and source-availability caveat.
|
||||
- Disabled installed profiles read Detected rather than Certified.
|
||||
- Stale/unknown builds read Degraded or Unsupported with an actionable reason.
|
||||
- Provider controls unsupported by the current manifest are not presented as
|
||||
silently available.
|
||||
|
||||
## Buzz Integration
|
||||
|
||||
Settings -> Notifications -> Buzz Connection keeps relay communication
|
||||
separate from Buzz Agent execution. The connection view exposes reference-only
|
||||
URLs, public identity, environment-variable references, compatibility facets,
|
||||
one-channel mappings, definition preview/import, trigger rules, and bounded
|
||||
audit state.
|
||||
|
||||

|
||||
|
||||
Expected visible behavior:
|
||||
|
||||
- private keys and auth tags appear only as environment reference names;
|
||||
- compatibility failure disables delivery;
|
||||
- persona/team import is preview-first and creates disabled objects;
|
||||
- workflow triggers accept root `message.posted` only;
|
||||
- replay, reply, edit, delete, reaction, echo, and disabled-rule dispositions
|
||||
are visible without launching duplicate workflow runs.
|
||||
|
||||
## Approval And Run Evidence
|
||||
|
||||
Task Detail and shared-run views project the causal `run-event/v1` journal,
|
||||
exact-action approval requests, conversation lifecycle controls, tool/MCP
|
||||
evidence, worktree/launch identity, usage, artifacts, and authoritative
|
||||
completion result.
|
||||
|
||||
No public-safe approval was active in the isolated release profile. The text
|
||||
contract below is retained instead of fabricating a passing approval state.
|
||||
|
||||
Expected visible behavior:
|
||||
|
||||
- approval copy identifies the exact action and risk without showing secret
|
||||
values;
|
||||
- stale, expired, changed, cancelled, or already-decided requests cannot be
|
||||
approved;
|
||||
- resume, steer, fork, compact, archive, interrupt, and close appear only when
|
||||
current provider evidence supports them;
|
||||
- reconnect replays causal events by cursor without duplicating completion;
|
||||
- mobile-safe questions remain separate from filesystem, command, network,
|
||||
permission, and MCP approval.
|
||||
|
||||
## Retained v5 Shell Views
|
||||
|
||||
The v6 harness work does not replace the established board, desktop shell,
|
||||
Workbench, Squad Chat, Maintenance, or mobile/PWA layout. These release-safe
|
||||
dummy captures remain applicable:
|
||||
|
||||
| Surface | Retained capture |
|
||||
| ---------------------------------- | ---------------------------------------------------------------- |
|
||||
| Board to workflow |  |
|
||||
| Desktop shell |  |
|
||||
| Agent provider settings foundation |  |
|
||||
| Workbench |  |
|
||||
| Squad Chat |  |
|
||||
| Task work view |  |
|
||||
| Maintenance |  |
|
||||
|
||||
## Capture Rules
|
||||
|
||||
Before publication, retain only verified v6 screenshots captured from an
|
||||
isolated runtime without private data. If a state cannot be safely produced,
|
||||
retain the text contract and record the missing visual evidence in the release
|
||||
packet rather than fabricating a screenshot.
|
||||
|
||||
Capture desktop dark/light and compact widths where the state materially
|
||||
changes. Verify keyboard focus, labels, contrast, and recoverable error copy.
|
||||
Never capture credentials, private provider conversations, relay events,
|
||||
tokens, local user paths, or unrestricted diagnostics.
|
||||
|
|
@ -9,10 +9,6 @@ development server, disable an old watchdog, or update automation credentials.
|
|||
|
||||
## Choose The Correct Path
|
||||
|
||||
Run the inventory on the Mac that will own the desktop workspace. Record each
|
||||
host separately; counts reported by another Mac, server, or backup are context,
|
||||
not proof of the target Mac's active database.
|
||||
|
||||
Check the desktop database before importing anything. Quit the app first so the
|
||||
direct inspection does not race the authoritative writer:
|
||||
|
||||
|
|
@ -92,72 +88,6 @@ After cutover, the process tree should resolve through:
|
|||
|
||||
Only one VK server should accept active writes during the cutover.
|
||||
|
||||
## Wait For The Desktop Server
|
||||
|
||||
`open -a` asks LaunchServices to open the app and returns before Electron and
|
||||
the bundled server are necessarily ready. An immediate failed `curl` is not
|
||||
proof that startup failed. Do not replace the readiness check with a fixed
|
||||
`sleep`; startup time varies by host.
|
||||
|
||||
From a Veritas Kanban checkout, launch the app and wait up to 30 seconds for the
|
||||
exact installed version:
|
||||
|
||||
```bash
|
||||
EXPECTED_VERSION="$(defaults read "/Applications/Veritas Kanban.app/Contents/Info" CFBundleShortVersionString)"
|
||||
open -a "Veritas Kanban"
|
||||
pnpm desktop:wait:ready -- --expected-version "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
Homebrew-only operators and agents can use the built-in macOS tools instead:
|
||||
|
||||
```bash
|
||||
EXPECTED_VERSION="$(defaults read "/Applications/Veritas Kanban.app/Contents/Info" CFBundleShortVersionString)"
|
||||
HEALTH_URL="http://127.0.0.1:3001/api/health"
|
||||
HEALTH_JSON=""
|
||||
DESKTOP_READY=0
|
||||
DESKTOP_DEADLINE=$((SECONDS + 30))
|
||||
|
||||
open -a "Veritas Kanban"
|
||||
|
||||
while ((SECONDS < DESKTOP_DEADLINE)); do
|
||||
REMAINING_SECONDS=$((DESKTOP_DEADLINE - SECONDS))
|
||||
|
||||
if HEALTH_JSON="$(curl -fsS --max-time "$REMAINING_SECONDS" "$HEALTH_URL" 2>/dev/null)"; then
|
||||
HEALTH_OK="$(printf '%s' "$HEALTH_JSON" | plutil -extract ok raw -o - - 2>/dev/null || true)"
|
||||
HEALTH_SERVICE="$(printf '%s' "$HEALTH_JSON" | plutil -extract service raw -o - - 2>/dev/null || true)"
|
||||
HEALTH_VERSION="$(printf '%s' "$HEALTH_JSON" | plutil -extract version raw -o - - 2>/dev/null || true)"
|
||||
PORT_PID="$(lsof -tiTCP:3001 -sTCP:LISTEN 2>/dev/null || true)"
|
||||
PORT_COMMAND="$(ps -o command= -p "$PORT_PID" 2>/dev/null || true)"
|
||||
|
||||
if test "$HEALTH_OK" = "true" &&
|
||||
test "$HEALTH_SERVICE" = "veritas-kanban" &&
|
||||
test "$HEALTH_VERSION" = "$EXPECTED_VERSION" &&
|
||||
printf '%s' "$PORT_COMMAND" | grep -Fq "/Applications/Veritas Kanban.app/Contents/"; then
|
||||
printf '%s\n' "$HEALTH_JSON"
|
||||
DESKTOP_READY=1
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if test "$DESKTOP_READY" -ne 1; then
|
||||
echo "Veritas Kanban $EXPECTED_VERSION did not become ready at $HEALTH_URL." >&2
|
||||
lsof -nP -iTCP:3001 -sTCP:LISTEN || true
|
||||
ps -o pid,ppid,pgid,command -p "$(lsof -tiTCP:3001 -sTCP:LISTEN)" 2>/dev/null || true
|
||||
tail -n 80 "$HOME/Library/Application Support/@veritas-kanban/desktop/profiles/default/workspaces/local/logs/server.log" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
The version and listener-owner checks prevent a stale source server or an older
|
||||
desktop process from satisfying upgrade verification. If another process owns
|
||||
`3001`, the desktop app may select another loopback port for its renderer. That
|
||||
fallback can keep the UI usable, but it is not an accepted migration cutover:
|
||||
stop the competing writer, relaunch the desktop app, and prove that the packaged
|
||||
server owns `3001`.
|
||||
|
||||
## Already Migrated Desktop Data
|
||||
|
||||
Use this path when a migration tool or operator has already populated
|
||||
|
|
@ -200,9 +130,6 @@ Use this path when a migration tool or operator has already populated
|
|||
open -a "Veritas Kanban"
|
||||
```
|
||||
|
||||
Then run **Wait For The Desktop Server**. Do not issue one immediate health
|
||||
request after `open -a`.
|
||||
|
||||
4. On first launch, choose **Use Existing Data**. The setup screen displays
|
||||
representative record counts read from the active desktop SQLite database.
|
||||
5. Select **Secure Existing Data**, create the admin password, and save the
|
||||
|
|
@ -468,20 +395,18 @@ Preserve:
|
|||
open -a "Veritas Kanban"
|
||||
```
|
||||
|
||||
Run **Wait For The Desktop Server** before treating the new database as active.
|
||||
|
||||
Choose **Use Existing Data**, then **Secure Existing Data**. Create the admin
|
||||
password and save the recovery key. Do not choose **Board Only** or **Restore
|
||||
Backup** for the staged database.
|
||||
|
||||
## Verify The Cutover
|
||||
|
||||
After **Wait For The Desktop Server** succeeds, confirm the packaged app owns
|
||||
`3001`:
|
||||
Confirm the packaged app owns `3001`:
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:3001 -sTCP:LISTEN
|
||||
ps -o pid,ppid,pgid,command -p "$(lsof -tiTCP:3001 -sTCP:LISTEN)"
|
||||
curl -fsS http://127.0.0.1:3001/api/health | jq .
|
||||
```
|
||||
|
||||
Verify counts only after closing the app or by using the app/API. Do not use
|
||||
|
|
@ -530,20 +455,14 @@ export VK_API_KEY="paste-scoped-token-here"
|
|||
Store long-lived tokens in the operator's normal secret manager. Do not
|
||||
hard-code owner/admin keys into repo scripts.
|
||||
|
||||
Heartbeat or service recovery should reopen the desktop app and wait for the
|
||||
readiness gate:
|
||||
Heartbeat or service recovery should reopen the desktop app:
|
||||
|
||||
```bash
|
||||
EXPECTED_VERSION="$(defaults read "/Applications/Veritas Kanban.app/Contents/Info" CFBundleShortVersionString)"
|
||||
open -a "Veritas Kanban"
|
||||
pnpm desktop:wait:ready -- --expected-version "$EXPECTED_VERSION"
|
||||
```
|
||||
|
||||
Use the Homebrew-only readiness block above when the automation does not have a
|
||||
Veritas Kanban checkout. Pause any heartbeat or supervisor before quitting the
|
||||
app for migration or upgrade. Resume it only after exact-version readiness
|
||||
succeeds. It should not restart the old source checkout unless the operator
|
||||
explicitly wants development mode.
|
||||
It should not restart the old source checkout unless the operator explicitly
|
||||
wants development mode.
|
||||
|
||||
## Rollback
|
||||
|
||||
|
|
@ -576,13 +495,6 @@ Check who owns `3001`. If it is `node`, `tsx`, `vite`, or `pnpm dev` from the
|
|||
source checkout, the desktop app is not authoritative. Stop that process and
|
||||
its watchdog, then relaunch the app.
|
||||
|
||||
### `open -a` returns but port 3001 refuses connections
|
||||
|
||||
Run **Wait For The Desktop Server**. A refusal during the first few seconds is a
|
||||
normal asynchronous launch window. If the bounded wait times out, inspect the
|
||||
reported port owner and desktop server log. Do not restart the old source server
|
||||
as a workaround.
|
||||
|
||||
### APIs return `AUTH_REQUIRED`
|
||||
|
||||
This is expected in packaged mode. Use the desktop UI session or a scoped token
|
||||
|
|
|
|||
|
|
@ -142,20 +142,8 @@ 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. 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.
|
||||
3. Click on "Hello World Workflow"
|
||||
4. You'll see your active run with real-time step progress
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -1217,19 +1205,7 @@ If the server crashes mid-workflow, runs can be recovered:
|
|||
curl -X POST http://localhost:3001/api/workflow-runs/run_XYZ/resume
|
||||
```
|
||||
|
||||
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.
|
||||
> **📝 Note**: Automatic recovery is planned for a future release.
|
||||
|
||||
### Performance Issues
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue