fix(runner): clarify active task history discovery scope

This commit is contained in:
Dotta committed 2026-11-04 12:29:25 -06:00
1 parent b111963000
commit 4713327d22
7 files changed
+32 -8

No files matched your search

@@ -1 +1 @@
[{"annotations":{"exposure":"always","operationId":"get_task_context","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the active task and actor, including the exact approved Markdown revision when this issue has an accepted plan.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"get_task_context","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"get_task_history","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded comments on the active task.","inputSchema":{"additionalProperties":false,"properties":{"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":[],"type":"object"},"name":"get_task_history","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_documents","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List revisioned documents on the active task.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_documents","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"read_document","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the current revision of one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"name":"read_document","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_document_revisions","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded revision history for one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"},"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":["key"],"type":"object"},"name":"list_document_revisions","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"report_progress","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append a durable progress comment to the active task.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Multiline progress update.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"report_progress","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"answer_status_question","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append the answer to a status-only wake without changing task disposition.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Concise status answer.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"answer_status_question","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"write_document","requiredClaimLine truncated [{"annotations":{"exposure":"always","operationId":"get_task_context","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the active task and actor, including the exact approved Markdown revision when this issue has an accepted plan.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"get_task_context","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"get_task_history","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded comments on the active task only. Calling get_task does not change the active task. This is not a status or assignment audit. For progress chronology and handoff actors, use search_api to discover the company timeline or issue activity, then call_api.","inputSchema":{"additionalProperties":false,"properties":{"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":[],"type":"object"},"name":"get_task_history","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_documents","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"List revisioned documents on the active task.","inputSchema":{"additionalProperties":false,"properties":{},"required":[],"type":"object"},"name":"list_documents","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"read_document","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read the current revision of one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"}},"required":["key"],"type":"object"},"name":"read_document","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"list_document_revisions","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Read bounded revision history for one active-task document.","inputSchema":{"additionalProperties":false,"properties":{"key":{"description":"Stable issue-document key.","maxLength":120,"minLength":1,"type":"string"},"limit":{"default":50,"maximum":200,"minimum":1,"type":"integer"}},"required":["key"],"type":"object"},"name":"list_document_revisions","outputSchema":{"additionalProperties":true,"type":"object"}},{"annotations":{"exposure":"always","operationId":"report_progress","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append a durable progress comment to the active task.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Multiline progress update.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"report_progress","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevision":{"minimum":0,"type":"integer"}},"required":["commandId","disposition","stateRevision","entityRefs","scheduledWakeIds"],"type":"object"}},{"annotations":{"exposure":"always","operationId":"answer_status_question","requiredClaims":[],"semanticContract":"paperclip.semantic-tool.v1","version":1},"description":"Append the answer to a status-only wake without changing task disposition.","inputSchema":{"additionalProperties":false,"properties":{"body":{"description":"Concise status answer.","maxLength":20000,"minLength":1,"type":"string"},"idempotencyKey":{"description":"Caller-stable retry key.","maxLength":240,"minLength":1,"type":"string"}},"required":["idempotencyKey","body"],"type":"object"},"name":"answer_status_question","outputSchema":{"additionalProperties":false,"properties":{"commandId":{"description":"Stable command identifier.","maxLength":200,"minLength":1,"type":"string"},"disposition":{"enum":["applied","duplicate"]},"entityRefs":{"description":"Entities affected by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"scheduledWakeIds":{"description":"Wake identifiers scheduled by the operation.","items":{"minLength":1,"type":"string"},"maxItems":200,"type":"array","uniqueItems":true},"stateRevisLine truncated
@@ -271,7 +271,7 @@
"planning", "planning",
"skill_test" "skill_test"
], ],
"description": "Read bounded comments on the active task.", "description": "Read bounded comments on the active task only. Calling get_task does not change the active task. This is not a status or assignment audit. For progress chronology and handoff actors, use search_api to discover the company timeline or issue activity, then call_api.",
"effect": "read", "effect": "read",
"inputSchema": { "inputSchema": {
"additionalProperties": false, "additionalProperties": false,
@@ -24,7 +24,7 @@
"prpVersion": 1, "prpVersion": 1,
"nativeExecutionVersion": 1, "nativeExecutionVersion": 1,
"catalogVersion": 1, "catalogVersion": 1,
"catalogSha256": "sha256:90ab0b84874818ca36e7dc6f610704457b22d8a69496f5d00133d2ca3353695d", "catalogSha256": "sha256:0f5ed174a13835ac831e8b7189052ab1645e3076b057ee3d375c84cef897b353",
"driverContractVersion": 1, "driverContractVersion": 1,
"driverKind": "paperclip-deterministic", "driverKind": "paperclip-deterministic",
"driverVersion": "1.0.0" "driverVersion": "1.0.0"
@@ -180,7 +180,7 @@
}, },
{ {
"path": "fixtures/evals/native-execution-seeded.json", "path": "fixtures/evals/native-execution-seeded.json",
"sha256": "221658ab8c0c5aa5db66b3d4fd0770f7c6342d415971036e2aa58d9b8c23f552", "sha256": "1ea768b354de46edc46aca9ac963fb623d3df8517377c9468801209272587ab7",
"expectation": "accept", "expectation": "accept",
"compatibilityCase": "canonical" "compatibilityCase": "canonical"
}, },
@@ -1,3 +1,4 @@
import { getTaskHistoryAction } from "../protocol-actions/get-task-history.js";
import { getWorkspaceAction } from "../protocol-actions/get-workspace.js"; import { getWorkspaceAction } from "../protocol-actions/get-workspace.js";
import { handoffCaseAction } from "../protocol-actions/handoff-case.js"; import { handoffCaseAction } from "../protocol-actions/handoff-case.js";
import { createCaseAction, readCaseAction, updateCaseAction } from "../protocol-actions/case-data.js"; import { createCaseAction, readCaseAction, updateCaseAction } from "../protocol-actions/case-data.js";
@@ -142,7 +143,7 @@ const descriptors: readonly PaperclipSemanticActionDescriptor[] = [
descriptor({ descriptor({
operationId: "get_task_history", operationId: "get_task_history",
title: "Get active task history", title: "Get active task history",
description: "Read bounded comments on the active task.", description: getTaskHistoryAction.live.descriptor.description,
inputSchema: object({ inputSchema: object({
limit: { type: "integer", minimum: 1, maximum: 200, default: 50 }, limit: { type: "integer", minimum: 1, maximum: 200, default: 50 },
}), }),
@@ -1,4 +1,6 @@
/** Canonical definition and documentation for `get_task_history`. */ /** Canonical definition and documentation for `get_task_history`. */
const description = "Read bounded comments on the active task only. Calling get_task does not change the active task. This is not a status or assignment audit. For progress chronology and handoff actors, use search_api to discover the company timeline or issue activity, then call_api.";
export const getTaskHistoryAction = { export const getTaskHistoryAction = {
"id": "get_task_history", "id": "get_task_history",
"canonical": { "canonical": {
@@ -27,7 +29,7 @@ export const getTaskHistoryAction = {
}, },
"documentation": { "documentation": {
"title": "Get active task history", "title": "Get active task history",
"description": "Read bounded comments on the active task.", "description": description,
"note": null "note": null
}, },
"examples": { "examples": {
@@ -48,7 +50,7 @@ export const getTaskHistoryAction = {
"operationId": "get_task_history", "operationId": "get_task_history",
"version": 1, "version": 1,
"title": "Get active task history", "title": "Get active task history",
"description": "Read bounded comments on the active task.", "description": description,
"exposure": "always", "exposure": "always",
"requiredClaims": [], "requiredClaims": [],
"allowedModes": [ "allowedModes": [
@@ -91,7 +93,7 @@ export const getTaskHistoryAction = {
"operationId": "get_task_history", "operationId": "get_task_history",
"version": 1, "version": 1,
"title": "Get task history", "title": "Get task history",
"description": "Read comments and audit-visible history for the active task only.", "description": description,
"inputSchema": { "inputSchema": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { createCapabilityProviderNeutralBinding } from "./provider-neutral.js"; import { createCapabilityProviderNeutralBinding } from "./provider-neutral.js";
import { createSkillAction } from "../protocol-actions/create-skill.js"; import { createSkillAction } from "../protocol-actions/create-skill.js";
import { getTaskHistoryAction } from "../protocol-actions/get-task-history.js";
import type { PaperclipSemanticActionBinding } from "./types.js"; import type { PaperclipSemanticActionBinding } from "./types.js";
import { PaperclipSemanticDispatcher } from "./paperclip-dispatcher.js"; import { PaperclipSemanticDispatcher } from "./paperclip-dispatcher.js";
@@ -76,6 +77,26 @@ describe("run-scoped semantic tool authority", () => {
); );
}); });
it("identifies active-task comments and discovers separate progress audit tools in registered history contracts", async () => {
const dispatcher = new PaperclipSemanticDispatcher({
contextProvider: () => runContext(),
bindings: [binding("get_task_history", { comments: [] })],
});
const tools = await dispatcher.listAlwaysAvailableTools(correlation.runId);
const history = tools.find(tool => tool.name === "get_task_history")!;
const live = createCapabilityProviderNeutralBinding("live_codex").contracts.find(tool => tool.name === "get_task_history")!;
for (const description of [history.description, live.description, getTaskHistoryAction.scenario.descriptor.description]) {
expect(description).toContain("comments on the active task only");
expect(description).toContain("get_task does not change the active task");
expect(description).toContain("not a status or assignment audit");
expect(description).toMatch(/search_api.*timeline.*issue activity.*call_api/);
}
expect(history.inputSchema).toEqual(live.inputSchema);
expect(history.inputSchema.properties).not.toHaveProperty("taskId");
expect(history.inputSchema.additionalProperties).toBe(false);
expect(history.annotations.requiredClaims).toEqual([]);
});
it("distinguishes authored skills from source imports in the actual discovered and provider tool contracts", async () => { it("distinguishes authored skills from source imports in the actual discovered and provider tool contracts", async () => {
const apiClaims = ["api:discover", "api:call"]; const apiClaims = ["api:discover", "api:call"];
let context = runContext({ actorClaims: apiClaims, delegatedClaims: apiClaims }); let context = runContext({ actorClaims: apiClaims, delegatedClaims: apiClaims });