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:
@@ -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(),
|
||||
});
|
||||
|
||||
|
||||
@@ -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(' | ||||