agentic-teams: epics, tasks, git forges, agent authority #139
@@ -3261,6 +3261,146 @@
|
||||
"status": "done",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-08T15:42:14.594Z"
|
||||
},
|
||||
{
|
||||
"id": "164",
|
||||
"title": "RBAC enforcement tests for the agentic-teams resources",
|
||||
"description": "The permissions surface changed (5 new resources + the tasks namespace split) with no test proving the gating actually works.",
|
||||
"details": "Verified missing: no test references `epics` or `gitproviders` outside the backfill test.\n\n- Assert mapUrlToPermission returns the right resource+action for every new route, including the non-CRUD verbs (transition/accept/reject → run:tasks, import/probe → run:gitproviders — note these are NOT currently special-cased and default to create, which may be wrong).\n- Assert a caller with only view:epics cannot POST /api/v1/epics.\n- Assert name-scoped grants (view:epics:<name>) resolve through nameResolvers.\n- Assert list filtering via getAllowedScope actually narrows /api/v1/epics.\n- Assert a pre-split view:tasks grant authorizes /api/v1/inference-tasks after the backfill and NOT /api/v1/tasks.",
|
||||
"testStrategy": "src/mcpd/tests/rbac-agentic-teams.test.ts, modelled on rbac-name-scope-integration.test.ts.",
|
||||
"priority": "high",
|
||||
"dependencies": [
|
||||
"134"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "165",
|
||||
"title": "Back up the agentic-teams resources (silent data loss today)",
|
||||
"description": "BACKUP_KINDS does not include epics/epictasks/tasks/repos/gitproviders, so git-backup never captures them and a restore loses all of it.",
|
||||
"details": "Verified: BACKUP_KINDS = [server, secret, project, user, group, rbac, prompt, skill, template].\n\n- Add the new kinds to BACKUP_KINDS + APPLY_ORDER (dependency order: gitprovider → repo → epic → epictask → task) + kindMap in yaml-serializer.ts, plus the export loop and serializeResource in git-backup.service.ts.\n- Wire main.ts kindFromSegment / importResource / deleteResource.\n- PRE-EXISTING BUG found while checking: `skill` is missing from importResource and deleteResource in main.ts, so skills are exported to git but never restored from it. Fix in the same pass.\n- Credentials must NOT be serialized: back up credRef (secret name + key), never the resolved value.",
|
||||
"testStrategy": "Extend src/mcpd/tests/yaml-serializer.test.ts; assert a round-trip export→import for each new kind and that no serialized blob contains a resolved secret value.",
|
||||
"priority": "high",
|
||||
"dependencies": [
|
||||
"135"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "166",
|
||||
"title": "Make AgentProject real — chat still uses the legacy Agent.projectId",
|
||||
"description": "The AgentProject junction has existed since the data-model commit but nothing reads it: chat.service resolves context from the single Agent.projectId scalar, so an agent still cannot meaningfully join many projects.",
|
||||
"details": "Verified: 0 references to agentProject in chat.service.ts.\n\n- Routes to join/leave a project with a role (task 152).\n- prepareContext resolves the project from the membership when the caller names one, falling back to the scalar during the transition release.\n- `mcpctl chat <agent> --project <name>` picks the membership; error clearly when the agent is not a member.\n- Per-membership llmId override honoured (this is the brain-ownership question, §15 Q1).\n- Only after that: drop Agent.projectId.",
|
||||
"testStrategy": "Service tests for membership resolution + a smoke test where one agent is used from two projects and gets different context in each.",
|
||||
"priority": "high",
|
||||
"dependencies": [
|
||||
"120",
|
||||
"152"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "167",
|
||||
"title": "describe formatters for the agentic-teams resources",
|
||||
"description": "epics/epictasks/tasks/repos/gitproviders fall through to the generic key-value dump, which breaks the project rule that describe shows clean sections and tables.",
|
||||
"details": "Verified: no case for these in describe.ts.\n\n- describe epic: vision, done-criteria table with met/unmet, accepted tradeoffs, and the roadmap (nodes + per-node completeness + which are ready) from GET /epics/:id/roadmap — the API exists and has no client.\n- describe task: state, legal transitions, risk + which gates it still needs, PR link, verdicts, transition history.\n- describe repo / gitprovider: credential REFERENCE only, never a resolved value.",
|
||||
"testStrategy": "src/cli/tests/commands/describe.test.ts cases per resource; assert no secret value ever appears in output.",
|
||||
"priority": "medium",
|
||||
"dependencies": [
|
||||
"136"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "168",
|
||||
"title": "Docs for the agentic-teams resources + the tasks rename note",
|
||||
"description": "No docs/*.md for the new resources, and the breaking `mcpctl get tasks` rename is recorded only in a commit message.",
|
||||
"details": "- docs/epics.md, docs/tasks.md, docs/repos.md, docs/gitproviders.md following the house shape (Model / CLI / RBAC / See also).\n- README Resources table (already stale — missing skill/proposal/revision too) and the CLAUDE.md resource cheatsheet.\n- Call out prominently that `mcpctl get tasks` now means WORK ITEMS and the inference queue moved to inference-tasks/itasks.\n- Document the `mcpctl` system agent and its permission model (acts as the caller, loopback through the API).",
|
||||
"testStrategy": "Manual read-through; assert every documented command actually exists by running --help.",
|
||||
"priority": "medium",
|
||||
"dependencies": [
|
||||
"154"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "169",
|
||||
"title": "Smoke tests for gitprovider discovery and import",
|
||||
"description": "The agentic-teams smoke covers epic/epictask/task but not the forge flow, which is the part with a live external dependency.",
|
||||
"details": "Register a provider against a reachable Gitea, discover, import, assert provenance (sourceProvider/sourceFullName), assert re-discovery marks imported, assert import of an unknown name is skipped with a reason rather than failing the batch. Skip cleanly when no forge credential is configured.",
|
||||
"testStrategy": "src/mcplocal/tests/smoke/gitprovider.smoke.test.ts",
|
||||
"priority": "medium",
|
||||
"dependencies": [
|
||||
"133"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "170",
|
||||
"title": "Reconcile Epic completeness against delivered work",
|
||||
"description": "Epic completeness counts only its own doneCriteria, which nobody flips automatically. An Epic can read 0% while every task under it is merged — the exact blind spot the Epic exists to remove.",
|
||||
"details": "EpicService.completeness reads epic.doneCriteria and nothing else; there is no path from task state → epictask criteria → epic criteria.\n\n- Surface delivered-vs-claimed: for an Epic, how many of its tasks are merged vs how many criteria are marked met, and flag the mismatch in both directions (work delivered but unclaimed; criteria claimed with no delivering task).\n- This is the mechanical half of the Tier-2 comparison (task 158) and is worth having before the critic persona exists.",
|
||||
"testStrategy": "Service tests over a fixture epic with a mix of merged/open tasks and met/unmet criteria; assert both mismatch directions are reported.",
|
||||
"priority": "medium",
|
||||
"dependencies": [
|
||||
"131",
|
||||
"126"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "171",
|
||||
"title": "End-to-end test of `mcpctl chat mcpctl` through mcplocal",
|
||||
"description": "The mcpctl system agent tool layer is verified live, but the full path — CLI → mcplocal → mcpd → LLM picks a tool → loopback — has never been run.",
|
||||
"details": "The tool provider and its permission model are unit-tested and were exercised directly against a live mcpd. What is untested is the LLM actually selecting an mcpctl tool, and the token surviving the mcplocal hop (mcplocal forwards Authorization; unverified for this path).\n\nNeeds a real model, so it belongs in smoke rather than unit tests. Assert: a request like the original example creates the prompt; a user without permission gets a reported 403 rather than a retry loop.",
|
||||
"testStrategy": "src/mcplocal/tests/smoke/mcpctl-agent.smoke.test.ts using the ChatReporter helper.",
|
||||
"priority": "medium",
|
||||
"dependencies": [
|
||||
"163"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "172",
|
||||
"title": "Release: nothing from the agentic-teams branch is live",
|
||||
"description": "Every change on feat/agentic-teams has been verified against a locally-run mcpd on the test database. The deployed mcpd and the installed mcpctl/mcplocal have none of it.",
|
||||
"details": "- mcpd needs a k8s deploy (scripts/deploy-k8s.sh) before any API or the system agent exists in the real environment.\n- mcplocal needs an RPM rebuild for the 30s proxy timeout fix (da4752b); the fix is committed but the running daemon predates it. Restart drops in-memory MCP sessions.\n- The rbac-tasks-backfill runs once on the first upgraded start: verify the log line and spot-check that pre-split grants moved to inference-tasks.\n- The RBAC smoke test (rbac-tasks-split.smoke.test.ts) is RED until this lands, by design.",
|
||||
"testStrategy": "After deploy: pnpm test:smoke should go green for rbac-tasks-split and agentic-teams.",
|
||||
"priority": "high",
|
||||
"dependencies": [],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
},
|
||||
{
|
||||
"id": "173",
|
||||
"title": "Web UI and Pulumi kinds for the agentic-teams resources",
|
||||
"description": "src/web has no pages for the new resources, and src/pulumi/src/kinds.ts has no mapping (it falls back gracefully, so this is cosmetic).",
|
||||
"details": "Lowest priority of the gap set — the CLI is the primary surface and it is complete.",
|
||||
"testStrategy": "Existing web test patterns.",
|
||||
"priority": "low",
|
||||
"dependencies": [
|
||||
"136"
|
||||
],
|
||||
"status": "pending",
|
||||
"subtasks": [],
|
||||
"updatedAt": "2026-08-09T19:36:34.052Z"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
@@ -3273,4 +3413,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,6 +869,9 @@ async function main(): Promise<void> {
|
||||
case 'group': await groupService.upsertByName(data); break;
|
||||
case 'rbac': await rbacDefinitionService.upsertByName(data); break;
|
||||
case 'prompt': await promptService.upsertByName(data); break;
|
||||
// Skills were exported and enqueued but never restored, so a git restore
|
||||
// silently dropped every skill. SkillService always had the method.
|
||||
case 'skill': await skillService.upsertByName(data); break;
|
||||
case 'template': await templateService.upsertByName(data); break;
|
||||
}
|
||||
};
|
||||
@@ -881,6 +884,7 @@ async function main(): Promise<void> {
|
||||
case 'group': await groupService.deleteByName(name); break;
|
||||
case 'rbac': await rbacDefinitionService.deleteByName(name); break;
|
||||
case 'prompt': await promptService.deleteByName(name); break;
|
||||
case 'skill': await skillService.deleteByName(name); break;
|
||||
case 'template': await templateService.deleteByName(name); break;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user