docs(agentic-teams): document the new resources, and make the webhook reachable

Five resources shipped with no documentation and one documented lie:
docs/inference-tasks.md still told people `mcpctl get tasks` lists the
inference queue. Work items own that name now.

Adds docs/epics.md (the Epic -> EpicTask -> Task hierarchy, the state machine,
risk-scaled review gates, the accept-merges gate, and reconciliation),
docs/gitproviders.md (forge registration, discovery/import, merge-on-accept,
post-merge reopen) and docs/agent-authority.md (the three run modes, and
plainly which two are not implemented).

Two things found while writing them, both fixed rather than filed:

- `webhookSecretKey` was read by the webhook route but writable from NOWHERE
  -- absent from the create schema, update schema, service, CLI and apply
  spec. Since the endpoint fails closed on a null key, post-merge CI failures
  could never reopen their task; the board would keep claiming `merged` for
  work that broke the build. Adds --webhook-secret-key, the schema fields and
  the apply key, with tests covering persist / omit / explicit-null /
  reject-empty. Omission deliberately does not clear the column, so an
  unrelated edit cannot silently disarm the webhook.

- src/cli/src/commands/create.ts contained a literal NUL byte inside a string
  literal (a binary-file check written with the raw character rather than an escape). Identical behaviour, but it made the whole file read as
  binary, so `grep -I` skipped it entirely and 1120 lines of CLI were
  invisible to every text search. Replaced with the escape.

The Epic write path (criteria/status/tradeoffs) is API-only -- documented as
such rather than papered over, and filed as task 180.

Full suite 2612 green; lint at baseline 794/77.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4wNHWf7xSwnZCWpJcyv9p
This commit is contained in:
Michal
2026-08-16 00:14:58 +01:00
parent 2a85447fac
commit 6d38953a96
14 changed files with 587 additions and 6 deletions

View File

@@ -232,6 +232,10 @@ const GitProviderSpecSchema = z.object({
type: z.string().optional(),
description: z.string().optional(),
credRef: z.object({ name: z.string(), key: z.string() }).nullable().optional(),
// Key in the credential Secret holding the webhook signing secret. The
// webhook endpoint fails closed without it, so a provider that omits this
// silently never reopens tasks whose merge broke CI.
webhookSecretKey: z.string().nullable().optional(),
skipProbe: z.boolean().optional(),
});

View File

@@ -131,11 +131,13 @@ export function createCreateCommand(deps: CreateCommandDeps): Command {
.option('--description <text>', 'Description')
.option('--secret <name>', 'Secret holding the API token')
.option('--key <key>', 'Key within that secret', 'TOKEN')
.option('--webhook-secret-key <key>', 'Key in that secret holding the webhook signing secret; without it incoming webhooks are refused')
.option('--skip-probe', 'Skip the credential check at registration time')
.action(async (name: string, opts) => {
const body: Record<string, unknown> = { name, url: opts.url, type: opts.type };
if (opts.description !== undefined) body.description = opts.description;
if (opts.secret !== undefined) body.credRef = { name: opts.secret, key: opts.key };
if (opts.webhookSecretKey !== undefined) body.webhookSecretKey = opts.webhookSecretKey;
if (opts.skipProbe === true) body.skipProbe = true;
const created = await client.post<{ name: string; type: string; url: string }>('/api/v1/gitproviders', body);
log(`Created gitprovider '${created.name}' (${created.type} @ ${created.url})`);
@@ -1019,7 +1021,7 @@ export function createCreateCommand(deps: CreateCommandDeps): Command {
const buf = await fs.readFile(full);
// Reject non-UTF8 — v1 is text-only.
const text = buf.toString('utf-8');
if (text.includes('')) {
if (text.includes('\u0000')) {
throw new Error(`File ${rel} contains a null byte; binaries aren't supported in v1`);
}
files[rel] = text;