From e1ebcbb7e3ad18af91b6c0dcc6391e088e40de89 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sat, 31 Oct 2026 23:34:23 +0100 Subject: [PATCH 1/6] fix(agent-ux): atomic claim --next --start, disclosed intent limit overrides, leaner list/search envelopes, MCP flat read parameters GH-1643 (pm-gh1643): `pm claim --next --start` was refused with "Specify an item id or pass --next" even though --next was passed. Any --start went to the explicit-id target check, and MCP pm_claim failed the same way. runClaim now takes an optional `start` that moves the item to the configured in-progress status inside the claim's own locked mutation (the release --handoff pattern). That means one write and one claim history event, type-workflow enforcement, a terminal_state_conflict refusal for terminal items, and a `start {from_status,to_status}` receipt. claim --next forwards it through the race-loss walk, so filters and --if-available behave as before. Explicit `claim --start` keeps its documented claim-then-update composition and its typed refusal. The claim-target refusals moved into the SDK claim module with typed codes (missing_required_argument, ambiguous_option_combination) and never ask for a flag the caller already passed. PmClient.claimNext({ start: true }) is typed. The MCP schema no longer forbids start+next, and start+ifAvailable is exclusive only with an explicit id. GH-1644 (pm-gh1644): `pm context --for orient --limit 10` silently returned 3 rows. The orient budget carries 3 rows (10 rows measure ~5,900 JSON tokens on this tracker), so the ceiling stays. The override is now disclosed through context_intent.limit_override {requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning "limit_overridden_by_intent:--limit 10->3". This applies equally to list --for triage and search --for discover. The three appliers share one clamp helper. The agent quickstart, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and the replayed transcripts now use the measured canonical `pm context --for orient`. The token baseline shows only a digest change, because the output is identical. The quickstart no longer calls explicit --start atomic. GH-1642 (pm-gh1642): brief TOON pages printed redundant envelopes. The list projection had stopped matching the default sort once sorting gained effective_keys. It now drops the legacy sort/order echo and keeps the effective default order on one line (the GH-1507 disclosure contract), and drops a filters.limit equal to applied_limit. The new public projectPmBriefSearchEnvelope drops truncated when it repeats has_more and limit-only filters, and replaces the default projection_metadata omission receipt with details --full. Measured against published 2026.10.31: search 1,033 -> 863 B, list 988 -> 782 B. JSON, --full and SDK results are unchanged. pm-hke9z7: MCP read tools silently ignored declared flat parameters. pm_context read none of them, and for/tokenBudget/depth on pm_list/pm_search/pm_next/pm_get never reached the intent layer, because flat-to-options hoisting was a hand list. Hoisting for context, get, list, next and search is now derived from PM_TOOL_ACTION_PARAMETER_CONTRACTS, and nested options still win. A spec iterates every declared key. pm_search also honours its declared `keywords` alias. Generated artifacts were refreshed: the contracts snapshot, the error-code catalog, the SDK surface (optional additions, acknowledged), the agent-task baseline digest, and the surface-replication census (two CLI refusals moved into the SDK). --- .agents/pm/features/pm-f05lsg.toon | 7 +- .agents/pm/history/pm-7zs0.jsonl | 1 + .agents/pm/history/pm-f05lsg.jsonl | 1 + .agents/pm/history/pm-gh1642.jsonl | 17 ++ .agents/pm/history/pm-gh1643.jsonl | 31 ++++ .agents/pm/history/pm-gh1644.jsonl | 25 +++ .agents/pm/history/pm-hke9z7.jsonl | 13 ++ .agents/pm/issues/pm-gh1642.toon | 59 ++++++ .agents/pm/issues/pm-gh1643.toon | 72 ++++++++ .agents/pm/issues/pm-gh1644.toon | 68 +++++++ .agents/pm/issues/pm-hke9z7.toon | 56 ++++++ .agents/pm/reminders/pm-7zs0.toon | 7 +- .../pm-user/references/ECOSYSTEM_PLANNING.md | 2 +- AGENTS.md | 6 +- CHANGELOG.md | 9 + README.md | 2 +- docs/AGENT_GUIDE.md | 6 +- docs/COMMANDS.md | 1 + docs/COMMAND_NAMESPACES.md | 2 +- docs/LIFECYCLE_COMMANDS.md | 19 +- docs/SDK.md | 8 +- docs/SDK_ACTION_CONFORMANCE.md | 16 ++ docs/agent-task-token-baseline.json | 2 +- docs/agent-task-transcripts.json | 6 +- scripts/release/surface-replication-sets.json | 4 +- sdk/public-surface.json | 82 ++++++++- src/cli/register-operations.ts | 105 +++++------ src/core/output/output.ts | 9 +- src/sdk/cli-contracts/tool-schema.ts | 9 +- src/sdk/context-intent-contracts.ts | 171 +++++++++++++----- src/sdk/core.ts | 1 + .../generated-error-code-catalog-part-1.ts | 4 +- .../generated-error-code-catalog-part-2.ts | 7 +- src/sdk/index.ts | 1 + src/sdk/init-agent-guidance.ts | 4 +- src/sdk/lifecycle/claim.ts | 95 +++++++++- src/sdk/lifecycle/mcp-actions.ts | 11 +- src/sdk/lifecycle/task-composition.ts | 3 +- src/sdk/output.ts | 5 +- src/sdk/output/list-envelope.ts | 101 ++++++++++- src/sdk/runtime-input.ts | 37 +++- src/sdk/runtime-public-contracts.ts | 2 + src/sdk/runtime.ts | 2 + src/sdk/runtime/actions.ts | 6 +- tests/fixtures/contracts/full.json | 70 +++---- tests/integration/cli.integration.spec.ts | 2 +- tests/unit/cli/compact-agent-output.spec.ts | 102 ++++++++++- tests/unit/commands/register-commands.spec.ts | 22 ++- .../commands/workspace/init-command.spec.ts | 2 +- .../mcp/read-tool-flat-parameters.spec.ts | 62 +++++++ .../release/agent-task-token-gate.spec.ts | 2 +- .../sdk/context/intent-limit-override.spec.ts | 69 +++++++ tests/unit/sdk/lifecycle/claim-start.spec.ts | 139 ++++++++++++++ .../sdk/recovery/recovery-guidance.spec.ts | 2 +- 54 files changed, 1344 insertions(+), 223 deletions(-) create mode 100644 .agents/pm/history/pm-gh1642.jsonl create mode 100644 .agents/pm/history/pm-gh1643.jsonl create mode 100644 .agents/pm/history/pm-gh1644.jsonl create mode 100644 .agents/pm/history/pm-hke9z7.jsonl create mode 100644 .agents/pm/issues/pm-gh1642.toon create mode 100644 .agents/pm/issues/pm-gh1643.toon create mode 100644 .agents/pm/issues/pm-gh1644.toon create mode 100644 .agents/pm/issues/pm-hke9z7.toon create mode 100644 tests/unit/mcp/read-tool-flat-parameters.spec.ts create mode 100644 tests/unit/sdk/context/intent-limit-override.spec.ts create mode 100644 tests/unit/sdk/lifecycle/claim-start.spec.ts diff --git a/.agents/pm/features/pm-f05lsg.toon b/.agents/pm/features/pm-f05lsg.toon index b08e186678..aea805e4fa 100644 --- a/.agents/pm/features/pm-f05lsg.toon +++ b/.agents/pm/features/pm-f05lsg.toon @@ -6,7 +6,7 @@ status: open priority: 1 tags[4]: agent-ergonomics,ci-gates,discoverability,long-horizon created_at: "2026-08-01T13:09:26.104Z" -updated_at: "2026-10-23T10:17:53.217Z" +updated_at: "2026-10-31T21:50:08.744Z" author: "harness:claude-code" estimated_minutes: 720 acceptance_criteria: "A replay corpus of wrong invocations is generated from the contract tables and covers every flag with a closed domain, every subcommand family, every required-argument omission and every mutually exclusive flag pair; Every declared error code is reachable from at least one corpus input, and a code with no reachable input is reported as such; A scorer reports the recovery-closure fraction and the list of unrecoverable refusals, scoring a lone nearest-match suggestion as not recoverable; The closure fraction is a ratchet in the gate registry that may not fall, with a negative control proving the gate can fail; The refusal envelope has a declared shape carrying the failing surface, the rejected value, the legal domain when known, and the exit code; Non-interactive init guidance warnings expose bounded executable inspection and explicit add commands while preserving skip/decline intent and user-authored documentation, with real isolated recovery and byte-preservation evidence" @@ -47,7 +47,7 @@ dependencies[28]{id,kind,created_at,author,source_kind,author_source}: pm-gh1363,related,"2026-10-10T05:34:42.387Z","harness:codex","cli:update:dep",detected pm-998juj,discovered_from,"2026-10-10T11:46:33.219Z","harness:codex","cli:update:dep",detected pm-83cz0o,discovered_from,"2026-10-10T12:22:11.123Z","harness:codex","cli:update:dep",detected -comments[86]: +comments[87]: - created_at: "2026-08-18T19:18:11.981Z" author: "harness:codex" text: "Implementation start 2026-08-18: active in one coupled SDK-first command-contract tranche spanning canonical flag concepts and budgets, truthful invocation arity, complete closed-domain recovery, refusal-closure scoring, source-derived executable recovery obligations, and shared CLI/MCP/completion/docs discovery metadata. This item is claimed because its acceptance surface will be edited and verified in this branch; unrelated parents and backlog items remain open and unclaimed." @@ -308,6 +308,9 @@ comments[86]: - created_at: "2026-10-23T10:17:53.217Z" author: "harness:codex" text: "Additional recovery corpus candidate discovered during pm-t2dqzijokl31: src/core/test/run-selectors.ts still advises adding a first test with --command, but the current test command contract refuses --command as unknown_option. Existing --add and --add-json alternatives are valid. Preserve this in the canonical recovery-closure programme rather than create a duplicate general UX item; current implementation remains scoped to linked-resource positional recovery and assignment-only evidence." + - created_at: "2026-10-31T21:50:08.744Z" + author: "harness:claude-code" + text: "Scope note from pm-gh1643 (GH-1643): a second refusal shape belongs in this closure gate. 'pm claim --next --start' told the caller to 'pass --next' while --next was in recovery.provided_fields (the same defect shape GH-1352 had on --if-available). pm-gh1643 fixes the claim-target refusals and adds a targeted spec. The class-wide invariant is still unenforced: no refusal's message, required text, next_steps or suggested_retry may instruct passing a flag already present in provided_fields. PmRefusalClosureObservation does not carry next_steps or provided_fields today, so the census cannot observe this; extend the observation and add a redundant_flag_advice finding, then run it over every grammar-derived probe." notes[3]: - created_at: "2026-10-04T16:59:01.232Z" author: "harness:codex" diff --git a/.agents/pm/history/pm-7zs0.jsonl b/.agents/pm/history/pm-7zs0.jsonl index ded5bf91b7..9df73c4136 100644 --- a/.agents/pm/history/pm-7zs0.jsonl +++ b/.agents/pm/history/pm-7zs0.jsonl @@ -579,3 +579,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-30T13:14:20.174Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"7b0e58428f53e3aaeaafb372","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1626","claim:pm-gh1630","claim:pm-gh1633","lineage:pm-gh1630","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1626+pm-gh1630+pm-gh1633","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1626","claim:pm-gh1630","claim:pm-gh1633","lineage:pm-gh1630","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.312","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/257","value":{"created_at":"2026-10-30T13:14:20.173Z","author":"harness:claude-code","text":"October 30 follow-up at 13:15 UTC: scheduled Auto Release run 41029596560 (08:54 UTC) ended cancelled after 15m13s, mid-pipeline. The workflow uses cancel-in-progress: false with a 180-minute job timeout, so this was an external cancellation, not a concurrency or timeout cut. It made no publication. Automation opened blocker issue #1632 for the earlier dispatcher failure 40999107873; it is now linked to the owner pm-gh1622, with no duplicate item. Live state: npm @unbrained/pm-cli is 2026.10.28, latest GitHub release is v2026.10.28, and there is no 2026.10.29 or 2026.10.30 cut, so no duplicate automatic cut can exist. Sentry has exactly one unresolved issue in 14 days: PM-CLI-3Q, 2 events, last seen 2026-10-29 03:11:51 UTC. Under the release gate's 14-day window it blocks every cut until it is resolved with verified pm-presets remediation; upstream PR170 is still open at 97d5d1bf4. Telemetry stack check at 13:13 UTC: all containers healthy, API synced at 13:09, queue drained, 74,676 events in 24h, newest event under one minute old. The last hour split 3,432 automation and 352 test source contexts, so test-run tagging from GH-1625 is visible in production rows."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-30T13:14:20.174Z"}],"before_hash":"6b69ce088b398cc97ec761a311b01654b7dbff970bdeadcf17cf87c5ecda87b0","after_hash":"ed3825cf0237eaec733c5699030cfce4d351e709a0c08651e9a179fdb21eece6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"02a1320865bc15cf9c902dc6407444821147a86595014b0d2c30d2b4aabc12a5"} {"hash_algorithm":"sha256","ts":"2026-10-30T17:18:07.453Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"7b0e58428f53e3aaeaafb372","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.312","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/expected_result","value":"At October 31 06:00 UTC verify the next eligible daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes."},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-30T17:18:07.453Z"}],"before_hash":"ed3825cf0237eaec733c5699030cfce4d351e709a0c08651e9a179fdb21eece6","after_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","item_hash_version":3,"message":"Align the release-health expected result with the rearmed October 31 reminder","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2251442c1d82111b7c9ac9c605d14f71ad1f1ad1f0855a6f25108160779225a1","reanchor_evidence":[{"before_hash":"6b69ce088b398cc97ec761a311b01654b7dbff970bdeadcf17cf87c5ecda87b0","after_hash":"eec31ea294980d7fe3bd9669dd82dc9502c2f2d67ce2bd31739e51cd9c9caf72","patch_hash":"8415e8b1b08ac08fb872b01e06a2c6322ade6d26de5a1cd4fda62d56a6a1cbc5","hash_algorithm":"sha256","item_hash_version":3,"record_hash_version":1,"record_hash":"ae50f6d1abbb2313aa5f6d3a23b0b8e80cd44b5b89a6662eda211d088af72c4f","record":{"hash_algorithm":"sha256","ts":"2026-10-30T17:18:07.453Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"7b0e58428f53e3aaeaafb372","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.312","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/expected_result","value":"At October 31 06:00 UTC verify the next eligible daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes."},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-30T17:18:07.453Z"}],"before_hash":"6b69ce088b398cc97ec761a311b01654b7dbff970bdeadcf17cf87c5ecda87b0","after_hash":"eec31ea294980d7fe3bd9669dd82dc9502c2f2d67ce2bd31739e51cd9c9caf72","item_hash_version":3,"message":"Align the release-health expected result with the rearmed October 31 reminder","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"ae50f6d1abbb2313aa5f6d3a23b0b8e80cd44b5b89a6662eda211d088af72c4f"}}]} {"hash_algorithm":"sha256","ts":"2026-10-31T00:01:31.558Z","author":"harness:claude-code","op":"merge_reconcile","patch":[],"before_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","after_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","message":"record field-aware branch merge provenance","item_hash_version":3,"context":{"merge":{"receipts":[{"receipt_id":"fe024a5f-0084-4e67-882f-b85ebc7b88c8","item_id":"pm-7zs0","item_path":".agents/pm/reminders/pm-7zs0.toon","conflict_fields":[],"fields_from_theirs":["expected_result"],"union_fields":[],"requested_preference":"ours","requested_preference_applied":false,"conflict_resolution":"latest_document_update","decisions":[]}]}},"event_class":"substantive","record_hash_version":1,"record_hash":"1a1377a900f98fb21321bbfc1e4a41d5223b4f4f67cba66b208ef6ef487aab66"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:12:47.452Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/258","value":{"created_at":"2026-10-31T22:12:47.451Z","author":"harness:claude-code","text":"October 31 checkpoint (verified live 22:10 UTC): the 03:45 UTC manual run 41165698315 and the 08:47 UTC scheduled run 41185994587 both stopped at static-quality-gate on 1a72d089d, before publication. The causes were two registry advisories published that morning (follow-redirects GHSA-mrwp-f3q8-3g9p, minimatch GHSA-g3hv-75wg-m249) and the pm-changelog 2026.10.31 grammar census drift. PR #1638 (3ff44fb6a) fixed both. Closing blocker #1639 at 19:16 UTC fired Auto Release (blocker_retry) 41233462967 → Release 41236628626 → v2026.10.31: npm latest 2026.10.31, GitHub Release published 20:51 UTC. Today has exactly one cut and no duplicate automatic release, and the nine other blocker_retry runs were skipped. The nightly quality alert #1641 had the same root cause and is closed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:12:47.452Z"}],"before_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","after_hash":"6d04e973eae1c0d9d96628dae8004240edaa87f87417fc5635e5d06a303f4517","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"61afdba7c0cf5bad087c45a9ae165d91962a9db958c0b42e9260221a51c81961"} diff --git a/.agents/pm/history/pm-f05lsg.jsonl b/.agents/pm/history/pm-f05lsg.jsonl index 0140283ba7..0cedb16148 100644 --- a/.agents/pm/history/pm-f05lsg.jsonl +++ b/.agents/pm/history/pm-f05lsg.jsonl @@ -337,3 +337,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-22T12:21:17.869Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"f6beeceeb3fb6ff7c9919162","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-5xn8we1wue0m","claim:pm-c9wz5ef4ulnv","claim:pm-gxq0dt","claim:pm-p8s5k7e6lhda","lineage:pm-gxq0dt","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-5xn8we1wue0m+pm-c9wz5ef4ulnv+pm-gxq0dt+pm-p8s5k7e6lhda","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-5xn8we1wue0m","claim:pm-c9wz5ef4ulnv","claim:pm-gxq0dt","claim:pm-p8s5k7e6lhda","lineage:pm-gxq0dt","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/84","value":{"created_at":"2026-10-22T12:21:17.868Z","author":"harness:codex","text":"Additional reproducible recovery-closure corpus case from October22 dogfood: pm search \"nightly timeout shard\" --mode keyword --match-mode any --limit15 correctly refuses with allowed values and|or|exact, but its Examples line changes --mode keyword to --mode and while retaining invalid --match-mode any. The intended legal recovery is pm search \"nightly timeout shard\" --mode keyword --match-mode or --limit15. Bind allowed-value correction to the rejected flag, retain unrelated valid options, and execute the emitted retry against the active contract. Existing positional files/docs/test misuse remains separately tracked by pm-t2dqzijokl31; neither backlog item is claimed for this delivery."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T12:21:17.869Z"}],"before_hash":"2c14446562eb37db39815b30be34a7779076bee0fd340dee2b66e2b79df32e88","after_hash":"285f0f0917b3dc1dc02790308cacda88213b065d66482caa36e1aa2cbca08665","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"967389d44b5cda19cb9a783a6c233fa9cf2bc7951438092586b3c061609ce92e"} {"hash_algorithm":"sha256","ts":"2026-10-22T13:03:03.995Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"f6beeceeb3fb6ff7c9919162","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-4vymxc","claim:pm-gxq0dt","lineage:pm-4vymxc","lineage:pm-u9d0","lineage:pm-doxj"]},"topic":{"value":"workset:pm-4vymxc+pm-gxq0dt","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-4vymxc","claim:pm-gxq0dt","lineage:pm-4vymxc","lineage:pm-u9d0","lineage:pm-doxj"]}},"op":"comment_edit","patch":[{"op":"replace","path":"/metadata/comments/84/text","value":"Additional reproducible recovery-closure corpus case from October22 dogfood: pm search \"nightly timeout shard\" --mode keyword --match-mode any --limit 15 correctly refuses with allowed values and|or|exact, but its Examples line changes --mode keyword to --mode and while retaining invalid --match-mode any. The intended legal recovery is pm search \"nightly timeout shard\" --mode keyword --match-mode or --limit 15. Bind allowed-value correction to the rejected flag, retain unrelated valid options, and execute the emitted retry against the active contract. Existing positional files/docs/test misuse remains separately tracked by pm-t2dqzijokl31; neither backlog item is claimed for this delivery."},{"op":"add","path":"/metadata/comments/84/edited_at","value":"2026-10-22T13:03:03.995Z"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-22T13:03:03.995Z"}],"before_hash":"285f0f0917b3dc1dc02790308cacda88213b065d66482caa36e1aa2cbca08665","after_hash":"472b0d257b21a2457c5a6c0b8cb7d088372f2cd049230972dfc8e780432e0f4e","item_hash_version":3,"message":"Correct documented retry token separation after executable review","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8373a6767564894a3acec32fc42754cc96341f34fd8e1bcd209dff9fe008e512"} {"hash_algorithm":"sha256","ts":"2026-10-23T10:17:53.217Z","author":"harness:codex","author_source":"detected","agent_harness":"codex","agent_model":"gpt-6.1-sol","agent_model_source":"probe","agent_instance":"877aec11872ca473544699e2","agent_provenance":{"model":{"value":"gpt-6.1-sol","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1555","claim:pm-t2dqzijokl31","lineage:pm-t2dqzijokl31","lineage:pm-f05lsg","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1555+pm-t2dqzijokl31","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1555","claim:pm-t2dqzijokl31","lineage:pm-t2dqzijokl31","lineage:pm-f05lsg","lineage:pm-mpbb","lineage:pm-doxj"]}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/85","value":{"created_at":"2026-10-23T10:17:53.217Z","author":"harness:codex","text":"Additional recovery corpus candidate discovered during pm-t2dqzijokl31: src/core/test/run-selectors.ts still advises adding a first test with --command, but the current test command contract refuses --command as unknown_option. Existing --add and --add-json alternatives are valid. Preserve this in the canonical recovery-closure programme rather than create a duplicate general UX item; current implementation remains scoped to linked-resource positional recovery and assignment-only evidence."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-23T10:17:53.217Z"}],"before_hash":"472b0d257b21a2457c5a6c0b8cb7d088372f2cd049230972dfc8e780432e0f4e","after_hash":"4fc0314c5663fecbf560281f933c78971fcd4791bc5fbbd2174ef0320896334e","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"effort":{"status":"resolved","resolver":"codex_session_file","rule_version":"v1"},"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f6268cac344afd02d9d6f6ca6d59fa799f8b686b3b389e0b3af02c342a2d5e59"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:50:08.744Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/86","value":{"created_at":"2026-10-31T21:50:08.744Z","author":"harness:claude-code","text":"Scope note from pm-gh1643 (GH-1643): a second refusal shape belongs in this closure gate. 'pm claim --next --start' told the caller to 'pass --next' while --next was in recovery.provided_fields (the same defect shape GH-1352 had on --if-available). pm-gh1643 fixes the claim-target refusals and adds a targeted spec. The class-wide invariant is still unenforced: no refusal's message, required text, next_steps or suggested_retry may instruct passing a flag already present in provided_fields. PmRefusalClosureObservation does not carry next_steps or provided_fields today, so the census cannot observe this; extend the observation and add a redundant_flag_advice finding, then run it over every grammar-derived probe."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:50:08.744Z"}],"before_hash":"4fc0314c5663fecbf560281f933c78971fcd4791bc5fbbd2174ef0320896334e","after_hash":"4ba789f5c1ce5839201086f863179663dadd6c6725d71b58099052fc1005b75d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"a3df9f8ef7b13910415fcf4354e9378879efa385ddd8fefb0faa86a08e48bc89"} diff --git a/.agents/pm/history/pm-gh1642.jsonl b/.agents/pm/history/pm-gh1642.jsonl new file mode 100644 index 0000000000..9e2fd1e4de --- /dev/null +++ b/.agents/pm/history/pm-gh1642.jsonl @@ -0,0 +1,17 @@ +{"hash_algorithm":"sha256","ts":"2026-10-31T21:14:02.437Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-gh1642"},{"op":"add","path":"/metadata/title","value":"GH-1642: brief pm list and pm search pages print an envelope as large as their rows; the default sort and search's default omission receipt are no longer compacted"},{"op":"add","path":"/metadata/description","value":"https://github.com/unbraind/pm-cli/issues/1642\n\nReproduced on the current source build (2026.10.31). pm list: projectPmBriefListEnvelope (src/sdk/output/list-envelope.ts, pm-6pycus) only drops the default sorting block when it has exactly the keys sort and order. Sorting later gained effective_keys, so the 7-line default sort is printed on every brief list page — the compaction silently stopped matching its own default. filters echoes limit, which applied_limit already states. pm search has no brief projection at all: it prints truncated (equal to has_more), filters {limit} (equal to applied_limit) and a 4-line omission_receipt whose only group is projection_metadata restorable with --full.\n\nDeliberate exclusions, with reasons: completeness unchecked stays visible (pm-6pycus AC: unchecked and partial source evidence cannot become complete or disappear); the items_encoding block stays because it is the lossless decode contract for the matched_fields list column, and the formatter round-trip tests require json_columns and absent to restore rows; JSON, --full and SDK result types keep the full receipts.\n\nDuplicate search: 'brief envelope list', GH-1501, GH-1642; nearest is pm-6pycus (GH-1501 brief list envelope, closed) which this extends."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["agent-ux","github","output","token-efficiency"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-31T21:14:02.437Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-31T21:14:02.437Z"},{"op":"add","path":"/metadata/author","value":"harness:claude-code"},{"op":"add","path":"/metadata/estimated_minutes","value":120},{"op":"add","path":"/metadata/acceptance_criteria","value":"A default brief pm list page omits the default sorting block including effective_keys and a filters.limit equal to applied_limit; a default pm search page omits truncated when equal to has_more, a filters block that only echoes applied_limit, and replaces the single default projection_metadata omission receipt with details --full; unchecked or partial completeness, non-default sorting, non-limit filters, custom omission receipts, JSON, --full and SDK results are unchanged; the search projection is a reusable SDK export that leaves its input unchanged; measured envelope bytes drop on a small page"},{"op":"add","path":"/metadata/parent","value":"pm-5t33or"},{"op":"add","path":"/metadata/risk","value":"low"},{"op":"add","path":"/metadata/confidence","value":"high"},{"op":"add","path":"/metadata/expected_result","value":"Small default pm list and pm search pages print only envelope fields that carry information, while JSON, --full and SDK results keep complete receipts."},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-6pycus","kind":"related","created_at":"2026-10-31T21:14:02.437Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-gh1627","kind":"related","created_at":"2026-10-31T21:14:02.437Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"eb8cd39552c18538dda33ca990dd49b5a5d1ef3769e92fe9d3e8486a3b091be4","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7cb087c4ad5d318c2416cd678b28ab6d4b5d351497ec787163ecdc507264eccb"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:15:11.440Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1643","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1643+pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1643","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:15:11.440Z"},{"op":"add","path":"/metadata/assignee","value":"harness:claude-code"},{"op":"add","path":"/metadata/claim_principal","value":"harness:claude-code#8a4268fe12c2a5f9fcd4da8d"}],"before_hash":"eb8cd39552c18538dda33ca990dd49b5a5d1ef3769e92fe9d3e8486a3b091be4","after_hash":"ebfd6cf51cb0e60ff3bb3f4c21e60e0fc4766ba253691f1d6bf0c5bb8430c9fb","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"45dd9dbf860e92b799959ef8d9f726bb1c88d2aae2bb9c7c82a7b91e20e3afa0"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:15:11.618Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1642","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1642","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:15:11.618Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"ebfd6cf51cb0e60ff3bb3f4c21e60e0fc4766ba253691f1d6bf0c5bb8430c9fb","after_hash":"a7cd5891f0dd762086cf8f10636f6c4d677ef66ba7402fcaae53e6a1f7eecdf6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"b8abcc1e37cccbd23f518b1b80405fddcec0d9ef569f1cc2ad782553af16cf09"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:26.635Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:26.635Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/sdk/output/list-envelope.ts","scope":"project"}]}],"before_hash":"a7cd5891f0dd762086cf8f10636f6c4d677ef66ba7402fcaae53e6a1f7eecdf6","after_hash":"831e6260eb54ef4e0e468ee5d558292537f1bc93056aed3ee7a18fee202c431d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"ad379f1a88946001b8e54e95dc12d3b2ec235b642f829bacebe5598f20003357"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:27.443Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/0/path","value":"src/sdk/output.ts"},{"op":"add","path":"/metadata/files/1","value":{"path":"src/sdk/output/list-envelope.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:27.443Z"}],"before_hash":"831e6260eb54ef4e0e468ee5d558292537f1bc93056aed3ee7a18fee202c431d","after_hash":"ba209bfcb5c84a23da6d44fbe876860425faa7243742594030adfd06e81c1e22","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"4478f9a6d0971a80f7ef9d76f24b51892b0f11635aa60b2a5bd72204b6e9d071"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:28.340Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/output.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"src/core/output/output.ts"},{"op":"add","path":"/metadata/files/2","value":{"path":"src/sdk/output/list-envelope.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:28.340Z"}],"before_hash":"ba209bfcb5c84a23da6d44fbe876860425faa7243742594030adfd06e81c1e22","after_hash":"cbcb114811784f9f815a974069d42ef28d11c83374155736bab5ff1e3fc2115e","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d892e144b10342fc43de6697c32efaed46d93a1e1d6d14d7ac0cfb02623236f1"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:29.218Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/3","value":{"path":"tests/unit/cli/compact-agent-output.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:29.218Z"}],"before_hash":"cbcb114811784f9f815a974069d42ef28d11c83374155736bab5ff1e3fc2115e","after_hash":"28d579eeba13409945b8db586382aba24e72f0e41dcad8efd7f9b2150c0f4ba0","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d974b9284b694378c2ac088d9f18b38a73bb48aa0c8e455ed396d5f46fdbd73f"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:29.878Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:29.878Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/SDK.md","scope":"project"}]}],"before_hash":"28d579eeba13409945b8db586382aba24e72f0e41dcad8efd7f9b2150c0f4ba0","after_hash":"a6de24a8dba34413db03886941f28e10b69a6b2fdf5380195b373b789fb1cc34","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d3e385967fbd55b5409ab75d65b89926eb69c31c506219280967454b209b6c2e"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:35.399Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:35.399Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts","scope":"project","provenance":{"author":"harness:claude-code","created_at":"2026-10-31T21:46:35.370Z","source_kind":"local_mutation","source_ref":"fix/agent-ux-orient-claim-envelopes"}}]}],"before_hash":"a6de24a8dba34413db03886941f28e10b69a6b2fdf5380195b373b789fb1cc34","after_hash":"86627dbb0b68609ea14f2468e8d577d7fff92c38de927c92ef5cb704b3b38b80","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"71efc248c8c9f67c2479bdd6a8c6e40ade1cfa33c239d8f007c9cc46a1739af2"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:47:00.908Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:47:00.908Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-31T21:47:00.908Z","author":"harness:claude-code","text":"Implementation and measurement: projectPmBriefListEnvelope now drops the default ascending sort when it carries effective_keys (the regression), and drops a filters.limit equal to applied_limit and empty runtime_filters. New public projectPmBriefSearchEnvelope (sdk/output) drops truncated when it equals has_more and limit-only filters, and replaces the single default projection_metadata omission receipt with details --full. It is chained after the list projection in formatBuiltInOutput (TOON only). Measured with the published 2026.10.31 package against this branch on this tracker: pm search 'release workflow' --limit 3 drops from 1,033 to 863 B (-16%) and pm list --status open --limit 3 from 988 to 782 B (-21%). completeness unchecked and items_encoding remain by design (see description)."}]}],"before_hash":"86627dbb0b68609ea14f2468e8d577d7fff92c38de927c92ef5cb704b3b38b80","after_hash":"62d50ec868bfec0715884b79ab47980e777ed5dc1402be7478fc6fb48daad7e4","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3d8aa84666c77f5800a8d45385b00a1d91ce0201fc05f33a5bb904491e6f5408"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:49:41.856Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T21:49:41.855Z","author":"harness:claude-code","text":"Design correction while testing: the 7-line default sort block is not only redundant metadata. GH-1507's accepted contract (pm-zd46m7, integration spec default-list-order) requires brief TOON to disclose the effective default comparator order so an agent knows why rows arrive in that order. Dropping it would undo that decision. The projection therefore drops only the legacy sort/order echo and renders the effective keys on one line, for example sorting.effective_keys 'status_registry:nonterminal_before_terminal asc, priority asc, updated_at desc, id asc'. Seven lines become two, and JSON keeps the full pair array. A malformed or extended sorting block passes through unchanged."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:49:41.856Z"}],"before_hash":"62d50ec868bfec0715884b79ab47980e777ed5dc1402be7478fc6fb48daad7e4","after_hash":"51547f15e4c4b3baed63e1ff60d34e6c6d913373aed163beb52fb5c3aef29d2d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9e94915a3a25a1749d1d9e27317d03d5c84bdaafb6cf4a7b4e0af5c27efbcdae"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:49:42.870Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"add","path":"/metadata/dependencies/2","value":{"id":"pm-zd46m7","kind":"related","created_at":"2026-10-31T21:49:42.569Z","author":"harness:claude-code","source_kind":"cli:update:dep","author_source":"detected"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:49:42.870Z"}],"before_hash":"51547f15e4c4b3baed63e1ff60d34e6c6d913373aed163beb52fb5c3aef29d2d","after_hash":"3849a40333bdcc0c393302dc611dcde481a8ca3ebdfab0501d832c0c384f6827","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"208a753059f9c4444cded402206b0c17a5354622186a703e092bfe8b5497b160"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:12.647Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:12.647Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_added","owner":"pm-gh1642","gate_id":"tests/unit/cli/compact-agent-output.spec.ts GH-1642 brief search and list envelopes","negative_control":"git stash push src/sdk/output/list-envelope.ts src/core/output/output.ts src/sdk/output.ts && node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts; git stash pop (the GH-1642 cases fail before the fix)","local_checks":["node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts tests/integration/read-output/default-list-order.integration.spec.ts","node scripts/sdk-surface-snapshot.mjs --check"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"3849a40333bdcc0c393302dc611dcde481a8ca3ebdfab0501d832c0c384f6827","after_hash":"5803632a15c591b7261e3f4d057fc32b7c93ffa13644e5f66725621bf7f1c203","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"a1f055bfb226dfde4233a1944f2ccee270ea3dc685496e6c0f60e5b63a5879ce"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:46.540Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/output/list-envelope.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/output.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/core/output/output.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"sdk/public-surface.json"},{"op":"add","path":"/metadata/files/4","value":{"path":"tests/unit/cli/compact-agent-output.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:46.540Z"}],"before_hash":"5803632a15c591b7261e3f4d057fc32b7c93ffa13644e5f66725621bf7f1c203","after_hash":"cbb323f9d890c82623536a8597ceb7fa473484f6e2b93cf749cb80429b7ea805","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"4a96c97bfd98795385f3248c863f426c6223f784213f2878390fb37cbde475b2"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:30:07.457Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:30:07.457Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz55qw-51axji","kind":"test","status":"passed","started_at":"2026-10-31T22:29:59.138Z","finished_at":"2026-10-31T22:30:07.448Z","recorded_at":"2026-10-31T22:30:07.448Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"cbb323f9d890c82623536a8597ceb7fa473484f6e2b93cf749cb80429b7ea805","after_hash":"6781ea340c88a027084c46ef83d087576d93f0e736738fb5d7ed91a9c2f261c8","item_hash_version":3,"message":"Track test run summary (test-local-mvwz55qw-51axji)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1a1c314e8bc2fa8ef846fb5f4d4c0991e3f515d3e946fce3d2e4e56e73d58de4"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:34.358Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:34.358Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:34.335Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:34.335Z"},{"op":"add","path":"/metadata/resolution","value":"projectPmBriefListEnvelope now recognizes the default ascending sort even with effective_keys. It replaces the legacy sort/order echo with one effective_keys comparator line, keeping the GH-1507 disclosure, and drops a filters.limit equal to applied_limit and empty runtime_filters. The new public SDK projectPmBriefSearchEnvelope drops truncated when it repeats has_more and limit-only filters, and replaces the default projection_metadata omission receipt with details --full. formatBuiltInOutput chains both for TOON only. completeness unchecked and items_encoding stay by contract."},{"op":"add","path":"/metadata/actual_result","value":"Same tracker, published 2026.10.31 vs this branch: pm search 'release workflow' --limit 3 drops from 1,033 to 863 bytes and pm list --status open --limit 3 from 988 to 782 bytes. compact-agent-output spec 59/59 and default-list-order integration pass; the GH-1642 cases fail with the projection stashed. SDK surface update is additive for the new export."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: brief pm list and pm search TOON pages drop redundant envelope fields"}],"before_hash":"6781ea340c88a027084c46ef83d087576d93f0e736738fb5d7ed91a9c2f261c8","after_hash":"fd4bb7a8925c3606f9b7aaeb1f92daf781b9d23c5cd8f9cfb4cdba139f2d4951","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:34.335Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"597f8ffd7c8592bed9985297e478b5365ed80bbb1146b94e9bf7af7c7451a5b1"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:37.050Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-hke9z7","release:pm-gh1644"]},"topic":{"value":"workset:pm-gh1642+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-hke9z7","release:pm-gh1644"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:37.050Z"}],"before_hash":"fd4bb7a8925c3606f9b7aaeb1f92daf781b9d23c5cd8f9cfb4cdba139f2d4951","after_hash":"627e202bef981e72032fa181cd400f4ac75740bb8390ad552e041a5c58b73155","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0b8c11ade15bb2e05fbe4d4a77091f4f64e2bb07b3c075c20625c2b775f3e86e"} diff --git a/.agents/pm/history/pm-gh1643.jsonl b/.agents/pm/history/pm-gh1643.jsonl new file mode 100644 index 0000000000..c9e98ef107 --- /dev/null +++ b/.agents/pm/history/pm-gh1643.jsonl @@ -0,0 +1,31 @@ +{"hash_algorithm":"sha256","ts":"2026-10-31T21:13:57.945Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-gh1643"},{"op":"add","path":"/metadata/title","value":"GH-1643: pm claim --next --start is refused with 'Specify an item id or pass --next' although --next was passed; select-claim-start needs one atomic write"},{"op":"add","path":"/metadata/description","value":"https://github.com/unbraind/pm-cli/issues/1643\n\nReproduced on the current source build (2026.10.31) in a fresh non-git sandbox: `pm claim --next --start` exits 2 with the generic command_failed refusal 'Specify an item id or pass --next' and refusal.surface --next. Root cause: runClaimAction in src/cli/register-operations.ts routes any --start to requireClaimTarget(id, false), which ignores --next and emits the hint to pass the flag already passed. The MCP transport has the same defect: runMcpClaimAction (src/sdk/lifecycle/mcp-actions.ts) routes start to runMcpTaskCompositionAction, which requires an explicit id and refuses with 'Missing required argument: id'.\n\nSelecting, claiming and starting the next item is one agent intent. Today it takes claim --next followed by update --status in_progress: a crash between the two leaves a claimed-but-open item. claim --next already selects and claims under the item lock with an advancing race-loss walk (pm-114v, pm-fjxm). release --handoff already changes status inside the release mutation with enforceTypeWorkflowTransition (src/sdk/lifecycle/release-handoff.ts), so the same pattern gives an atomic claim-and-start.\n\nDuplicate search: 'claim next start', 'refusal suggests flag already provided', GH-1352, GH-1643; nearest are pm-114v and pm-fjxm (claim --next, closed), pm-gh1369 (claim --start compact receipts, closed), pm-ixi1 (provided flag listed as missing, closed) and pm-f05lsg (refusal closure gate, open)."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["agent-ux","claim","github","refusal"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-31T21:13:57.945Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-31T21:13:57.945Z"},{"op":"add","path":"/metadata/author","value":"harness:claude-code"},{"op":"add","path":"/metadata/estimated_minutes","value":180},{"op":"add","path":"/metadata/acceptance_criteria","value":"pm claim --next --start selects, claims and moves the selected item to the workspace's in-progress status in one locked mutation with one history event, honours the selection filters, --if-available and the race-loss walk, enforces configured type workflows, and reports the start transition in the receipt; MCP pm_claim with next and start behaves identically; a claim target refusal never tells the caller to pass a flag already present and carries a typed code; regression tests cover CLI, MCP and the refusal hint"},{"op":"add","path":"/metadata/parent","value":"pm-mpbb"},{"op":"add","path":"/metadata/risk","value":"medium"},{"op":"add","path":"/metadata/confidence","value":"high"},{"op":"add","path":"/metadata/expected_result","value":"pm claim --next --start atomically claims and starts the next available item on CLI, MCP and SDK, and no claim-target refusal recommends a flag the caller already supplied."},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-114v","kind":"related","created_at":"2026-10-31T21:13:57.945Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-f05lsg","kind":"related","created_at":"2026-10-31T21:13:57.945Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-fjxm","kind":"related","created_at":"2026-10-31T21:13:57.945Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-gh1369","kind":"related","created_at":"2026-10-31T21:13:57.945Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-ixi1","kind":"related","created_at":"2026-10-31T21:13:57.945Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"452ba45b449d273bea8de14644ce246d149da8713b45e04284416d103a1a591b","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"65a8d2a37393d4857b1e815fe56f9977d5d868933e3d6fe3021a0afdbea12fa3"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:15:10.678Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1644","lineage:pm-gh1644","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1644","lineage:pm-gh1644","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:15:10.678Z"},{"op":"add","path":"/metadata/assignee","value":"harness:claude-code"},{"op":"add","path":"/metadata/claim_principal","value":"harness:claude-code#8a4268fe12c2a5f9fcd4da8d"}],"before_hash":"452ba45b449d273bea8de14644ce246d149da8713b45e04284416d103a1a591b","after_hash":"05e33d77d1bd32441d170b8ba6a9eded1a26f471490c408ac868cb957505db44","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c224fdfb86693980281b6e883a3f6f777f785ef7c9e25eb884943a316a5db524"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:15:10.785Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1643","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1643+pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1643","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:15:10.785Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"05e33d77d1bd32441d170b8ba6a9eded1a26f471490c408ac868cb957505db44","after_hash":"d9bb61fca0bfed52138c22a69843f754d22699773390ffc826fe116d7e37f622","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7b2bda42e953f0c29800222626b02d9184f60cea65cc894c46acbc5605c41092"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:07.835Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:07.835Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/sdk/lifecycle/claim.ts","scope":"project"}]}],"before_hash":"d9bb61fca0bfed52138c22a69843f754d22699773390ffc826fe116d7e37f622","after_hash":"4d1ef687f0ba269b5b6c5e54b3850a3cc1c53e4f48902cc6bd678630ccef3bb1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"4e3907422b54cea7f6f0f82361ff393501d715c0c01f24085fc112eb52369099"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:08.490Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/0/path","value":"src/sdk/lifecycle/claim-target.ts"},{"op":"add","path":"/metadata/files/1","value":{"path":"src/sdk/lifecycle/claim.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:08.490Z"}],"before_hash":"4d1ef687f0ba269b5b6c5e54b3850a3cc1c53e4f48902cc6bd678630ccef3bb1","after_hash":"0cb18ae8e4db79557f7aa613f9d57f61d82f9d0d1f9be7722b8abcedcb4afbde","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"84773501b89ec08ed96fd8e0328a4a840412e094f6605cba5d5c7de871eb6b20"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:09.182Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/2","value":{"path":"src/sdk/lifecycle/mcp-actions.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:09.182Z"}],"before_hash":"0cb18ae8e4db79557f7aa613f9d57f61d82f9d0d1f9be7722b8abcedcb4afbde","after_hash":"56d5fca10961682b9a53f9d35896f7e283b95e7515df90a38bb8f24ded118df5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0224ac628e2ce8208fec5e24345396119c69ad69902fe4efcce15dfcfcbcec86"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:09.850Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/3","value":{"path":"src/sdk/lifecycle/task-composition.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:09.850Z"}],"before_hash":"56d5fca10961682b9a53f9d35896f7e283b95e7515df90a38bb8f24ded118df5","after_hash":"586034d2727d6d42102e8a212a305fc6404441bb0efd843e7b2b4d01bf22175b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"11b5054109accc2ff6b5a8e3d084fe2a292b6312236e05546c921af381450ee7"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:10.647Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/lifecycle/claim-target.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"src/cli/register-operations.ts"},{"op":"add","path":"/metadata/files/4","value":{"path":"src/sdk/lifecycle/task-composition.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:10.647Z"}],"before_hash":"586034d2727d6d42102e8a212a305fc6404441bb0efd843e7b2b4d01bf22175b","after_hash":"cd3f6eeae6ea70b23eada6bf58877b607145ced83e0638b04e08ddbb9f17d858","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"614a745bdf02cd4e68aa3979185a223e81620530a32501434b9c2adb443559af"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:11.401Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/lifecycle/claim-target.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"add","path":"/metadata/files/5","value":{"path":"src/sdk/lifecycle/task-composition.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:11.401Z"}],"before_hash":"cd3f6eeae6ea70b23eada6bf58877b607145ced83e0638b04e08ddbb9f17d858","after_hash":"fe904eb7089d9ce603870c4f6573a5be11d59502f68a59118e872ab2de8f7982","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1150a9cf67d6e2ea9b6ad1b1288e62fba3ccf6d42ef0b678463152ade3ebfe58"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:12.169Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/6","value":{"path":"src/sdk/runtime-public-contracts.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:12.169Z"}],"before_hash":"fe904eb7089d9ce603870c4f6573a5be11d59502f68a59118e872ab2de8f7982","after_hash":"554417ac05fe2d4bee50cd3f4cd7fceb01834a05a82cd43ecb3da9dfcb9cfbfc","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d9259aba93a1390290b855d7383f8a0356ddb48c7118941c86516c4b4df3e3d3"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:13.050Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/lifecycle/claim-target.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/cli/register-operations.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"scripts/generate-error-code-catalog.mjs"},{"op":"add","path":"/metadata/files/7","value":{"path":"src/sdk/runtime-public-contracts.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:13.050Z"}],"before_hash":"554417ac05fe2d4bee50cd3f4cd7fceb01834a05a82cd43ecb3da9dfcb9cfbfc","after_hash":"5fe689da4c026a9df747a32c92f92745d01c97d7ca7c58321f05efd16c6df8d6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b2955fcb5345c23355b19adcf509a51c035e1a6f7a78992d98e60e9f171fab25"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:13.964Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/8","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:13.964Z"}],"before_hash":"5fe689da4c026a9df747a32c92f92745d01c97d7ca7c58321f05efd16c6df8d6","after_hash":"414e40269a85f832fa590d512c05ecead2516e4d1fc13625253c51223dd955a2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0b7827ccaacc159042cb7744bdb92a048e6c2cf57d5803464eeed92216c2c21b"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:14.818Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:14.818Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/LIFECYCLE_COMMANDS.md","scope":"project"}]}],"before_hash":"414e40269a85f832fa590d512c05ecead2516e4d1fc13625253c51223dd955a2","after_hash":"6bc26cc4bf49fcaa467b57a5d5288462a24bcb73c568e5674c8991be9c9d55ce","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"9910107a010a3cae4543e6885167bc59c1e7efba99a37eba0cc83c5edeadfc96"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:15.753Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/0/path","value":"docs/COMMANDS.md"},{"op":"add","path":"/metadata/docs/1","value":{"path":"docs/LIFECYCLE_COMMANDS.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:15.753Z"}],"before_hash":"6bc26cc4bf49fcaa467b57a5d5288462a24bcb73c568e5674c8991be9c9d55ce","after_hash":"e06275f152edbdc1d1c82f8682ae62bf11da1364fef1e46834f9fdee48bfbdca","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"8d9b48237708ccb87dabc7af8305a52a500aebd32f120e0ce6f4fdd855d4ae76"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:33.611Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:33.611Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts","scope":"project","provenance":{"author":"harness:claude-code","created_at":"2026-10-31T21:46:33.577Z","source_kind":"local_mutation","source_ref":"fix/agent-ux-orient-claim-envelopes"}}]}],"before_hash":"e06275f152edbdc1d1c82f8682ae62bf11da1364fef1e46834f9fdee48bfbdca","after_hash":"086a581a8b3443eba05987df49553606c2bb7cd523bbc6f0c8cdf61e859e8b46","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"f583277336b17d3b81e5980d8255ce3151ca4c7a0be64da5adab43b82d3dac10"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:58.956Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:58.956Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-31T21:46:58.955Z","author":"harness:claude-code","text":"Implementation: runClaim gained an optional start that calls applyClaimStart inside the same mutateItem callback (the release --handoff pattern). It resolves the in-progress status through resolveStartTaskInProgressStatus, refuses a terminal item with terminal_state_conflict even under --force, enforces type workflows via enforceTypeWorkflowTransition, and returns start {from_status,to_status}. runClaimNext forwards start to every candidate in the race-loss walk, so --if-available and filters behave as before. CLI runClaimAction and MCP runMcpClaimAction route start with an explicit id to the unchanged two-step composition, which keeps its typed 'Remove --next and --if-available' refusal; start without an id uses the atomic path or the new requireClaimTarget in src/sdk/lifecycle/claim-target.ts (missing_required_argument / ambiguous_option_combination, attributed to claim through the catalog generator's module map). runStartTask now strips start before its claim step so explicit --start stays claim-then-update. MCP schema: the start+next exclusion is gone and start+ifAvailable is exclusive only with an id. PmClient.claimNext({start:true}) is typed. Verified in a sandbox: claim --next --start → status in_progress, changed_field_count 3, history create→claim (one event)."}]}],"before_hash":"086a581a8b3443eba05987df49553606c2bb7cd523bbc6f0c8cdf61e859e8b46","after_hash":"caae0ce979674f4d2ac1f31c9f3b1498cdbd18e6bfba6e5933b60fbf7c18de97","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7387ea35c538d213b6c4cede15c638adde6ec3d38848d1d59f8bef40cebfc0fc"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:05:45.377Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"remove","path":"/metadata/files/8"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/unit/sdk/lifecycle/claim-start.spec.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:05:45.377Z"}],"before_hash":"caae0ce979674f4d2ac1f31c9f3b1498cdbd18e6bfba6e5933b60fbf7c18de97","after_hash":"6cfb8a235c41161f45be1202840874cc0782c3c12f3760cacd0985665bfc50b8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"e3bfdee87e6f68568d82c1bf61263a687b1fa1a6fb4b0ab0492aaaf0b1b8fabb"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:06:26.339Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"remove","path":"/metadata/files/7"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/unit/sdk/lifecycle/claim-start.spec.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"src/cli/register-operations.ts"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:06:26.339Z"}],"before_hash":"6cfb8a235c41161f45be1202840874cc0782c3c12f3760cacd0985665bfc50b8","after_hash":"925ca9e38c43fe8349775c48bae580bcd4202b06431dcfb2e2937fece4bd5f95","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"bffa5d9c7cfc27eb062fe5ed15feeca40453c508681be77bd393ac3f28a90a14"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:10.963Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:10.963Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_added","owner":"pm-gh1643","gate_id":"tests/unit/sdk/lifecycle/claim-start.spec.ts GH-1643 claim --next --start","negative_control":"git stash push src/cli/register-operations.ts src/sdk/lifecycle/mcp-actions.ts src/sdk/lifecycle/task-composition.ts && node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts; git stash pop (the CLI/MCP start test fails before the fix)","local_checks":["node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts","node --import tsx scripts/release/static-quality-gate.mts"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"925ca9e38c43fe8349775c48bae580bcd4202b06431dcfb2e2937fece4bd5f95","after_hash":"06b461bdae7ff594fe236f36073775ea262496ff47f0ce8666f8f05fb09b7c89","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3dc62a3a748f5a0630ae18143104deefbf3cefbca28a1743457fdccac52474ff"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:24.311Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/index.ts"},{"op":"add","path":"/metadata/files/7","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:24.311Z"}],"before_hash":"06b461bdae7ff594fe236f36073775ea262496ff47f0ce8666f8f05fb09b7c89","after_hash":"f7f1aeb2b557925aaffc9fdc817e3e495c7e4478b76badc728ef52452e71ce0c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c4dbbc9570eca3adfd0aa08cd02f8c080e4e820d64699e575a28fc5edea3d729"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:25.560Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/core.ts"},{"op":"add","path":"/metadata/files/8","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:25.560Z"}],"before_hash":"f7f1aeb2b557925aaffc9fdc817e3e495c7e4478b76badc728ef52452e71ce0c","after_hash":"d24b4f86fd6b4c5cb18fa5bdb3a16beda566c1447c17da8003d025e6488beb9a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"ab3e9dfa25b1fbdb86de8548fd56fee544cade2bf1be2e7726b0452de62b0e45"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:27.119Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/runtime.ts"},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:27.119Z"}],"before_hash":"d24b4f86fd6b4c5cb18fa5bdb3a16beda566c1447c17da8003d025e6488beb9a","after_hash":"bdd39db7ccb0f7a564b5fbdc9c053c9240f96cb3fa6976e61a304639830e3df9","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a4c9c38f5ef9b94b128ca95dbd6488e53ed55c6be618470dcc0eb33f71b0b7b6"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:29.178Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/9/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"add","path":"/metadata/files/10","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:29.178Z"}],"before_hash":"bdd39db7ccb0f7a564b5fbdc9c053c9240f96cb3fa6976e61a304639830e3df9","after_hash":"5749283a37c663d839cb88f68d1a008714591766209fb7ab4c5fa9aff47c1015","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1aa2e402d11c8de412ff0d99d9cf950b9692762b9aeffbefd45cae408e06e8bf"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:31.318Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/10/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"add","path":"/metadata/files/11","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:31.318Z"}],"before_hash":"5749283a37c663d839cb88f68d1a008714591766209fb7ab4c5fa9aff47c1015","after_hash":"553b270dd0ed218fdd015adb0cf47613bbceb34c8ee1e5bf186709b8a4dd06f2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"865bc0217997aaf877d5165a0dff2e373fa4ce0455da24688ba0339894b8386c"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:33.211Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/11/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"add","path":"/metadata/files/12","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:33.211Z"}],"before_hash":"553b270dd0ed218fdd015adb0cf47613bbceb34c8ee1e5bf186709b8a4dd06f2","after_hash":"650867aa3d49269d0d3315d0139a49c1b9937445ad4dc475e2f8f14992c2e934","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"446e108b612a8bb645c7baa8479274182d111f799eaffce9e4d3822170cb015e"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:35.020Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/12/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/11/path","value":"tests/fixtures/contracts/full.json"},{"op":"add","path":"/metadata/files/13","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:35.020Z"}],"before_hash":"650867aa3d49269d0d3315d0139a49c1b9937445ad4dc475e2f8f14992c2e934","after_hash":"6d283ec845ca940b6978f39b0b706c391d024b601fe5c481743a0ac7b3660b0d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"f9a9bd646803fa1295421bac0fa3b576c12297b11318bbc081bad06ba86d6bfc"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:36.552Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/13/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/12/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/11/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/core.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/cli/register-operations.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"scripts/release/surface-replication-sets.json"},{"op":"add","path":"/metadata/files/14","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:36.552Z"}],"before_hash":"6d283ec845ca940b6978f39b0b706c391d024b601fe5c481743a0ac7b3660b0d","after_hash":"f75072b37a52f860a2c26df239f919d6d7fb88375835ef91c72d0f8dbe66aa3a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1a691b482747cc4f531f34053bab329a4d7d7281ba59f880786340d4eecc93e4"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:37.729Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/14/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/13/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/12/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/11/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/core.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/cli/register-operations.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"sdk/public-surface.json"},{"op":"add","path":"/metadata/files/15","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:37.729Z"}],"before_hash":"f75072b37a52f860a2c26df239f919d6d7fb88375835ef91c72d0f8dbe66aa3a","after_hash":"1824dd491107af5c58293cbaaaa4a390933bf593a4b0571963e5906cdbbc9f7c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"35d330a9d0cbe8a12718ba4d70223a09d26aaf2b8f8230b7c2cc18e3fcaf1fbc"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:29:48.579Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:29:48.579Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz4r6h-yrwyti","kind":"test","status":"passed","started_at":"2026-10-31T22:29:33.718Z","finished_at":"2026-10-31T22:29:48.568Z","recorded_at":"2026-10-31T22:29:48.568Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"1824dd491107af5c58293cbaaaa4a390933bf593a4b0571963e5906cdbbc9f7c","after_hash":"ee2a77cf030267de2614814bd70d47e2d6083e14e1bd91128cd11a92b1baba9e","item_hash_version":3,"message":"Track test run summary (test-local-mvwz4r6h-yrwyti)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"bf54e63809726f36ae5f0f0bb3c160f892857542190e9e6df36fadfd983907f0"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:19.316Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"replace","path":"/metadata/expected_result","value":"pm claim --next --start claims and starts the next available item in one write on every transport, and no claim-target refusal recommends a flag the caller supplied."},{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:19.316Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:19.294Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:19.294Z"},{"op":"add","path":"/metadata/resolution","value":"runClaim accepts start and applyClaimStart moves the claimed item to the configured in-progress status inside the same mutateItem callback. That gives one claim history event, type-workflow enforcement, terminal_state_conflict for terminal items, and a start {from_status,to_status} receipt. runClaimNext forwards start through the race-loss walk. The CLI runClaimAction and MCP runMcpClaimAction route an id-less start to that path, while an explicit-id start keeps the claim-then-update composition and its typed refusal. requireClaimTarget now lives in the SDK claim module with missing_required_argument and ambiguous_option_combination codes and never names a flag the caller already passed. PmClient.claimNext accepts start. The MCP schema drops the start+next exclusion, and start+ifAvailable is exclusive only with an id."},{"op":"add","path":"/metadata/actual_result","value":"Sandbox: pm claim --next --start returns status in_progress with changed_field_count 3 and history create then claim. MCP claim {next,start} and PmClient.claimNext({start:true}) return in_progress. pm claim --start refuses with missing_required_argument, suggesting pm claim --next --start. claim-start spec 7/7, register-commands 91/91, and lifecycle integration specs pass. With the routing stashed, the CLI/MCP start test fails. Full suite 11,748 passed."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: claim --next --start is one locked select-claim-start write on CLI, MCP and SDK"}],"before_hash":"ee2a77cf030267de2614814bd70d47e2d6083e14e1bd91128cd11a92b1baba9e","after_hash":"065140ff634b467fa999693a1a3660e132d5b6c8c791d0325d527b35dec738d2","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:19.294Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2ebf8ce0a74563221e3b28982ca6a676366966aa20c3ba6bbef32ae94300bf01"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:35.818Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:35.818Z"}],"before_hash":"065140ff634b467fa999693a1a3660e132d5b6c8c791d0325d527b35dec738d2","after_hash":"af534f93266d1c1d470f4b168a59227bbe92e6f2f8b18be86fe6825a59f884f6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0be7ee2c239d055d56b5abd635e86d77b85daeee7e6113038ee48ea8a827d8df"} diff --git a/.agents/pm/history/pm-gh1644.jsonl b/.agents/pm/history/pm-gh1644.jsonl new file mode 100644 index 0000000000..f8f1ef7367 --- /dev/null +++ b/.agents/pm/history/pm-gh1644.jsonl @@ -0,0 +1,25 @@ +{"hash_algorithm":"sha256","ts":"2026-10-31T21:13:36.728Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-gh1644"},{"op":"add","path":"/metadata/title","value":"GH-1644: context --for orient silently reduces an explicit --limit to the intent's budget-derived row ceiling, and pm's own quickstart recommends that command"},{"op":"add","path":"/metadata/description","value":"https://github.com/unbraind/pm-cli/issues/1644\n\nReproduced on the current source build (2026.10.31) in a fresh 6-task sandbox and on the real tracker: `pm context --limit 10 --for orient` returns filters.limit \"3\" in either flag order, while `pm context --limit 10` returns 10. The intent applier (CONTEXT_INTENT_DEFAULT_APPLIERS in src/sdk/context-intent-contracts.ts) computes a budget-derived ceiling ((3000 - 1200) / 600 = 3 rows) and replaces the caller's --limit with min(requested, derived). The context_intent receipt reports binding_constraint token_budget but neither the requested value nor a warning; filters.limit shows only the effective 3. list --for triage and search --for discover use the same clamp.\n\nMeasured on the real tracker (3173 items): 3 orient rows serialize to ~1,680 JSON tokens, 5 rows ~2,724, 10 rows ~5,937. The 3000-token preset therefore genuinely cannot carry 10 rows, so the preset ceiling is correct; the defects are (1) the silent override, (2) filters reporting only the effective value, and (3) pm's own agent quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE) recommending `pm context --limit 10 --for orient`, a self-contradicting command. The measured canonical in token-budget-gate is `context --for orient` without --limit.\n\nDuplicate search: 'context orient preset limit', 'for orient intent preset', GH-1644; nearest are pm-gy885b (intent discoverability, closed), pm-3crymx (intent returned no context, closed) and pm-ka6d (canonical cold-start docs, closed)."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["agent-ux","context","github","token-efficiency"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-31T21:13:36.728Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-31T21:13:36.728Z"},{"op":"add","path":"/metadata/author","value":"harness:claude-code"},{"op":"add","path":"/metadata/estimated_minutes","value":150},{"op":"add","path":"/metadata/acceptance_criteria","value":"An explicit row limit above the intent's budget-derived ceiling yields a typed limit_overridden_by_intent warning naming --limit, the requested and effective values, and a --token-budget that would carry the request; filters reports requested_limit beside the effective limit whenever they differ; the context_intent receipt records requested_limit; the same behaviour holds for context, list --for triage and search --for discover; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers every row-bounded intent with an explicit over-ceiling limit"},{"op":"add","path":"/metadata/parent","value":"pm-5t33or"},{"op":"add","path":"/metadata/risk","value":"medium"},{"op":"add","path":"/metadata/confidence","value":"high"},{"op":"add","path":"/metadata/expected_result","value":"Every row-bounded --for intent either honours an explicit --limit or reports a typed override naming the flag, the requested and effective values and the token budget that would honour it; pm's quickstart no longer recommends a command whose flag is silently overridden."},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-3crymx","kind":"related","created_at":"2026-10-31T21:13:36.728Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-gy885b","kind":"related","created_at":"2026-10-31T21:13:36.728Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-ka6d","kind":"related","created_at":"2026-10-31T21:13:36.728Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"aa58fdbd71635c85f4f104992b1f68e78c956651f6c18eb18ce1608f960330a0","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"29a0baed6819128b6c61d240a6b3894372abbe9308d74bff83e76712206bcebe"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:15:09.913Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:15:09.913Z"},{"op":"add","path":"/metadata/assignee","value":"harness:claude-code"},{"op":"add","path":"/metadata/claim_principal","value":"harness:claude-code#8a4268fe12c2a5f9fcd4da8d"}],"before_hash":"aa58fdbd71635c85f4f104992b1f68e78c956651f6c18eb18ce1608f960330a0","after_hash":"4a12c0ac2684b9316df62b5768fa08e6fc9e8743630979a879bc0cdbbc5f66e2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"6b63f19fa4be9ccc6ddf8818551df4276780af25f7ba5c2405778e3e43d18efd"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:15:10.168Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1644","lineage:pm-gh1644","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1644","lineage:pm-gh1644","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:15:10.168Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"4a12c0ac2684b9316df62b5768fa08e6fc9e8743630979a879bc0cdbbc5f66e2","after_hash":"71cdcfe833e228b11f69ba5c26ddf3c7ded380570489e7fae3dd472c3bfd0b02","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7bc5cf892bbfdf72bfd648fcb5d34b762543910c4d3ad51d08ffe8596bfa7b7a"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:16.475Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:16.475Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/sdk/context-intent-contracts.ts","scope":"project"}]}],"before_hash":"71cdcfe833e228b11f69ba5c26ddf3c7ded380570489e7fae3dd472c3bfd0b02","after_hash":"c3a27fc557d8614827f33b24b142cdb269f3390ba4a331c8e3ad7f8d1ab74b4f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c1b76abae7e25cefd2fdefeef5bf1fddf65badcb7372401e2818a667603b097d"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:17.249Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/1","value":{"path":"src/sdk/init-agent-guidance.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:17.249Z"}],"before_hash":"c3a27fc557d8614827f33b24b142cdb269f3390ba4a331c8e3ad7f8d1ab74b4f","after_hash":"3663f594005098998ea2a5fd6cf8fbf7b5b5607a0e523b6fc0a8e8fe7708d95a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"52762ee5470c4e0c0c7879b58d0fae7c3329470b94b05f0b7c9019a616ca3294"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:18.110Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/context-intent-contracts.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"docs/agent-task-transcripts.json"},{"op":"add","path":"/metadata/files/2","value":{"path":"src/sdk/init-agent-guidance.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:18.110Z"}],"before_hash":"3663f594005098998ea2a5fd6cf8fbf7b5b5607a0e523b6fc0a8e8fe7708d95a","after_hash":"c84c0f4ea8a3f161ce7e8325ba3f786b01c37d0fc249290b814f0b403e6da366","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"7379abe76ad12e5aeccff9b022d088db112a4b3561f530b3ddc25e78d6e986b0"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:19.076Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/3","value":{"path":"tests/unit/sdk/context/intent-limit-override.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:19.076Z"}],"before_hash":"c84c0f4ea8a3f161ce7e8325ba3f786b01c37d0fc249290b814f0b403e6da366","after_hash":"68dae6258e72032eeafad29ab85094768c2c9742077ebb8e2cbee16cd43a565d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"018fb2f5a4a224687a09ba3e04cbb80348700af82d9a408f119c0a48ddefa1d0"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:19.782Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/3/path","value":"tests/unit/scripts/release/agent-task-token-gate.spec.ts"},{"op":"add","path":"/metadata/files/4","value":{"path":"tests/unit/sdk/context/intent-limit-override.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:19.782Z"}],"before_hash":"68dae6258e72032eeafad29ab85094768c2c9742077ebb8e2cbee16cd43a565d","after_hash":"8a66137c060ac989b1303da1a1b23f644acedcc6548e4cd3080bb3739bdb92fa","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"89cae305905b99264a71a707cbb2eda30025eea3192d5cb592af9f5dd1254a64"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:20.977Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/5","value":{"path":"tests/unit/sdk/recovery/recovery-guidance.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:20.977Z"}],"before_hash":"8a66137c060ac989b1303da1a1b23f644acedcc6548e4cd3080bb3739bdb92fa","after_hash":"0936de4ab221be26a016aec64ce5789365f0d6ae45d871695b48108347124c97","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c598974daf82ffce2a01fc6a8a4c895c2d453b1bffcb41b7e9dd7798d97b3b54"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:21.917Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:21.917Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"AGENTS.md","scope":"project"}]}],"before_hash":"0936de4ab221be26a016aec64ce5789365f0d6ae45d871695b48108347124c97","after_hash":"3bc4286cb24e9bd2c84f673f56ff3417eda5a475a0c27e9cc302cab448c9675a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a38abd83f4e77082b8f17c99c5ca5687f99447b345c213d23bb0b33dedc26507"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:22.634Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"add","path":"/metadata/docs/1","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:22.634Z"}],"before_hash":"3bc4286cb24e9bd2c84f673f56ff3417eda5a475a0c27e9cc302cab448c9675a","after_hash":"25c883447e929b5d37e7fe63e26bcf39ec39d32c85df8f72349b44ff05d4072a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"773adca94fa4e5fbfb957c08e00f501a90fcb531b6bb24f964e47f2d6ed1006f"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:23.513Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/1/path","value":"docs/AGENT_GUIDE.md"},{"op":"add","path":"/metadata/docs/2","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:23.513Z"}],"before_hash":"25c883447e929b5d37e7fe63e26bcf39ec39d32c85df8f72349b44ff05d4072a","after_hash":"0d47f09814337887679ca6da9e0f86e59fba182fcede31607feb9c86c804a22c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2ec193189cbde4a567fdcc9be04607f8c2d6bc2ca9f25fa9ac80b9450401c6b8"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:24.263Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/2/path","value":"docs/COMMAND_NAMESPACES.md"},{"op":"add","path":"/metadata/docs/3","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:24.263Z"}],"before_hash":"0d47f09814337887679ca6da9e0f86e59fba182fcede31607feb9c86c804a22c","after_hash":"622e7a77f227775fe6dccc9d5ec52fa17e4101d8c494dadb6c8903d5ba466e4a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"01902205374d8a7175189b5f3f8d4df24ddfc0df0343259277d9b206901b75de"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:25.006Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/3/path","value":"docs/SDK_ACTION_CONFORMANCE.md"},{"op":"add","path":"/metadata/docs/4","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:25.006Z"}],"before_hash":"622e7a77f227775fe6dccc9d5ec52fa17e4101d8c494dadb6c8903d5ba466e4a","after_hash":"d3e98799ccec8fdd135fd5f6d65e0458da0b2bd1874c58017c8af51a20e04f2f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b8b5fbe5c0e7ee1b9a291661b68f06edd02ec718e8eaf5b8eeb5c2f81e0b0b21"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:25.913Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/docs/4/path","value":"docs/SDK_ACTION_CONFORMANCE.md"},{"op":"replace","path":"/metadata/docs/3/path","value":"docs/COMMAND_NAMESPACES.md"},{"op":"replace","path":"/metadata/docs/2/path","value":"docs/AGENT_GUIDE.md"},{"op":"replace","path":"/metadata/docs/1/path","value":"AGENTS.md"},{"op":"replace","path":"/metadata/docs/0/path","value":".agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md"},{"op":"add","path":"/metadata/docs/5","value":{"path":"README.md","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:25.913Z"}],"before_hash":"d3e98799ccec8fdd135fd5f6d65e0458da0b2bd1874c58017c8af51a20e04f2f","after_hash":"37c8112bf4c2146e9241b8d13302243b5df94a19343105013422bb765d9f2c67","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a7594c4d52778bb86b5ec86db455c080ba71458363fda0c1bf6f2d13bdb0f1c9"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:34.581Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:34.581Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts","scope":"project","provenance":{"author":"harness:claude-code","created_at":"2026-10-31T21:46:34.470Z","source_kind":"local_mutation","source_ref":"fix/agent-ux-orient-claim-envelopes"}}]}],"before_hash":"37c8112bf4c2146e9241b8d13302243b5df94a19343105013422bb765d9f2c67","after_hash":"7916b0f769a2d4115f2eb0068f493724491a2622d925b3d528e05dfd994d49f3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c354b6857f02ad48dce6d883e67d24ecc0d3c77f6ce2c04091b690c227128d0a"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:59.575Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:59.575Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-31T21:46:59.575Z","author":"harness:claude-code","text":"Implementation: the three row-bounded intent appliers share applyIntentRowLimit, which keeps the existing min(requested, budget-derived) clamp but stashes a reduced request as intentRequestedLimit. attachContextIntentReceipt calls discloseIntentLimitOverride, which reads that key or the raw host --limit and writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning 'limit_overridden_by_intent:--limit N->M'. token_budget_for_requested = max(current budget, overhead + rowCost*requested) from the shared resolveIntentRowCost table. Measured on this tracker: pm context --for orient --limit 10 now prints the override with restore_with 'pm context --for orient --limit 10 --token-budget 7200'. That retry returns 10 rows at an estimated 4,637 tokens within budget. list --for triage --limit 150 → effective 100, retry with --token-budget 3200 honours 150. The quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, pm-user ECOSYSTEM_PLANNING) and the replayed agent-task transcript now use the measured canonical pm context --for orient."}]}],"before_hash":"7916b0f769a2d4115f2eb0068f493724491a2622d925b3d528e05dfd994d49f3","after_hash":"c91250aafbc301adb3987ffe8a0365cd377b3bbcbe8e3a52b7e91fb37a146d9c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"04aec77aed8bf5911a7dd78ecae728b666e2f2353d8b3bf6fadc66c557da66cb"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:11.774Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:11.774Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_added","owner":"pm-gh1644","gate_id":"tests/unit/sdk/context/intent-limit-override.spec.ts GH-1644 intent limit override","negative_control":"git stash push src/sdk/context-intent-contracts.ts && node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts; git stash pop (3 of 3 fail before the fix)","local_checks":["node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts","node scripts/release/agent-task-token-gate.mjs"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"c91250aafbc301adb3987ffe8a0365cd377b3bbcbe8e3a52b7e91fb37a146d9c","after_hash":"e6929fdd276115070a026e6af74ccc7ffa2a262e67f07d47d59521bcbc9f2f95","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"88473550d09350b168c0c9aa9ff47be6b05630fc18dcb5b4d71267baf75d49b6"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:39.272Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/5/path","value":"tests/unit/sdk/context/intent-limit-override.spec.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/unit/scripts/release/agent-task-token-gate.spec.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/init-agent-guidance.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/context-intent-contracts.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"docs/agent-task-transcripts.json"},{"op":"replace","path":"/metadata/files/0/path","value":"docs/agent-task-token-baseline.json"},{"op":"add","path":"/metadata/files/6","value":{"path":"tests/unit/sdk/recovery/recovery-guidance.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:39.272Z"}],"before_hash":"e6929fdd276115070a026e6af74ccc7ffa2a262e67f07d47d59521bcbc9f2f95","after_hash":"e5ee5847c12bd65c94e9439bfa13968bc2b0d832a40e85b70cb9ba11c04a4ca5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"5eb75b666ca92a716a0d67366cdd0aebc79346b03e128d0838ab3ac41919b7c8"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:40.648Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/6/path","value":"tests/unit/sdk/context/intent-limit-override.spec.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/unit/scripts/release/agent-task-token-gate.spec.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"tests/integration/cli.integration.spec.ts"},{"op":"add","path":"/metadata/files/7","value":{"path":"tests/unit/sdk/recovery/recovery-guidance.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:40.648Z"}],"before_hash":"e5ee5847c12bd65c94e9439bfa13968bc2b0d832a40e85b70cb9ba11c04a4ca5","after_hash":"54187011ed1ac828f36facb28ce74e0e7d72f67fabcd89da0aaaa508ca538562","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"e54a3bea5c845645a1aee1b4e3655f33c769ecac7fec20496bc6f24a52353572"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:42.253Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/7/path","value":"tests/unit/sdk/context/intent-limit-override.spec.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/unit/scripts/release/agent-task-token-gate.spec.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/unit/commands/workspace/init-command.spec.ts"},{"op":"add","path":"/metadata/files/8","value":{"path":"tests/unit/sdk/recovery/recovery-guidance.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:42.253Z"}],"before_hash":"54187011ed1ac828f36facb28ce74e0e7d72f67fabcd89da0aaaa508ca538562","after_hash":"b75a6d09c9c6b13546d143d038fd6eb9a88dcf9acf0def3723c4d3ba7ec2b222","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"c444cb12598b2fa4871395077fad81b327978d2d0d3ea8ea66e53171d6cc39d4"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:43.594Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/8/path","value":"tests/unit/sdk/context/intent-limit-override.spec.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"tests/unit/scripts/release/agent-task-token-gate.spec.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"tests/unit/commands/workspace/init-command.spec.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"tests/integration/cli.integration.spec.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/init-agent-guidance.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/sdk/context-intent-contracts.ts"},{"op":"replace","path":"/metadata/files/2/path","value":"sdk/public-surface.json"},{"op":"add","path":"/metadata/files/9","value":{"path":"tests/unit/sdk/recovery/recovery-guidance.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:43.594Z"}],"before_hash":"b75a6d09c9c6b13546d143d038fd6eb9a88dcf9acf0def3723c4d3ba7ec2b222","after_hash":"ad12283fcfc2badd6bc167dbc0c87863824761b35f8cc6bd6b5b2d0839e80f82","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b08e4c678099ec196b73a179544ad57804d8edc1baffd18520da38a945f1f643"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:29:58.500Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:29:58.500Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz4yu4-gbikjl","kind":"test","status":"passed","started_at":"2026-10-31T22:29:49.127Z","finished_at":"2026-10-31T22:29:58.492Z","recorded_at":"2026-10-31T22:29:58.492Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"ad12283fcfc2badd6bc167dbc0c87863824761b35f8cc6bd6b5b2d0839e80f82","after_hash":"acf2c6ba2acf7f46243970de22e6f6afad9c32684ccd47f71572c9f2cc1827bf","item_hash_version":3,"message":"Track test run summary (test-local-mvwz4yu4-gbikjl)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d0d19b26efcb4247093f738661ecb798197d3e52e231011d2e2c443265f7a238"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:33.712Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"replace","path":"/metadata/expected_result","value":"Every row-bounded --for intent either honours an explicit --limit or reports a typed override with the requested and effective values and a retry that keeps the limit; pm's quickstart recommends no silently overridden flag."},{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:33.712Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:33.693Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:33.693Z"},{"op":"add","path":"/metadata/resolution","value":"The three row-bounded intent appliers share applyIntentRowLimit, which keeps the budget-derived ceiling and records a reduced request. attachContextIntentReceipt writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning limit_overridden_by_intent:--limit N->M for context, list and search intents, reading either projected or raw host options. The quickstart generator, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and both replayed agent-task transcripts use pm context --for orient. The quickstart no longer calls explicit --start atomic."},{"op":"add","path":"/metadata/actual_result","value":"Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning, and restore_with pm context --for orient --limit 10 --token-budget 7200. That retry returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100. The agent-task token baseline changed only its transcript digest (identical output). intent-limit-override spec 3/3 passes; 3/3 fail with the fix stashed."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: an intent budget that reduces an explicit --limit now discloses the override, and the quickstart recommends the measured canonical orientation"}],"before_hash":"acf2c6ba2acf7f46243970de22e6f6afad9c32684ccd47f71572c9f2cc1827bf","after_hash":"7912de92c4e92670b8de69abe871b765f67f62c3c906b3d5a9a960911409874c","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:33.693Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"96adbb1f390a825d3f60f3490486770f7b6da938c34fa546b500b73bb56f442d"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:36.414Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1644","claim:pm-hke9z7","release:pm-gh1643"]},"topic":{"value":"workset:pm-gh1642+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1644","claim:pm-hke9z7","release:pm-gh1643"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:36.414Z"}],"before_hash":"7912de92c4e92670b8de69abe871b765f67f62c3c906b3d5a9a960911409874c","after_hash":"3af5415b0896757a4f376196cef6875357d653157167a31a9fbbaabdb141632a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"83318603431cc4b37507ee923d6e4821fdc6597bf7dfddb8375bb90bb923e712"} diff --git a/.agents/pm/history/pm-hke9z7.jsonl b/.agents/pm/history/pm-hke9z7.jsonl new file mode 100644 index 0000000000..4ccba6d407 --- /dev/null +++ b/.agents/pm/history/pm-hke9z7.jsonl @@ -0,0 +1,13 @@ +{"hash_algorithm":"sha256","ts":"2026-10-31T21:27:47.547Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1642","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1642","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-hke9z7"},{"op":"add","path":"/metadata/title","value":"MCP read tools silently ignore declared flat parameters: pm_context reads none, and for/tokenBudget/depth on pm_list, pm_search, pm_next and pm_get never reach the intent layer"},{"op":"add","path":"/metadata/description","value":"Discovered while verifying pm-gh1644 over MCP (published 2026.10.31 and current source both reproduce).\n\nEvery narrow MCP read tool declares its action parameters flat in its inputSchema (pm_context declares for, limit, depth, section, fields, tokenBudget, ...; pm_list/pm_search/pm_next/pm_get declare for and tokenBudget). The SDK runtime only moves a flat argument into the options record the handlers read when it appears in the hand-maintained HOISTED_ACTION_OPTION_KEYS table in src/sdk/runtime-input.ts. That table has no context entry at all and omits for/tokenBudget (and most filters) from list, search, next and get. A flat argument outside the table is accepted without a warning (it is declared) and then dropped.\n\nMeasured in a fresh sandbox through the MCP tools/call handler, flat vs nested options: pm_context {depth: deep} → depth brief (nested → deep); pm_context {for: orient, limit: 10} → no intent, limit null, token_budget 1600 (nested → orient, limit 3); pm_list {for: triage}, pm_search {query, for: discover}, pm_next {for: execute}, pm_get {id, for: inspect} → no context_intent receipt (nested → applied); pm_next {limit: 1} → 5 rows (nested → 1). The declared intent layer is therefore unreachable for MCP hosts that send the flat arguments the schema advertises. Same defect family as pm-wcaa (pm_files declared-but-ignored params, closed individually) and pm-hno5.\n\nDuplicate search: 'MCP pm_context flat arguments ignored options', 'MCP declared parameter silently ignored', 'mcp for intent not applied' — no open item."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":1},{"op":"add","path":"/metadata/tags","value":["agent-ux","context","contracts","mcp"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-31T21:27:47.547Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-31T21:27:47.547Z"},{"op":"add","path":"/metadata/author","value":"harness:claude-code"},{"op":"add","path":"/metadata/estimated_minutes","value":120},{"op":"add","path":"/metadata/acceptance_criteria","value":"For context, get, list, next and search, every optional parameter the action's tool contract declares is hoisted from flat MCP arguments into the handler options (derived from PM_TOOL_ACTION_PARAMETER_CONTRACTS, not a hand list), nested options still win; flat and nested calls produce identical results for for, limit, depth and tokenBudget on all five tools; a regression test iterates every declared optional key of the five actions and fails if one is not hoisted"},{"op":"add","path":"/metadata/parent","value":"pm-5k4v"},{"op":"add","path":"/metadata/risk","value":"high"},{"op":"add","path":"/metadata/confidence","value":"high"},{"op":"add","path":"/metadata/expected_result","value":"An MCP host that sends the flat parameters a read tool's schema declares gets the same intent, projection and limits as the nested options form."},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-gh1644","kind":"discovered_from","created_at":"2026-10-31T21:27:47.547Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-hno5","kind":"related","created_at":"2026-10-31T21:27:47.547Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-wcaa","kind":"related","created_at":"2026-10-31T21:27:47.547Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"6dbc88b03472e9eb3f3f58b362c375231c56b2afa66c996a0a90add54340e992","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"a0334c493436144b67d1076e4d4c5be646759351c684aad3cbdb3c6b71305080"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:27:52.479Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1642","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","lineage:pm-gh1642","lineage:pm-5t33or","lineage:pm-n7rr","lineage:pm-mpbb","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:27:52.479Z"},{"op":"add","path":"/metadata/assignee","value":"harness:claude-code"},{"op":"add","path":"/metadata/claim_principal","value":"harness:claude-code#8a4268fe12c2a5f9fcd4da8d"}],"before_hash":"6dbc88b03472e9eb3f3f58b362c375231c56b2afa66c996a0a90add54340e992","after_hash":"17fe0ecd2d383ad9ba0e66d7bf15e8277feaf3177d450a4546aaf9f99ba3ccef","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"5d7d3a59f08278ede0f4ffaf085a119a5dba7e352848c5e4a5ffb7a6fbb090d5"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:27:52.615Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:27:52.615Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"17fe0ecd2d383ad9ba0e66d7bf15e8277feaf3177d450a4546aaf9f99ba3ccef","after_hash":"a4e22578a0bc23a81c36ce39c9ca3d385bc578c931ff6a714f9479245bf38e12","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"4524f0440d9a8f2167e3d94905b5c886a382bacce861e8f53c9372e2521193dc"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:31.306Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:31.306Z"},{"op":"add","path":"/metadata/files","value":[{"path":"src/sdk/runtime-input.ts","scope":"project"}]}],"before_hash":"a4e22578a0bc23a81c36ce39c9ca3d385bc578c931ff6a714f9479245bf38e12","after_hash":"344ba5e646fe36e2c7c1ab7134fcf4927fa10ba3c8d90da6719f553f1d3ce9fc","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"ccb97af11b128448f9c61de7d55185441e97f29049049cb82728aca7b62c7a7e"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:32.074Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/1","value":{"path":"tests/unit/mcp/read-tool-flat-parameters.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:32.074Z"}],"before_hash":"344ba5e646fe36e2c7c1ab7134fcf4927fa10ba3c8d90da6719f553f1d3ce9fc","after_hash":"95325de373403322d34c6e65c2898776838eeedf8926948e0f2953c5e60dbb18","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"54af2e8d4d52a7ad70361c3e92efcf0ac0a6c051a29f7c40d1f71ba4e5c0c8e8"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:32.872Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:32.872Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"docs/SDK_ACTION_CONFORMANCE.md","scope":"project"}]}],"before_hash":"95325de373403322d34c6e65c2898776838eeedf8926948e0f2953c5e60dbb18","after_hash":"4e3a27d8f36d40bb2b4fac37d86a15043e507e1f580eb084b2c3066d80cd4466","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"16615fd6f073cec4f3c88c2b91bea7efae5ba5a88b1656533c1774b9fde84fcf"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:46:36.203Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:46:36.203Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts","scope":"project","provenance":{"author":"harness:claude-code","created_at":"2026-10-31T21:46:36.177Z","source_kind":"local_mutation","source_ref":"fix/agent-ux-orient-claim-envelopes"}}]}],"before_hash":"4e3a27d8f36d40bb2b4fac37d86a15043e507e1f580eb084b2c3066d80cd4466","after_hash":"5a9b5c5bc7bff49be855bfbc0e2d956b7614dfcaa8673f19d5e351d2eb4c9e5a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a2114832df6c1c08301c87141a40012e7cb4b3126290b8090a7874029d7a195a"} +{"hash_algorithm":"sha256","ts":"2026-10-31T21:47:01.751Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T21:47:01.751Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-10-31T21:47:01.751Z","author":"harness:claude-code","text":"Implementation: runtime-input.ts derives hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional (declaredReadActionOptionKeys), after the hand table, so nested options still win. Verified through the MCP tools/call handler: flat and nested calls now agree for pm_context depth/for/limit, pm_list/pm_search/pm_next/pm_get for, pm_get depth and pm_next limit. The regression spec iterates every declared optional key of the five actions, so a new declared parameter can no longer be silently dropped."}]}],"before_hash":"5a9b5c5bc7bff49be855bfbc0e2d956b7614dfcaa8673f19d5e351d2eb4c9e5a","after_hash":"8fd91f4f1778dd03ab0b7f6a8b2489c4a56fd8fb646081e06884ecbc80755d45","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"fb11a9b7c7856be740f6bb71badaa770b34c6c25eea646ce47f1251ac5d165f3"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:13.606Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:13.606Z"},{"op":"add","path":"/metadata/escape_class","value":"production_defect"},{"op":"add","path":"/metadata/gate_evidence","value":{"disposition":"gate_added","owner":"pm-hke9z7","gate_id":"tests/unit/mcp/read-tool-flat-parameters.spec.ts declared flat parameter census","negative_control":"git stash push src/sdk/runtime-input.ts src/sdk/runtime/actions.ts && node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts; git stash pop (6 of 7 fail before the fix)","local_checks":["node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts tests/integration/mcp-handshake.spec.ts"],"hosted_checks":["CI / Gates (static)","CI / Gates (coverage)"]}}],"before_hash":"8fd91f4f1778dd03ab0b7f6a8b2489c4a56fd8fb646081e06884ecbc80755d45","after_hash":"e7680e23cfddc8e1f8d8537265aefe2e8f4faae193c0aaa372065d6aeb3ec6f8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"959218d8c3919449870a905a1a52f175f428d613938d281c6769fd45125a12c7"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:11:44.841Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/runtime/actions.ts"},{"op":"add","path":"/metadata/files/2","value":{"path":"tests/unit/mcp/read-tool-flat-parameters.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:11:44.841Z"}],"before_hash":"e7680e23cfddc8e1f8d8537265aefe2e8f4faae193c0aaa372065d6aeb3ec6f8","after_hash":"c0e7cd115e8e46bb44abd346bd43913bfff90ccb617e12d26232137c90064179","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"b2c54f6cc7b2e15d2bd7b67cfdf4f0a6f9dd09a570599de20cfe51e8652a5876"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:30:21.461Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:30:21.461Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz5gjx-0ayygj","kind":"test","status":"passed","started_at":"2026-10-31T22:30:08.071Z","finished_at":"2026-10-31T22:30:21.453Z","recorded_at":"2026-10-31T22:30:21.453Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"c0e7cd115e8e46bb44abd346bd43913bfff90ccb617e12d26232137c90064179","after_hash":"a2afdffa5b525c61c291a77a756911d0d9ee332f9482821ce48758019960b245","item_hash_version":3,"message":"Track test run summary (test-local-mvwz5gjx-0ayygj)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"3c2b5252ab4053b440645359e17d2f10ed09f55ea105c3dfdd5131ba2c42d191"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:35.103Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:35.103Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:35.081Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:35.081Z"},{"op":"add","path":"/metadata/resolution","value":"runtime-input.ts derives flat-to-options hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional, excluding the query/keywords operands. One hoisted-key list replaces three loops, and nested options still win. runMcpSearchAction reads query, then the declared keywords alias, then refuses with the unchanged missing-query error."},{"op":"add","path":"/metadata/actual_result","value":"Through the MCP tools/call handler, flat and nested now agree for pm_context depth/for/limit, for on pm_list/pm_search/pm_next/pm_get, pm_get depth and pm_next limit. The flat orient call also discloses the limit override. read-tool-flat-parameters spec 7/7 iterates every declared optional key; 6/7 fail with the fix stashed. mcp-handshake hoist expectations pass."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: MCP read tools read every declared flat parameter and pm_search honours its keywords alias"}],"before_hash":"a2afdffa5b525c61c291a77a756911d0d9ee332f9482821ce48758019960b245","after_hash":"50e1db555085a5bc7e76ea6f1038f330ebd57ee081ce3feab59f8912fce21ceb","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:35.081Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"75aaf770cabd5ed75a190c93e47c30786c7109187d49f4b36d24e1b3e41bb080"} +{"hash_algorithm":"sha256","ts":"2026-10-31T22:31:37.663Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-hke9z7","release:pm-gh1642"]},"topic":{"value":"pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-hke9z7","release:pm-gh1642"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:37.663Z"}],"before_hash":"50e1db555085a5bc7e76ea6f1038f330ebd57ee081ce3feab59f8912fce21ceb","after_hash":"b979ea17c98bcfc1cd9490f8b018fc4b0abeb1060a77e54936c2260063147d1c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"7423ce52a1be6976c564295433427ba17f6a5e653660c3aab8d616db459bc238"} diff --git a/.agents/pm/issues/pm-gh1642.toon b/.agents/pm/issues/pm-gh1642.toon new file mode 100644 index 0000000000..5f944ea48e --- /dev/null +++ b/.agents/pm/issues/pm-gh1642.toon @@ -0,0 +1,59 @@ +id: pm-gh1642 +title: "GH-1642: brief pm list and pm search pages print an envelope as large as their rows; the default sort and search's default omission receipt are no longer compacted" +description: "https://github.com/unbraind/pm-cli/issues/1642\n\nReproduced on the current source build (2026.10.31). pm list: projectPmBriefListEnvelope (src/sdk/output/list-envelope.ts, pm-6pycus) only drops the default sorting block when it has exactly the keys sort and order. Sorting later gained effective_keys, so the 7-line default sort is printed on every brief list page — the compaction silently stopped matching its own default. filters echoes limit, which applied_limit already states. pm search has no brief projection at all: it prints truncated (equal to has_more), filters {limit} (equal to applied_limit) and a 4-line omission_receipt whose only group is projection_metadata restorable with --full.\n\nDeliberate exclusions, with reasons: completeness unchecked stays visible (pm-6pycus AC: unchecked and partial source evidence cannot become complete or disappear); the items_encoding block stays because it is the lossless decode contract for the matched_fields list column, and the formatter round-trip tests require json_columns and absent to restore rows; JSON, --full and SDK result types keep the full receipts.\n\nDuplicate search: 'brief envelope list', GH-1501, GH-1642; nearest is pm-6pycus (GH-1501 brief list envelope, closed) which this extends." +type: Issue +status: closed +priority: 2 +tags[4]: agent-ux,github,output,token-efficiency +created_at: "2026-10-31T21:14:02.437Z" +updated_at: "2026-10-31T22:31:37.050Z" +closed_at: "2026-10-31T22:31:34.335Z" +completed_at: "2026-10-31T22:31:34.335Z" +author: "harness:claude-code" +estimated_minutes: 120 +acceptance_criteria: "A default brief pm list page omits the default sorting block including effective_keys and a filters.limit equal to applied_limit; a default pm search page omits truncated when equal to has_more, a filters block that only echoes applied_limit, and replaces the single default projection_metadata omission receipt with details --full; unchecked or partial completeness, non-default sorting, non-limit filters, custom omission receipts, JSON, --full and SDK results are unchanged; the search projection is a reusable SDK export that leaves its input unchanged; measured envelope bytes drop on a small page" +parent: pm-5t33or +risk: low +confidence: high +resolution: "projectPmBriefListEnvelope now recognizes the default ascending sort even with effective_keys. It replaces the legacy sort/order echo with one effective_keys comparator line, keeping the GH-1507 disclosure, and drops a filters.limit equal to applied_limit and empty runtime_filters. The new public SDK projectPmBriefSearchEnvelope drops truncated when it repeats has_more and limit-only filters, and replaces the default projection_metadata omission receipt with details --full. formatBuiltInOutput chains both for TOON only. completeness unchecked and items_encoding stay by contract." +expected_result: "Small default pm list and pm search pages print only envelope fields that carry information, while JSON, --full and SDK results keep complete receipts." +actual_result: "Same tracker, published 2026.10.31 vs this branch: pm search 'release workflow' --limit 3 drops from 1,033 to 863 bytes and pm list --status open --limit 3 from 988 to 782 bytes. compact-agent-output spec 59/59 and default-list-order integration pass; the GH-1642 cases fail with the projection stashed. SDK surface update is additive for the new export." +dependencies[3]{id,kind,created_at,author,source_kind,author_source}: + pm-6pycus,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected + pm-gh1627,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected + pm-zd46m7,related,"2026-10-31T21:49:42.569Z","harness:claude-code","cli:update:dep",detected +comments[2]{created_at,author,text}: + "2026-10-31T21:47:00.908Z","harness:claude-code","Implementation and measurement: projectPmBriefListEnvelope now drops the default ascending sort when it carries effective_keys (the regression), and drops a filters.limit equal to applied_limit and empty runtime_filters. New public projectPmBriefSearchEnvelope (sdk/output) drops truncated when it equals has_more and limit-only filters, and replaces the single default projection_metadata omission receipt with details --full. It is chained after the list projection in formatBuiltInOutput (TOON only). Measured with the published 2026.10.31 package against this branch on this tracker: pm search 'release workflow' --limit 3 drops from 1,033 to 863 B (-16%) and pm list --status open --limit 3 from 988 to 782 B (-21%). completeness unchecked and items_encoding remain by design (see description)." + "2026-10-31T21:49:41.855Z","harness:claude-code","Design correction while testing: the 7-line default sort block is not only redundant metadata. GH-1507's accepted contract (pm-zd46m7, integration spec default-list-order) requires brief TOON to disclose the effective default comparator order so an agent knows why rows arrive in that order. Dropping it would undo that decision. The projection therefore drops only the legacy sort/order echo and renders the effective keys on one line, for example sorting.effective_keys 'status_registry:nonterminal_before_terminal asc, priority asc, updated_at desc, id asc'. Seven lines become two, and JSON keeps the full pair array. A malformed or extended sorting block passes through unchanged." +files[5]{path,scope}: + sdk/public-surface.json,project + src/core/output/output.ts,project + src/sdk/output.ts,project + src/sdk/output/list-envelope.ts,project + tests/unit/cli/compact-agent-output.spec.ts,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts,project,"harness:claude-code","2026-10-31T21:46:35.370Z",local_mutation,fix/agent-ux-orient-claim-envelopes +test_runs[1]: + - run_id: test-local-mvwz55qw-51axji + kind: test + status: passed + started_at: "2026-10-31T22:29:59.138Z" + finished_at: "2026-10-31T22:30:07.448Z" + recorded_at: "2026-10-31T22:30:07.448Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts,schema,schema,source,local_source_ref +docs[1]{path,scope}: + docs/SDK.md,project +close_reason: "Fixed: brief pm list and pm search TOON pages drop redundant envelope fields" +escape_class: production_defect +gate_evidence: + disposition: gate_added + owner: pm-gh1642 + gate_id: tests/unit/cli/compact-agent-output.spec.ts GH-1642 brief search and list envelopes + negative_control: git stash push src/sdk/output/list-envelope.ts src/core/output/output.ts src/sdk/output.ts && node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts; git stash pop (the GH-1642 cases fail before the fix) + local_checks[2]: node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts tests/integration/read-output/default-list-order.integration.spec.ts,node scripts/sdk-surface-snapshot.mjs --check + hosted_checks[2]: CI / Gates (static),CI / Gates (coverage) +body: "" diff --git a/.agents/pm/issues/pm-gh1643.toon b/.agents/pm/issues/pm-gh1643.toon new file mode 100644 index 0000000000..8516a9debb --- /dev/null +++ b/.agents/pm/issues/pm-gh1643.toon @@ -0,0 +1,72 @@ +id: pm-gh1643 +title: "GH-1643: pm claim --next --start is refused with 'Specify an item id or pass --next' although --next was passed; select-claim-start needs one atomic write" +description: "https://github.com/unbraind/pm-cli/issues/1643\n\nReproduced on the current source build (2026.10.31) in a fresh non-git sandbox: `pm claim --next --start` exits 2 with the generic command_failed refusal 'Specify an item id or pass --next' and refusal.surface --next. Root cause: runClaimAction in src/cli/register-operations.ts routes any --start to requireClaimTarget(id, false), which ignores --next and emits the hint to pass the flag already passed. The MCP transport has the same defect: runMcpClaimAction (src/sdk/lifecycle/mcp-actions.ts) routes start to runMcpTaskCompositionAction, which requires an explicit id and refuses with 'Missing required argument: id'.\n\nSelecting, claiming and starting the next item is one agent intent. Today it takes claim --next followed by update --status in_progress: a crash between the two leaves a claimed-but-open item. claim --next already selects and claims under the item lock with an advancing race-loss walk (pm-114v, pm-fjxm). release --handoff already changes status inside the release mutation with enforceTypeWorkflowTransition (src/sdk/lifecycle/release-handoff.ts), so the same pattern gives an atomic claim-and-start.\n\nDuplicate search: 'claim next start', 'refusal suggests flag already provided', GH-1352, GH-1643; nearest are pm-114v and pm-fjxm (claim --next, closed), pm-gh1369 (claim --start compact receipts, closed), pm-ixi1 (provided flag listed as missing, closed) and pm-f05lsg (refusal closure gate, open)." +type: Issue +status: closed +priority: 1 +tags[4]: agent-ux,claim,github,refusal +created_at: "2026-10-31T21:13:57.945Z" +updated_at: "2026-10-31T22:31:35.818Z" +closed_at: "2026-10-31T22:31:19.294Z" +completed_at: "2026-10-31T22:31:19.294Z" +author: "harness:claude-code" +estimated_minutes: 180 +acceptance_criteria: "pm claim --next --start selects, claims and moves the selected item to the workspace's in-progress status in one locked mutation with one history event, honours the selection filters, --if-available and the race-loss walk, enforces configured type workflows, and reports the start transition in the receipt; MCP pm_claim with next and start behaves identically; a claim target refusal never tells the caller to pass a flag already present and carries a typed code; regression tests cover CLI, MCP and the refusal hint" +parent: pm-mpbb +risk: medium +confidence: high +resolution: "runClaim accepts start and applyClaimStart moves the claimed item to the configured in-progress status inside the same mutateItem callback. That gives one claim history event, type-workflow enforcement, terminal_state_conflict for terminal items, and a start {from_status,to_status} receipt. runClaimNext forwards start through the race-loss walk. The CLI runClaimAction and MCP runMcpClaimAction route an id-less start to that path, while an explicit-id start keeps the claim-then-update composition and its typed refusal. requireClaimTarget now lives in the SDK claim module with missing_required_argument and ambiguous_option_combination codes and never names a flag the caller already passed. PmClient.claimNext accepts start. The MCP schema drops the start+next exclusion, and start+ifAvailable is exclusive only with an id." +expected_result: "pm claim --next --start claims and starts the next available item in one write on every transport, and no claim-target refusal recommends a flag the caller supplied." +actual_result: "Sandbox: pm claim --next --start returns status in_progress with changed_field_count 3 and history create then claim. MCP claim {next,start} and PmClient.claimNext({start:true}) return in_progress. pm claim --start refuses with missing_required_argument, suggesting pm claim --next --start. claim-start spec 7/7, register-commands 91/91, and lifecycle integration specs pass. With the routing stashed, the CLI/MCP start test fails. Full suite 11,748 passed." +dependencies[5]{id,kind,created_at,author,source_kind,author_source}: + pm-114v,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected + pm-f05lsg,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected + pm-fjxm,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected + pm-gh1369,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected + pm-ixi1,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected +comments[1]{created_at,author,text}: + "2026-10-31T21:46:58.955Z","harness:claude-code","Implementation: runClaim gained an optional start that calls applyClaimStart inside the same mutateItem callback (the release --handoff pattern). It resolves the in-progress status through resolveStartTaskInProgressStatus, refuses a terminal item with terminal_state_conflict even under --force, enforces type workflows via enforceTypeWorkflowTransition, and returns start {from_status,to_status}. runClaimNext forwards start to every candidate in the race-loss walk, so --if-available and filters behave as before. CLI runClaimAction and MCP runMcpClaimAction route start with an explicit id to the unchanged two-step composition, which keeps its typed 'Remove --next and --if-available' refusal; start without an id uses the atomic path or the new requireClaimTarget in src/sdk/lifecycle/claim-target.ts (missing_required_argument / ambiguous_option_combination, attributed to claim through the catalog generator's module map). runStartTask now strips start before its claim step so explicit --start stays claim-then-update. MCP schema: the start+next exclusion is gone and start+ifAvailable is exclusive only with an id. PmClient.claimNext({start:true}) is typed. Verified in a sandbox: claim --next --start → status in_progress, changed_field_count 3, history create→claim (one event)." +files[16]{path,scope}: + scripts/release/surface-replication-sets.json,project + sdk/public-surface.json,project + src/cli/register-operations.ts,project + src/sdk/cli-contracts/tool-schema.ts,project + src/sdk/core.ts,project + src/sdk/generated/generated-error-code-catalog-part-1.ts,project + src/sdk/generated/generated-error-code-catalog-part-2.ts,project + src/sdk/index.ts,project + src/sdk/lifecycle/claim.ts,project + src/sdk/lifecycle/mcp-actions.ts,project + src/sdk/lifecycle/task-composition.ts,project + src/sdk/runtime-public-contracts.ts,project + src/sdk/runtime.ts,project + tests/fixtures/contracts/full.json,project + tests/unit/commands/register-commands.spec.ts,project + tests/unit/sdk/lifecycle/claim-start.spec.ts,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,project,"harness:claude-code","2026-10-31T21:46:33.577Z",local_mutation,fix/agent-ux-orient-claim-envelopes +test_runs[1]: + - run_id: test-local-mvwz4r6h-yrwyti + kind: test + status: passed + started_at: "2026-10-31T22:29:33.718Z" + finished_at: "2026-10-31T22:29:48.568Z" + recorded_at: "2026-10-31T22:29:48.568Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,schema,schema,source,local_source_ref +docs[2]{path,scope}: + docs/COMMANDS.md,project + docs/LIFECYCLE_COMMANDS.md,project +close_reason: "Fixed: claim --next --start is one locked select-claim-start write on CLI, MCP and SDK" +escape_class: production_defect +gate_evidence: + disposition: gate_added + owner: pm-gh1643 + gate_id: tests/unit/sdk/lifecycle/claim-start.spec.ts GH-1643 claim --next --start + negative_control: git stash push src/cli/register-operations.ts src/sdk/lifecycle/mcp-actions.ts src/sdk/lifecycle/task-composition.ts && node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts; git stash pop (the CLI/MCP start test fails before the fix) + local_checks[2]: node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,node --import tsx scripts/release/static-quality-gate.mts + hosted_checks[2]: CI / Gates (static),CI / Gates (coverage) +body: "" diff --git a/.agents/pm/issues/pm-gh1644.toon b/.agents/pm/issues/pm-gh1644.toon new file mode 100644 index 0000000000..d1489fd68f --- /dev/null +++ b/.agents/pm/issues/pm-gh1644.toon @@ -0,0 +1,68 @@ +id: pm-gh1644 +title: "GH-1644: context --for orient silently reduces an explicit --limit to the intent's budget-derived row ceiling, and pm's own quickstart recommends that command" +description: "https://github.com/unbraind/pm-cli/issues/1644\n\nReproduced on the current source build (2026.10.31) in a fresh 6-task sandbox and on the real tracker: `pm context --limit 10 --for orient` returns filters.limit \"3\" in either flag order, while `pm context --limit 10` returns 10. The intent applier (CONTEXT_INTENT_DEFAULT_APPLIERS in src/sdk/context-intent-contracts.ts) computes a budget-derived ceiling ((3000 - 1200) / 600 = 3 rows) and replaces the caller's --limit with min(requested, derived). The context_intent receipt reports binding_constraint token_budget but neither the requested value nor a warning; filters.limit shows only the effective 3. list --for triage and search --for discover use the same clamp.\n\nMeasured on the real tracker (3173 items): 3 orient rows serialize to ~1,680 JSON tokens, 5 rows ~2,724, 10 rows ~5,937. The 3000-token preset therefore genuinely cannot carry 10 rows, so the preset ceiling is correct; the defects are (1) the silent override, (2) filters reporting only the effective value, and (3) pm's own agent quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE) recommending `pm context --limit 10 --for orient`, a self-contradicting command. The measured canonical in token-budget-gate is `context --for orient` without --limit.\n\nDuplicate search: 'context orient preset limit', 'for orient intent preset', GH-1644; nearest are pm-gy885b (intent discoverability, closed), pm-3crymx (intent returned no context, closed) and pm-ka6d (canonical cold-start docs, closed)." +type: Issue +status: closed +priority: 1 +tags[4]: agent-ux,context,github,token-efficiency +created_at: "2026-10-31T21:13:36.728Z" +updated_at: "2026-10-31T22:31:36.414Z" +closed_at: "2026-10-31T22:31:33.693Z" +completed_at: "2026-10-31T22:31:33.693Z" +author: "harness:claude-code" +estimated_minutes: 150 +acceptance_criteria: "An explicit row limit above the intent's budget-derived ceiling yields a typed limit_overridden_by_intent warning naming --limit, the requested and effective values, and a --token-budget that would carry the request; filters reports requested_limit beside the effective limit whenever they differ; the context_intent receipt records requested_limit; the same behaviour holds for context, list --for triage and search --for discover; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers every row-bounded intent with an explicit over-ceiling limit" +parent: pm-5t33or +risk: medium +confidence: high +resolution: "The three row-bounded intent appliers share applyIntentRowLimit, which keeps the budget-derived ceiling and records a reduced request. attachContextIntentReceipt writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning limit_overridden_by_intent:--limit N->M for context, list and search intents, reading either projected or raw host options. The quickstart generator, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and both replayed agent-task transcripts use pm context --for orient. The quickstart no longer calls explicit --start atomic." +expected_result: Every row-bounded --for intent either honours an explicit --limit or reports a typed override with the requested and effective values and a retry that keeps the limit; pm's quickstart recommends no silently overridden flag. +actual_result: "Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning, and restore_with pm context --for orient --limit 10 --token-budget 7200. That retry returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100. The agent-task token baseline changed only its transcript digest (identical output). intent-limit-override spec 3/3 passes; 3/3 fail with the fix stashed." +dependencies[3]{id,kind,created_at,author,source_kind,author_source}: + pm-3crymx,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected + pm-gy885b,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected + pm-ka6d,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected +comments[1]{created_at,author,text}: + "2026-10-31T21:46:59.575Z","harness:claude-code","Implementation: the three row-bounded intent appliers share applyIntentRowLimit, which keeps the existing min(requested, budget-derived) clamp but stashes a reduced request as intentRequestedLimit. attachContextIntentReceipt calls discloseIntentLimitOverride, which reads that key or the raw host --limit and writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning 'limit_overridden_by_intent:--limit N->M'. token_budget_for_requested = max(current budget, overhead + rowCost*requested) from the shared resolveIntentRowCost table. Measured on this tracker: pm context --for orient --limit 10 now prints the override with restore_with 'pm context --for orient --limit 10 --token-budget 7200'. That retry returns 10 rows at an estimated 4,637 tokens within budget. list --for triage --limit 150 → effective 100, retry with --token-budget 3200 honours 150. The quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, pm-user ECOSYSTEM_PLANNING) and the replayed agent-task transcript now use the measured canonical pm context --for orient." +files[10]{path,scope}: + docs/agent-task-token-baseline.json,project + docs/agent-task-transcripts.json,project + sdk/public-surface.json,project + src/sdk/context-intent-contracts.ts,project + src/sdk/init-agent-guidance.ts,project + tests/integration/cli.integration.spec.ts,project + tests/unit/commands/workspace/init-command.spec.ts,project + tests/unit/scripts/release/agent-task-token-gate.spec.ts,project + tests/unit/sdk/context/intent-limit-override.spec.ts,project + tests/unit/sdk/recovery/recovery-guidance.spec.ts,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts,project,"harness:claude-code","2026-10-31T21:46:34.470Z",local_mutation,fix/agent-ux-orient-claim-envelopes +test_runs[1]: + - run_id: test-local-mvwz4yu4-gbikjl + kind: test + status: passed + started_at: "2026-10-31T22:29:49.127Z" + finished_at: "2026-10-31T22:29:58.492Z" + recorded_at: "2026-10-31T22:29:58.492Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts,schema,schema,source,local_source_ref +docs[6]{path,scope}: + .agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md,project + AGENTS.md,project + docs/AGENT_GUIDE.md,project + docs/COMMAND_NAMESPACES.md,project + docs/SDK_ACTION_CONFORMANCE.md,project + README.md,project +close_reason: "Fixed: an intent budget that reduces an explicit --limit now discloses the override, and the quickstart recommends the measured canonical orientation" +escape_class: production_defect +gate_evidence: + disposition: gate_added + owner: pm-gh1644 + gate_id: tests/unit/sdk/context/intent-limit-override.spec.ts GH-1644 intent limit override + negative_control: git stash push src/sdk/context-intent-contracts.ts && node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts; git stash pop (3 of 3 fail before the fix) + local_checks[2]: node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts,node scripts/release/agent-task-token-gate.mjs + hosted_checks[2]: CI / Gates (static),CI / Gates (coverage) +body: "" diff --git a/.agents/pm/issues/pm-hke9z7.toon b/.agents/pm/issues/pm-hke9z7.toon new file mode 100644 index 0000000000..c10ce7742a --- /dev/null +++ b/.agents/pm/issues/pm-hke9z7.toon @@ -0,0 +1,56 @@ +id: pm-hke9z7 +title: "MCP read tools silently ignore declared flat parameters: pm_context reads none, and for/tokenBudget/depth on pm_list, pm_search, pm_next and pm_get never reach the intent layer" +description: "Discovered while verifying pm-gh1644 over MCP (published 2026.10.31 and current source both reproduce).\n\nEvery narrow MCP read tool declares its action parameters flat in its inputSchema (pm_context declares for, limit, depth, section, fields, tokenBudget, ...; pm_list/pm_search/pm_next/pm_get declare for and tokenBudget). The SDK runtime only moves a flat argument into the options record the handlers read when it appears in the hand-maintained HOISTED_ACTION_OPTION_KEYS table in src/sdk/runtime-input.ts. That table has no context entry at all and omits for/tokenBudget (and most filters) from list, search, next and get. A flat argument outside the table is accepted without a warning (it is declared) and then dropped.\n\nMeasured in a fresh sandbox through the MCP tools/call handler, flat vs nested options: pm_context {depth: deep} → depth brief (nested → deep); pm_context {for: orient, limit: 10} → no intent, limit null, token_budget 1600 (nested → orient, limit 3); pm_list {for: triage}, pm_search {query, for: discover}, pm_next {for: execute}, pm_get {id, for: inspect} → no context_intent receipt (nested → applied); pm_next {limit: 1} → 5 rows (nested → 1). The declared intent layer is therefore unreachable for MCP hosts that send the flat arguments the schema advertises. Same defect family as pm-wcaa (pm_files declared-but-ignored params, closed individually) and pm-hno5.\n\nDuplicate search: 'MCP pm_context flat arguments ignored options', 'MCP declared parameter silently ignored', 'mcp for intent not applied' — no open item." +type: Issue +status: closed +priority: 1 +tags[4]: agent-ux,context,contracts,mcp +created_at: "2026-10-31T21:27:47.547Z" +updated_at: "2026-10-31T22:31:37.663Z" +closed_at: "2026-10-31T22:31:35.081Z" +completed_at: "2026-10-31T22:31:35.081Z" +author: "harness:claude-code" +estimated_minutes: 120 +acceptance_criteria: "For context, get, list, next and search, every optional parameter the action's tool contract declares is hoisted from flat MCP arguments into the handler options (derived from PM_TOOL_ACTION_PARAMETER_CONTRACTS, not a hand list), nested options still win; flat and nested calls produce identical results for for, limit, depth and tokenBudget on all five tools; a regression test iterates every declared optional key of the five actions and fails if one is not hoisted" +parent: pm-5k4v +risk: high +confidence: high +resolution: "runtime-input.ts derives flat-to-options hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional, excluding the query/keywords operands. One hoisted-key list replaces three loops, and nested options still win. runMcpSearchAction reads query, then the declared keywords alias, then refuses with the unchanged missing-query error." +expected_result: "An MCP host that sends the flat parameters a read tool's schema declares gets the same intent, projection and limits as the nested options form." +actual_result: "Through the MCP tools/call handler, flat and nested now agree for pm_context depth/for/limit, for on pm_list/pm_search/pm_next/pm_get, pm_get depth and pm_next limit. The flat orient call also discloses the limit override. read-tool-flat-parameters spec 7/7 iterates every declared optional key; 6/7 fail with the fix stashed. mcp-handshake hoist expectations pass." +dependencies[3]{id,kind,created_at,author,source_kind,author_source}: + pm-gh1644,discovered_from,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected + pm-hno5,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected + pm-wcaa,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected +comments[1]{created_at,author,text}: + "2026-10-31T21:47:01.751Z","harness:claude-code","Implementation: runtime-input.ts derives hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional (declaredReadActionOptionKeys), after the hand table, so nested options still win. Verified through the MCP tools/call handler: flat and nested calls now agree for pm_context depth/for/limit, pm_list/pm_search/pm_next/pm_get for, pm_get depth and pm_next limit. The regression spec iterates every declared optional key of the five actions, so a new declared parameter can no longer be silently dropped." +files[3]{path,scope}: + src/sdk/runtime-input.ts,project + src/sdk/runtime/actions.ts,project + tests/unit/mcp/read-tool-flat-parameters.spec.ts,project +tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts,project,"harness:claude-code","2026-10-31T21:46:36.177Z",local_mutation,fix/agent-ux-orient-claim-envelopes +test_runs[1]: + - run_id: test-local-mvwz5gjx-0ayygj + kind: test + status: passed + started_at: "2026-10-31T22:30:08.071Z" + finished_at: "2026-10-31T22:30:21.453Z" + recorded_at: "2026-10-31T22:30:21.453Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts,schema,schema,source,local_source_ref +docs[1]{path,scope}: + docs/SDK_ACTION_CONFORMANCE.md,project +close_reason: "Fixed: MCP read tools read every declared flat parameter and pm_search honours its keywords alias" +escape_class: production_defect +gate_evidence: + disposition: gate_added + owner: pm-hke9z7 + gate_id: tests/unit/mcp/read-tool-flat-parameters.spec.ts declared flat parameter census + negative_control: git stash push src/sdk/runtime-input.ts src/sdk/runtime/actions.ts && node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts; git stash pop (6 of 7 fail before the fix) + local_checks[1]: node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts tests/integration/mcp-handshake.spec.ts + hosted_checks[2]: CI / Gates (static),CI / Gates (coverage) +body: "" diff --git a/.agents/pm/reminders/pm-7zs0.toon b/.agents/pm/reminders/pm-7zs0.toon index 67012e34d4..e9f63f2b7d 100644 --- a/.agents/pm/reminders/pm-7zs0.toon +++ b/.agents/pm/reminders/pm-7zs0.toon @@ -6,7 +6,7 @@ status: open priority: 2 tags[3]: "area:release",living-map,reliability created_at: "2026-06-07T10:24:45.889Z" -updated_at: "2026-10-30T17:18:07.453Z" +updated_at: "2026-10-31T22:12:47.452Z" reminders[1]{at,text}: "2026-10-31T06:00:00.000Z",Verify the next daily release and npm/npx/Bun/bunx consumers; retain pm-gh1622 until installed-producer remediation and publication are verified author: maintainer-agent @@ -90,7 +90,7 @@ dependencies[14]: author: "harness:codex" source_kind: "cli:update:dep" author_source: detected -comments[258]: +comments[259]: - created_at: "2026-06-10T14:44:30.277Z" author: maintainer-agent text: "2026-06-10 daily auto-release (scheduled run 27258655654, 06:50Z) FAILED at sentry-telemetry-gate: PM-CLI-1J 'CommandError: {' — a CommandError whose message was the raw guided-error JSON envelope (code tracker_not_initialized) thrown by an ephemeral temp-dir script (readPmItemsForUpsert, source no longer on disk; 2 events 2026-06-09T21:34Z). Gate classifier fix already landed in PR #197 (fba75fb0, merged 11:34Z, adds 'tracker_not_initialized' to KNOWN_EXPECTED_HANDLED_CLI_ISSUE_PATTERNS) but AFTER the scheduled run. Recovery: resolved PM-CLI-1J in Sentry + re-dispatched auto-release.yml with push=true (run 27284359777). Will verify v2026.6.10 npm publish before closing." @@ -868,6 +868,9 @@ comments[258]: - created_at: "2026-10-30T13:14:20.173Z" author: "harness:claude-code" text: "October 30 follow-up at 13:15 UTC: scheduled Auto Release run 41029596560 (08:54 UTC) ended cancelled after 15m13s, mid-pipeline. The workflow uses cancel-in-progress: false with a 180-minute job timeout, so this was an external cancellation, not a concurrency or timeout cut. It made no publication. Automation opened blocker issue #1632 for the earlier dispatcher failure 40999107873; it is now linked to the owner pm-gh1622, with no duplicate item. Live state: npm @unbrained/pm-cli is 2026.10.28, latest GitHub release is v2026.10.28, and there is no 2026.10.29 or 2026.10.30 cut, so no duplicate automatic cut can exist. Sentry has exactly one unresolved issue in 14 days: PM-CLI-3Q, 2 events, last seen 2026-10-29 03:11:51 UTC. Under the release gate's 14-day window it blocks every cut until it is resolved with verified pm-presets remediation; upstream PR170 is still open at 97d5d1bf4. Telemetry stack check at 13:13 UTC: all containers healthy, API synced at 13:09, queue drained, 74,676 events in 24h, newest event under one minute old. The last hour split 3,432 automation and 352 test source contexts, so test-run tagging from GH-1625 is visible in production rows." + - created_at: "2026-10-31T22:12:47.451Z" + author: "harness:claude-code" + text: "October 31 checkpoint (verified live 22:10 UTC): the 03:45 UTC manual run 41165698315 and the 08:47 UTC scheduled run 41185994587 both stopped at static-quality-gate on 1a72d089d, before publication. The causes were two registry advisories published that morning (follow-redirects GHSA-mrwp-f3q8-3g9p, minimatch GHSA-g3hv-75wg-m249) and the pm-changelog 2026.10.31 grammar census drift. PR #1638 (3ff44fb6a) fixed both. Closing blocker #1639 at 19:16 UTC fired Auto Release (blocker_retry) 41233462967 → Release 41236628626 → v2026.10.31: npm latest 2026.10.31, GitHub Release published 20:51 UTC. Today has exactly one cut and no duplicate automatic release, and the nine other blocker_retry runs were skipped. The nightly quality alert #1641 had the same root cause and is closed." notes[11]: - created_at: "2026-07-02T06:41:55.766Z" author: claude-code-agent diff --git a/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md b/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md index 2ffdc92f7f..d359b9af20 100644 --- a/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +++ b/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md @@ -10,7 +10,7 @@ implementation starts with bounded context and expands only its active lineage. ## Establish the Population ```bash -pm context --limit 10 --for orient +pm context --for orient pm search "" --limit 10 # hybrid when semantic search is configured, keyword otherwise pm list --status open --limit 20 pm list --status in_progress --limit 20 diff --git a/AGENTS.md b/AGENTS.md index 52a6042d82..0ef95dbc1d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,8 +60,8 @@ For real repository tracking, do not override `PM_PATH`. If a script must target ## pm Workflow (Agent Quickstart) -- Cold start with the measured canonical orientation: `pm context --limit 10 --for orient`; before item mutation, also run `pm search "" --limit 10`, `pm list --status open --limit 20`, and `pm list --status in_progress --limit 20`. -- Claim and start atomically: `pm claim --start`. +- Cold start with the measured canonical orientation: `pm context --for orient`; before item mutation, also run `pm search "" --limit 10`, `pm list --status open --limit 20`, and `pm list --status in_progress --limit 20`. +- Claim and start: `pm claim --start`, or `pm claim --next --start` to take the next available item in one write. - Link evidence while coding: `pm files --add ...`, `pm docs --add ...`, `pm test --add command="node scripts/run-tests.mjs test"`. - Verify and close: `pm test --run --progress`, then atomically record closure evidence with `pm close "" --resolution "" --expected "" --actual "" --validate-close warn`, then `pm release `. - Author identity is automatic for supported agent harnesses; use `--author` only for an explicit override. @@ -70,7 +70,7 @@ For real repository tracking, do not override `PM_PATH`. If a script must target ## Working Rules -- **Orient:** start with the measured canonical cold-start, `pm context --limit 10 --for orient`. Before `pm create`, also run request-specific `pm search`, `pm list --status open`, and `pm list --status in_progress` so duplicate and ownership checks use live state. If net-new work is required, create or reuse the parent lineage first and record duplicate-check evidence in a create-time comment. +- **Orient:** start with the measured canonical cold-start, `pm context --for orient`. Before `pm create`, also run request-specific `pm search`, `pm list --status open`, and `pm list --status in_progress` so duplicate and ownership checks use live state. If net-new work is required, create or reuse the parent lineage first and record duplicate-check evidence in a create-time comment. - **Verify before you claim:** the verification recipe for the Non-Negotiable above. A `closed` item whose `resolution`/`close_reason` describes the fix represents completed, shipped work — never describe it as outstanding. Confirm release/CI/infra status against the live system, e.g. `pm get pm-9gxi`, `npm view @unbrained/pm-cli version`, `gh release list`, `gh run list --workflow=auto-release.yml`. - **Implement:** keep edits scoped to the claimed item. Link changed files with `pm files`, docs with `pm docs`, and runnable verification with `pm test`. - **Record:** use `pm comments`, `pm notes`, and `pm learnings` for progress, rationale, and durable lessons. Prefer append-style updates over rewriting item content. diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c7eb98858..32e5aee62e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Unreleased + +### Fixed + +- MCP read tools silently ignore declared flat parameters: pm_context reads none, and for/tokenBudget/depth on pm_list, pm_search, pm_next and pm_get never reach the intent layer ([pm-hke9z7](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-hke9z7.toon)) +- GH-1642: brief pm list and pm search pages print an envelope as large as their rows; the default sort and search's default omission receipt are no longer compacted ([pm-gh1642](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1642.toon)) +- GH-1644: context --for orient silently reduces an explicit --limit to the intent's budget-derived row ceiling, and pm's own quickstart recommends that command ([pm-gh1644](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1644.toon)) +- GH-1643: pm claim --next --start is refused with 'Specify an item id or pass --next' although --next was passed; select-claim-start needs one atomic write ([pm-gh1643](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1643.toon)) + ## 2026.10.31 - 2026-10-31 ### Changed diff --git a/README.md b/README.md index c119a160bb..4bb3d8f5c6 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ pm release Use the measured one-call context intent for cold-start orientation. Use `pm next` as an alternative when you only need the single highest-priority ready item, then search all statuses before creating anything. Measurement methodology and ratcheted evidence live in the [Agent Guide](docs/AGENT_GUIDE.md#agent-quick-context). ```bash -pm context --limit 10 --for orient # canonical cold start: state, ownership, actionable work +pm context --for orient # canonical cold start: state, ownership, actionable work # Alternative: pm next # one actionable item + rationale, ready/blocked queues pm search "keywords for the requested work" --limit 10 pm list --status open --limit 20 diff --git a/docs/AGENT_GUIDE.md b/docs/AGENT_GUIDE.md index 46d680e7ec..c67ca65145 100644 --- a/docs/AGENT_GUIDE.md +++ b/docs/AGENT_GUIDE.md @@ -7,7 +7,7 @@ This guide is optimized for coding agents that need to understand and mutate rep Run this before heavy work. The first call is the canonical cold start. Then run the request-specific duplicate and ownership checks before item mutation: ```bash -pm context --limit 10 --for orient +pm context --for orient pm search "" --limit 10 pm list --status open --limit 20 pm list --status in_progress --limit 20 @@ -33,7 +33,7 @@ Tracked documentation work: [pm-u9d0](../.agents/pm/epics/pm-u9d0.toon). 1. **Orient** ```bash -pm context --limit 10 --for orient +pm context --for orient pm search "" --limit 10 pm list --status open --limit 20 pm list --status in_progress --limit 20 @@ -128,7 +128,7 @@ tests compare complete history bytes while the unrelated tracker's claims change | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | The single next action + why | `pm next` (concrete ready leaves first; completed-container closeout rows surface only when no leaf work is ready; `--ready-only` for the tightest output) | | Next ready work in one epic | `pm next --parent ` | -| Canonical cold-start orientation | `pm context --limit 10 --for orient` (1,055 estimated tokens on the fixed 101-item replay corpus) | +| Canonical cold-start orientation | `pm context --for orient` (1,055 estimated tokens on the fixed 101-item replay corpus) | | Next work and agenda | `pm context --limit 10` | | Comprehensive whole-tracker snapshot | `pm context --depth full` (every section, no per-section row cap) | | Status of one epic/subtree | `pm context --parent --depth deep` | diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index b9441f2b8f..e6b7fdf921 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -919,6 +919,7 @@ Lifecycle composition flags combine claim, status, and close operations: ```bash pm claim --start # claim + move to the configured in-progress status +pm claim --next --start # select, claim and start the next available item in one locked write pm release --pause # move to the configured open status + release claim pm close "" --release-assignment # close with evidence + release assignment ``` diff --git a/docs/COMMAND_NAMESPACES.md b/docs/COMMAND_NAMESPACES.md index 0dc42e5694..0a32fa8a45 100644 --- a/docs/COMMAND_NAMESPACES.md +++ b/docs/COMMAND_NAMESPACES.md @@ -23,7 +23,7 @@ results, exit codes, and SDK behavior. | `pm ops normalize` | `pm normalize` | Normalize through an active governance package | | `pm history events` | `pm events` | Read or follow committed mutation events | -`pm context --limit 10 --for orient` still produces a context snapshot. +`pm context --for orient` still produces a context snapshot. `pm history --verify` still reads and verifies one item's history. Namespace leaves do not change either default operation. diff --git a/docs/LIFECYCLE_COMMANDS.md b/docs/LIFECYCLE_COMMANDS.md index 93ab169858..b074a559c1 100644 --- a/docs/LIFECYCLE_COMMANDS.md +++ b/docs/LIFECYCLE_COMMANDS.md @@ -33,9 +33,18 @@ stdin selection, dry runs, rollback behavior, and evidence flags. Put bulk flags after `many`. Deletion recovery remains `pm history restore `. Plain `claim`, `release`, `update`, and `close` keep their original semantics. -`claim --start` requires an explicit item and rejects `--next` and -`--if-available` before mutation. It claims ownership, then moves the item to the -configured in-progress status. `release --pause` moves work to the configured +`claim --start` rejects `--if-available` before mutation. It claims +ownership, then moves the item to the configured in-progress status. +`claim --next --start` selects the next caller-available item with the same +filters, `--if-available` behavior, and race-loss walk as `claim --next`. It then +claims the item and moves it to the configured in-progress status in one +locked mutation and one `claim` history event, so no other agent can observe +the claimed item before it starts. Configured type workflows are enforced, a +terminal candidate is refused (`terminal_state_conflict`), and the receipt +carries `start: { from_status, to_status }`. A claim without an id or `--next` +is refused with `missing_required_argument`, and an id combined with `--next` +with `ambiguous_option_combination`; neither refusal names a flag the caller +already passed. `release --pause` moves work to the configured open status, then releases ownership. `close --release-assignment` records the close and its inline evidence, then releases ownership. @@ -67,6 +76,7 @@ import { PmClient } from "@unbrained/pm-cli/sdk"; const pm = new PmClient({ pmRoot: "/project/.agents/pm" }); const started = await pm.claim("pm-example", { start: true }); +const nextStarted = await pm.claimNext({ start: true }); // one locked select-claim-start write const paused = await pm.release("pm-example", { pause: true }); const closed = await pm.close("pm-example", "Acceptance verified", { releaseAssignment: true, @@ -84,7 +94,8 @@ Literal `true` options return typed `StartTaskResult`, `PauseTaskResult`, or `runCloseTask` primitives expose the same SDK-owned compositions. MCP actions remain `claim`, `release`, and `close`, using `start`, `pause`, and -`releaseAssignment`. Action-specific discovery types claim's `start` as boolean; +`releaseAssignment`. MCP `claim` with `next: true` and `start: true` uses the +same single locked write as `claim --next --start`. Action-specific discovery types claim's `start` as boolean; scheduling actions retain string timestamps. For the broad provider tool schema, pass the claim boolean through `options: { start: true }` because its shared flat `start` property retains the scheduling string contract. Bulk action names remain diff --git a/docs/SDK.md b/docs/SDK.md index da5a3e4233..f00d8bc37c 100644 --- a/docs/SDK.md +++ b/docs/SDK.md @@ -2140,7 +2140,13 @@ Custom agent hosts can import `projectPmBriefListEnvelope` from `@unbrained/pm-cli/sdk` to apply the same brief-list presentation without mutating the original result. It retains unchecked source status and actionable continuation, and leaves partial, custom, or inconsistent envelopes intact. -The CLI applies this projection only to TOON; ordinary JSON and SDK query +`projectPmBriefSearchEnvelope` does the same for search pages. It drops +`truncated` when it repeats `has_more` and a `filters.limit` echo of +`applied_limit`, and it replaces the single default `projection_metadata` +omission receipt with `details: "--full"`. The list projection replaces the +default ascending sort's legacy `sort`/`order` echo with one +`effective_keys` comparator line, so the effective default order stays visible. +The CLI applies these projections only to TOON; ordinary JSON and SDK query results retain their complete contracts. The public `encodePmTableRows` and `decodePmTableRows` primitives distinguish explicit null and empty cells from missing keys. TOON rendering preserves those row facts before selecting the diff --git a/docs/SDK_ACTION_CONFORMANCE.md b/docs/SDK_ACTION_CONFORMANCE.md index a2d96ab8ac..a17f6733a5 100644 --- a/docs/SDK_ACTION_CONFORMANCE.md +++ b/docs/SDK_ACTION_CONFORMANCE.md @@ -90,6 +90,22 @@ ceiling. For row-oriented `list` and `search` intents, pm reports: - `binding_constraint` as `token_budget` or `explicit_limit`; - `limit_reason` with the selection rationale. +When the intent budget reduces an explicit `--limit`, as with +`pm context --for orient --limit 10` (the 3000-token orient budget carries 3 +rows), the reduction is never silent. `context_intent.limit_override` records +`requested`, `effective`, the `token_budget_for_requested` that would carry the +request, and a `restore_with` retry. `filters.requested_limit` sits beside the +effective `filters.limit`, and `warnings` carries +`limit_overridden_by_intent:--limit ->`. The same applies +to `context`, `list`, and `search` intents. + +MCP read tools (`pm_context`, `pm_get`, `pm_list`, `pm_next`, `pm_search`) +accept every optional parameter their action contract declares either flat or +under `options`. The runtime derives the flat-to-options mapping from +`PM_TOOL_ACTION_PARAMETER_CONTRACTS`, so a declared argument such as `for`, +`depth`, `limit`, or `tokenBudget` is never silently ignored; nested `options` +win when both are present. + Default intent budgets retain the ordinary page cap. Explicit budgets may derive larger pages; recursive compaction and resumable cursor safeguards still enforce the final serialized token ceiling. diff --git a/docs/agent-task-token-baseline.json b/docs/agent-task-token-baseline.json index b8e149c2aa..22fb3bc403 100644 --- a/docs/agent-task-token-baseline.json +++ b/docs/agent-task-token-baseline.json @@ -1,7 +1,7 @@ { "version": 5, "transcript_version": 2, - "transcript_digest": "sha256:83d4523ca528ad5e8801cf5c7c02dcebbf2b87dc72422d4b8e648755dcdc36c2", + "transcript_digest": "sha256:b4b151f3d36860e73cd535d0520e9f03b4ecec4444ee81a8b868896b5b0bba47", "estimator": "ceil(utf8_bytes / 4)", "comparison_scope": "plan_recovery_tracker_root_canonicalized", "measurement_scope": "output_before_token_accounting", diff --git a/docs/agent-task-transcripts.json b/docs/agent-task-transcripts.json index 234a7040cf..88272fab4d 100644 --- a/docs/agent-task-transcripts.json +++ b/docs/agent-task-transcripts.json @@ -101,7 +101,7 @@ "steps": [ { "id": "context", - "args": ["context", "--for", "orient", "--limit", "10"], + "args": ["context", "--for", "orient"], "expected_exit_code": 0, "expected_output_kind": "diagnostic", "expected_accounting_mode": "self_reported", @@ -141,7 +141,7 @@ "steps": [ { "id": "context", - "args": ["context", "--for", "orient", "--limit", "10"], + "args": ["context", "--for", "orient"], "expected_exit_code": 0, "expected_output_kind": "diagnostic", "expected_accounting_mode": "self_reported", @@ -187,7 +187,7 @@ }, { "id": "orient", - "args": ["context", "--for", "orient", "--limit", "5"], + "args": ["context", "--for", "orient"], "expected_exit_code": 0, "expected_output_kind": "diagnostic", "expected_accounting_mode": "self_reported", diff --git a/scripts/release/surface-replication-sets.json b/scripts/release/surface-replication-sets.json index abc9214e52..fde393ec6e 100644 --- a/scripts/release/surface-replication-sets.json +++ b/scripts/release/surface-replication-sets.json @@ -623,11 +623,11 @@ }, { "path": "src/cli/register-operations.ts", - "expected_count": 7, + "expected_count": 5, "rule_ownership": "all_occurrences", "owner": "pm-185870", "disposition": "transport_validation", - "reason": "Telemetry and workspace positional refusals moved to the shared SDK constructor; remaining interactive flags, foreground-only acknowledgement, and shell-facing setup errors stay in the CLI adapter." + "reason": "Telemetry and workspace positional refusals moved to the shared SDK constructor, and the claim target refusals moved to the shared SDK claim module (GH-1643) so CLI and MCP share typed codes; remaining interactive flags, foreground-only acknowledgement, and shell-facing setup errors stay in the CLI adapter." }, { "path": "src/cli/main.ts", diff --git a/sdk/public-surface.json b/sdk/public-surface.json index 14ea176477..c3b3ebe829 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -1,6 +1,6 @@ { "aggregate_completeness": { - "covered_symbols": 2035, + "covered_symbols": 2038, "excluded": [ { "name": "_testOnlyCliContracts", @@ -8,7 +8,7 @@ } ], "missing": [], - "required_symbols": 2036 + "required_symbols": 2039 }, "breaking_acknowledgements": [ { @@ -2971,6 +2971,20 @@ ], "package_version": "2026.10.30", "reason": "isGenericRefusalExamples was introduced unreleased on this stack (absent from main and v2026.10.30) and is superseded by withoutGenericRefusalExamples" + }, + { + "changes": [ + "signature ./sdk:ClaimNextOptions", + "signature ./sdk:ClaimResult", + "signature ./sdk:PmContextIntentReceipt", + "signature ./sdk/contracts:PmContextIntentReceipt", + "signature ./sdk/core:ClaimNextOptions", + "signature ./sdk/core:ClaimResult", + "signature ./sdk/runtime:ClaimNextOptions", + "signature ./sdk/runtime:ClaimResult" + ], + "package_version": "2026.10.31", + "reason": "Optional additions only: ClaimResult.start (claim --next --start receipt), ClaimNextOptions.start, and PmContextIntentReceipt.limit_override (GH-1643/GH-1644); no existing field changed type or became required." } ], "entrypoints": { @@ -5171,13 +5185,19 @@ "classification": "supported", "kind": "interface", "name": "ClaimNextOptions", - "signature": "export interface ClaimNextOptions extends PmClientFullMutationOptions { maxAttempts?: number | string; }" + "signature": "export interface ClaimNextOptions extends PmClientFullMutationOptions { maxAttempts?: number | string; start?: boolean; }" }, { "classification": "supported", "kind": "interface", "name": "ClaimResult", - "signature": "export interface ClaimResult { item: Record; changed_fields?: string[]; claimed_by: string; previous_assignee: string | null; forced: boolean; skipped?: boolean; warnings?: string[]; }" + "signature": "export interface ClaimResult { item: Record; changed_fields?: string[]; claimed_by: string; previous_assignee: string | null; forced: boolean; skipped?: boolean; warnings?: string[]; start?: ClaimStartReceipt; }" + }, + { + "classification": "supported", + "kind": "interface", + "name": "ClaimStartReceipt", + "signature": "export interface ClaimStartReceipt { from_status: string; to_status: string; }" }, { "classification": "supported", @@ -5881,6 +5901,12 @@ "name": "CONTEXT_FLAG_CONTRACTS", "signature": "CliFlagContract[]" }, + { + "classification": "contract_data", + "kind": "value", + "name": "CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING", + "signature": "\"limit_overridden_by_intent\"" + }, { "classification": "supported", "kind": "value", @@ -14437,6 +14463,12 @@ "name": "PmContextIntentDiscoveryOptions", "signature": "export interface PmContextIntentDiscoveryOptions { pmRoot: string; packages?: readonly PmContextIntentPackageModule[]; }" }, + { + "classification": "contract_data", + "kind": "interface", + "name": "PmContextIntentLimitOverride", + "signature": "export interface PmContextIntentLimitOverride { flag: \"--limit\"; requested: number; effective: number; token_budget_for_requested: number; restore_with: string; }" + }, { "classification": "advanced_export", "kind": "interface", @@ -14447,7 +14479,7 @@ "classification": "contract_data", "kind": "interface", "name": "PmContextIntentReceipt", - "signature": "export interface PmContextIntentReceipt { command: string; intent: string; source: PmContextIntentSource; included_field_groups: string[]; token_budget: number; declared_token_budget: number; token_budget_override: number; estimated_tokens: number; within_budget: boolean; degradation: \"bounded_fields_and_rows\" | \"bounded_ranked_rows\" | \"bounded_sections\" | \"budget_row_compaction\" | \"recursive_budget_compaction\" | \"budget_receipt_only\" | \"standard_item\" | \"none\"; declaration_feasible: boolean; result_omitted: boolean; budget_derived_limit?: number; binding_constraint?: \"explicit_limit\" | \"token_budget\"; limit_reason?: string; }" + "signature": "export interface PmContextIntentReceipt { command: string; intent: string; source: PmContextIntentSource; included_field_groups: string[]; token_budget: number; declared_token_budget: number; token_budget_override: number; estimated_tokens: number; within_budget: boolean; degradation: \"bounded_fields_and_rows\" | \"bounded_ranked_rows\" | \"bounded_sections\" | \"budget_row_compaction\" | \"recursive_budget_compaction\" | \"budget_receipt_only\" | \"standard_item\" | \"none\"; declaration_feasible: boolean; result_omitted: boolean; budget_derived_limit?: number; binding_constraint?: \"explicit_limit\" | \"token_budget\"; limit_reason?: string; limit_override?: PmContextIntentLimitOverride; }" }, { "classification": "contract_data", @@ -15901,6 +15933,12 @@ "name": "projectPmBriefListEnvelope", "signature": "(value: unknown) => unknown | calls: (value: unknown) => unknown" }, + { + "classification": "advanced_export", + "kind": "function", + "name": "projectPmBriefSearchEnvelope", + "signature": "(value: unknown) => unknown | calls: (value: unknown) => unknown" + }, { "classification": "contract_data", "kind": "function", @@ -24055,6 +24093,12 @@ "name": "CONTEXT_FLAG_CONTRACTS", "signature": "CliFlagContract[]" }, + { + "classification": "contract_data", + "kind": "value", + "name": "CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING", + "signature": "\"limit_overridden_by_intent\"" + }, { "classification": "contract_data", "kind": "function", @@ -25327,11 +25371,17 @@ "name": "PmContextIntentContract", "signature": "export interface PmContextIntentContract { command: PmContextIntentCommand; intent: string; description: string; included_field_groups: string[]; token_budget: number; source?: PmContextIntentSource; }" }, + { + "classification": "contract_data", + "kind": "interface", + "name": "PmContextIntentLimitOverride", + "signature": "export interface PmContextIntentLimitOverride { flag: \"--limit\"; requested: number; effective: number; token_budget_for_requested: number; restore_with: string; }" + }, { "classification": "contract_data", "kind": "interface", "name": "PmContextIntentReceipt", - "signature": "export interface PmContextIntentReceipt { command: string; intent: string; source: PmContextIntentSource; included_field_groups: string[]; token_budget: number; declared_token_budget: number; token_budget_override: number; estimated_tokens: number; within_budget: boolean; degradation: \"bounded_fields_and_rows\" | \"bounded_ranked_rows\" | \"bounded_sections\" | \"budget_row_compaction\" | \"recursive_budget_compaction\" | \"budget_receipt_only\" | \"standard_item\" | \"none\"; declaration_feasible: boolean; result_omitted: boolean; budget_derived_limit?: number; binding_constraint?: \"explicit_limit\" | \"token_budget\"; limit_reason?: string; }" + "signature": "export interface PmContextIntentReceipt { command: string; intent: string; source: PmContextIntentSource; included_field_groups: string[]; token_budget: number; declared_token_budget: number; token_budget_override: number; estimated_tokens: number; within_budget: boolean; degradation: \"bounded_fields_and_rows\" | \"bounded_ranked_rows\" | \"bounded_sections\" | \"budget_row_compaction\" | \"recursive_budget_compaction\" | \"budget_receipt_only\" | \"standard_item\" | \"none\"; declaration_feasible: boolean; result_omitted: boolean; budget_derived_limit?: number; binding_constraint?: \"explicit_limit\" | \"token_budget\"; limit_reason?: string; limit_override?: PmContextIntentLimitOverride; }" }, { "classification": "contract_data", @@ -26627,13 +26677,19 @@ "classification": "supported", "kind": "interface", "name": "ClaimNextOptions", - "signature": "export interface ClaimNextOptions extends PmClientFullMutationOptions { maxAttempts?: number | string; }" + "signature": "export interface ClaimNextOptions extends PmClientFullMutationOptions { maxAttempts?: number | string; start?: boolean; }" }, { "classification": "supported", "kind": "interface", "name": "ClaimResult", - "signature": "export interface ClaimResult { item: Record; changed_fields?: string[]; claimed_by: string; previous_assignee: string | null; forced: boolean; skipped?: boolean; warnings?: string[]; }" + "signature": "export interface ClaimResult { item: Record; changed_fields?: string[]; claimed_by: string; previous_assignee: string | null; forced: boolean; skipped?: boolean; warnings?: string[]; start?: ClaimStartReceipt; }" + }, + { + "classification": "supported", + "kind": "interface", + "name": "ClaimStartReceipt", + "signature": "export interface ClaimStartReceipt { from_status: string; to_status: string; }" }, { "classification": "supported", @@ -33719,13 +33775,19 @@ "classification": "supported", "kind": "interface", "name": "ClaimNextOptions", - "signature": "export interface ClaimNextOptions extends PmClientFullMutationOptions { maxAttempts?: number | string; }" + "signature": "export interface ClaimNextOptions extends PmClientFullMutationOptions { maxAttempts?: number | string; start?: boolean; }" }, { "classification": "supported", "kind": "interface", "name": "ClaimResult", - "signature": "export interface ClaimResult { item: Record; changed_fields?: string[]; claimed_by: string; previous_assignee: string | null; forced: boolean; skipped?: boolean; warnings?: string[]; }" + "signature": "export interface ClaimResult { item: Record; changed_fields?: string[]; claimed_by: string; previous_assignee: string | null; forced: boolean; skipped?: boolean; warnings?: string[]; start?: ClaimStartReceipt; }" + }, + { + "classification": "supported", + "kind": "interface", + "name": "ClaimStartReceipt", + "signature": "export interface ClaimStartReceipt { from_status: string; to_status: string; }" }, { "classification": "advanced_export", diff --git a/src/cli/register-operations.ts b/src/cli/register-operations.ts index 2e928da150..1370faa38a 100644 --- a/src/cli/register-operations.ts +++ b/src/cli/register-operations.ts @@ -26,7 +26,7 @@ import { WORKSPACE_SNAPSHOT_ACTIONS, } from "../sdk/workspace-snapshot.js"; import { readWorkspacePosition } from "../sdk/governance/workspace-position.js"; -import { runClaim, runClaimNext, runRelease } from "./commands/lifecycle/claim.js"; +import { requireClaimTarget, runClaim, runClaimNext, runRelease } from "./commands/lifecycle/claim.js"; import { runContracts } from "./commands/workspace/contracts.js"; import { runDuplicates } from "./commands/query/duplicates.js"; import { runGc } from "./commands/governance/gc.js"; @@ -707,63 +707,41 @@ async function runContractsAction( } } -/** Route explicit start composition or ordinary and ranked claims through SDK operations. */ +/** Route explicit start composition or ordinary, ranked and ranked-start claims through SDK operations. */ async function runClaimAction( id: string | undefined, options: Record, command: Command, ): Promise { - if (options.start === true) { - requireClaimTarget(id, false); - await runStartTaskAction(id as string, options, command); + const next = options.next === true; + const start = options.start === true; + if (start && id !== undefined) { + // The explicit-item composition refuses --next/--if-available with its own typed recovery. + await runStartTaskAction(id, options, command); return; } + requireClaimTarget(id, next, start); const globalOptions = getGlobalOptions(command); const startedAt = Date.now(); const lifecycleOptions = { ...buildLifecycleMutationOptions(options), ifAvailable: options.ifAvailable === true, maxAttempts: options.maxAttempts as string | number | undefined, + ...(start ? { start: true } : {}), }; - requireClaimTarget(id, options.next === true); - const result = - options.next === true - ? await runClaimNext( - Boolean(options.force), - globalOptions, - lifecycleOptions, - { - type: typeof options.type === "string" ? options.type : undefined, - tag: typeof options.tag === "string" ? options.tag : undefined, - priority: options.priority as string | number | undefined, - assigneeFilter: - typeof options.assigneeFilter === "string" - ? options.assigneeFilter - : undefined, - parent: - typeof options.parent === "string" ? options.parent : undefined, - sprint: - typeof options.sprint === "string" ? options.sprint : undefined, - release: - typeof options.release === "string" ? options.release : undefined, - includeDecisions: options.includeDecisions === true, - includeGates: options.includeGates === true, - includeContainers: options.includeContainers === true, - tokenBudget: (options.tokenBudget ?? options.token_budget) as - | string - | number - | undefined, - explainRanking: - options.explainRanking === true || - options.explain_ranking === true, - }, - ) - : await runClaim( - id as string, - Boolean(options.force), - globalOptions, - lifecycleOptions, - ); + const result = next + ? await runClaimNext( + Boolean(options.force), + globalOptions, + lifecycleOptions, + readClaimNextSelection(options), + ) + : await runClaim( + id as string, + Boolean(options.force), + globalOptions, + lifecycleOptions, + ); await invalidateSearchCachesForMutation(globalOptions, result); printResult(result, globalOptions); if (globalOptions.profile) { @@ -771,20 +749,33 @@ async function runClaimAction( } } -/** Enforces the claim target XOR selection contract before dispatch. */ -export function requireClaimTarget( - id: string | undefined, - next: boolean, -): void { - if (!next && !id) - throw new PmCliError("Specify an item id or pass --next", EXIT_CODE.USAGE); - if (next && id) - throw new PmCliError( - "Specify either an item id or --next, not both", - EXIT_CODE.USAGE, - ); +/** Read the ranked-selection filters shared by claim --next and claim --next --start. */ +function readClaimNextSelection( + options: Record, +): Parameters[3] { + return { + type: readOptionString(options, "type"), + tag: readOptionString(options, "tag"), + priority: options.priority as string | number | undefined, + assigneeFilter: readOptionString(options, "assigneeFilter"), + parent: readOptionString(options, "parent"), + sprint: readOptionString(options, "sprint"), + release: readOptionString(options, "release"), + includeDecisions: options.includeDecisions === true, + includeGates: options.includeGates === true, + includeContainers: options.includeContainers === true, + tokenBudget: (options.tokenBudget ?? options.token_budget) as + | string + | number + | undefined, + explainRanking: + options.explainRanking === true || options.explain_ranking === true, + }; } +/** Re-exported so registration callers keep one claim-target contract with the SDK. */ +export { requireClaimTarget }; + /** Route pause composition or ordinary release and invalidate affected search caches. */ async function runReleaseAction( id: string, @@ -1325,7 +1316,7 @@ export function registerOperationCommands(program: Command): void { const claimCommand = program .command("claim") - .option("--start", "Claim and start an explicit item") + .option("--start", "Claim and start (id or --next)") .argument("[id]", "Item id (omit with --next)") .option("--author ", "Mutation author") .option("--message ", "History message") diff --git a/src/core/output/output.ts b/src/core/output/output.ts index ec682a979e..ea81a9bf66 100644 --- a/src/core/output/output.ts +++ b/src/core/output/output.ts @@ -22,7 +22,10 @@ import { } from "../../sdk/read-output-contracts.js"; import { encode as encodeToon, type JsonValue } from "@toon-format/toon"; import { encodePmTableRows } from "../../sdk/output/table-rows.js"; -import { projectPmBriefListEnvelope } from "../../sdk/output/list-envelope.js"; +import { + projectPmBriefListEnvelope, + projectPmBriefSearchEnvelope, +} from "../../sdk/output/list-envelope.js"; import { prepareWellFormedOutputText } from "../shared/output-unicode.js"; const DECLARED_PROCESS_EXIT_CODES = new Set(Object.values(EXIT_CODE)); @@ -600,7 +603,9 @@ export function formatBuiltInOutput( return `${JSON.stringify(value, null, briefList ? undefined : 2)}\n`; } const compactedToon = compactToonValue( - projectEmptyCheckDetails(projectPmBriefListEnvelope(result)), + projectEmptyCheckDetails( + projectPmBriefSearchEnvelope(projectPmBriefListEnvelope(result)), + ), { omitDuplicateCountAliases: true, preserveRowFacts: true }, ); return compactedToon === undefined diff --git a/src/sdk/cli-contracts/tool-schema.ts b/src/sdk/cli-contracts/tool-schema.ts index 160ad37773..2765df1bfc 100644 --- a/src/sdk/cli-contracts/tool-schema.ts +++ b/src/sdk/cli-contracts/tool-schema.ts @@ -1437,8 +1437,11 @@ const PM_TOOL_ACTION_SCHEMA_CONTRACTS: Record = ], oneOfRequired: [["id"], ["next"]], mutuallyExclusiveWhen: [ - [{ property: "start", schema: { const: true } }, { property: "next", schema: { const: true } }], - [{ property: "start", schema: { const: true } }, { property: "ifAvailable", schema: { const: true } }], + [ + { property: "start", schema: { const: true } }, + { property: "ifAvailable", schema: { const: true } }, + { property: "id", schema: { type: "string" } }, + ], ], }, release: { @@ -1517,7 +1520,7 @@ function actionScopedToolParameterMetadata( ): { description: string; examples?: unknown[] } | undefined { if (action === "history-attest" && key === "verify") return { description: "Compare all retained history streams with a detached JSON proof object. Server-local proof paths are not accepted over MCP." }; if (action === "history-attest" && key === "output") return { description: "Local CLI/SDK only. MCP rejects this field; save the returned JSON bundle on the client." }; - if (action === "claim" && key === "start") return { description: "Claim an explicit item and move it to its configured in-progress status." }; + if (action === "claim" && key === "start") return { description: "Move the claimed item to its configured in-progress status; with next, the selected item is claimed and started in one locked write." }; if ((action === "list" || action.startsWith("list-")) && key === "all") { return { description: diff --git a/src/sdk/context-intent-contracts.ts b/src/sdk/context-intent-contracts.ts index dc1425d432..5a4e7ff933 100644 --- a/src/sdk/context-intent-contracts.ts +++ b/src/sdk/context-intent-contracts.ts @@ -86,8 +86,27 @@ export interface PmContextIntentReceipt { binding_constraint?: "explicit_limit" | "token_budget"; /** Machine-readable explanation for the selected row ceiling. */ limit_reason?: string; + /** Present only when the intent's budget reduced a larger caller-supplied row limit. */ + limit_override?: PmContextIntentLimitOverride; } +/** Evidence that a declared intent budget reduced an explicit row limit, with the retry that keeps it. */ +export interface PmContextIntentLimitOverride { + /** Flag whose explicit value the intent budget reduced. */ + flag: "--limit"; + /** Row limit the caller supplied. */ + requested: number; + /** Row limit the intent budget applied. */ + effective: number; + /** Smallest token budget, never below the current one, whose derived ceiling carries the requested rows. */ + token_budget_for_requested: number; + /** Bounded retry that keeps the requested limit under a sufficient explicit budget. */ + restore_with: string; +} + +/** Stable warning code emitted when an intent budget reduces an explicit row limit. */ +export const CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING = "limit_overridden_by_intent"; + /** Built-in intent projections shared by CLI, SDK, MCP, and package authors. */ export const PM_CONTEXT_INTENT_CONTRACTS: readonly PmContextIntentContract[] = Object.freeze([ @@ -385,6 +404,29 @@ type BuiltInContextIntentCommand = | "next" | "search"; +/** Option key carrying an explicit row limit that the intent budget reduced, for receipt disclosure. */ +const INTENT_REQUESTED_LIMIT_OPTION = "intentRequestedLimit"; + +/** Bound an explicit or absent row limit by the intent's budget-derived ceiling, remembering a reduced request. */ +function applyIntentRowLimit( + projected: Record, + budgetDerivedLimit: number, +): string { + const requestedLimit = parsePositiveIntentTokenBudget(projected.limit); + const effectiveLimit = String( + requestedLimit === undefined + ? budgetDerivedLimit + : Math.min(requestedLimit, budgetDerivedLimit), + ); + if (projected.limit === undefined || requestedLimit !== undefined) { + projected.limit = effectiveLimit; + } + if (requestedLimit !== undefined && requestedLimit > budgetDerivedLimit) { + projected[INTENT_REQUESTED_LIMIT_OPTION] = String(requestedLimit); + } + return effectiveLimit; +} + const CONTEXT_INTENT_DEFAULT_APPLIERS: Readonly< Record< BuiltInContextIntentCommand, @@ -416,15 +458,7 @@ const CONTEXT_INTENT_DEFAULT_APPLIERS: Readonly< tokenBudget, explicitTokenBudget, )!; - const requestedLimit = parsePositiveIntentTokenBudget(projected.limit); - const derivedLimit = String( - requestedLimit === undefined - ? budgetDerivedLimit - : Math.min(requestedLimit, budgetDerivedLimit), - ); - if (projected.limit === undefined || requestedLimit !== undefined) { - projected.limit = derivedLimit; - } + const derivedLimit = applyIntentRowLimit(projected, budgetDerivedLimit); if (projected.activityLimit === undefined) { projected.activityLimit = derivedLimit; } @@ -454,14 +488,7 @@ const CONTEXT_INTENT_DEFAULT_APPLIERS: Readonly< tokenBudget, explicitTokenBudget, )!; - const requestedLimit = parsePositiveIntentTokenBudget(projected.limit); - if (projected.limit === undefined || requestedLimit !== undefined) { - projected.limit = String( - requestedLimit === undefined - ? budgetDerivedLimit - : Math.min(requestedLimit, budgetDerivedLimit), - ); - } + applyIntentRowLimit(projected, budgetDerivedLimit); }, next: (projected) => { if (projected.readyOnly === undefined) projected.readyOnly = true; @@ -484,14 +511,7 @@ const CONTEXT_INTENT_DEFAULT_APPLIERS: Readonly< tokenBudget, explicitTokenBudget, )!; - const requestedLimit = parsePositiveIntentTokenBudget(projected.limit); - if (projected.limit === undefined || requestedLimit !== undefined) { - projected.limit = String( - requestedLimit === undefined - ? budgetDerivedLimit - : Math.min(requestedLimit, budgetDerivedLimit), - ); - } + applyIntentRowLimit(projected, budgetDerivedLimit); }, }; @@ -623,6 +643,20 @@ function resolveIntentTokenBudget( return parsed; } +/** Fixed overhead and per-row token cost each row-bounded intent reserves when deriving its page size. */ +function resolveIntentRowCost( + command: BuiltInContextIntentCommand, + intent: string, +): { overhead: number; rowCost: number } { + const contextOrient = command === "context" && intent === "orient"; + if (command === "context") { + return contextOrient + ? { overhead: 1200, rowCost: 600 } + : { overhead: 800, rowCost: 500 }; + } + return { overhead: command === "list" ? 520 : 480, rowCost: 16 }; +} + function resolveIntentRowBudget( command: BuiltInContextIntentCommand, intent: string, @@ -630,16 +664,7 @@ function resolveIntentRowBudget( explicitTokenBudget: boolean, ): number | undefined { if (command === "get" || command === "next") return undefined; - const contextOrient = command === "context" && intent === "orient"; - const overhead = - command === "context" - ? contextOrient - ? 1200 - : 800 - : command === "list" - ? 520 - : 480; - const rowCost = command === "context" ? (contextOrient ? 600 : 500) : 16; + const { overhead, rowCost } = resolveIntentRowCost(command, intent); const calculatedLimit = Math.max( command === "context" ? 1 : 2, Math.floor((tokenBudget - overhead) / rowCost), @@ -906,6 +931,12 @@ export function attachContextIntentReceipt< ? "The caller supplied a smaller row limit than the budget-derived ceiling." : "The selected intent token budget constrains the effective row ceiling."; resultWithDiagnostics.budget_derived_limit = budgetDerivedLimit; + discloseIntentLimitOverride( + { command, builtInCommand, intent: contract.intent }, + receipt, + resultWithDiagnostics, + options, + ); } let projected: Record = { ...resultWithDiagnostics, @@ -1006,12 +1037,9 @@ function isOutputRecord(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); } -/** Build a shell-safe bounded retry while retaining required read positionals. */ -function buildContextIntentRecoveryCommand( - command: string, +/** Return the shell-quoted read positional (item id or search query) a retry must repeat. */ +function contextIntentRecoveryPositional( builtInCommand: BuiltInContextIntentCommand, - intent: string, - receipt: PmContextIntentReceipt, result: Record, ): string { const recoveryTarget = @@ -1022,10 +1050,65 @@ function buildContextIntentRecoveryCommand( : builtInCommand === "search" && typeof result.query === "string" ? result.query : undefined; - const recoveryPositional = - recoveryTarget === undefined - ? "" - : ` '${recoveryTarget.replaceAll("'", `'"'"'`)}'`; + return recoveryTarget === undefined + ? "" + : ` '${recoveryTarget.replaceAll("'", `'"'"'`)}'`; +} + +/** Record a reduced explicit row limit in the receipt, warnings and filters, with the budget that keeps it. */ +function discloseIntentLimitOverride( + identity: { + command: string; + builtInCommand: BuiltInContextIntentCommand; + intent: string; + }, + receipt: PmContextIntentReceipt, + result: Record, + options: Record, +): void { + // Projected options carry the reduced request; raw host options still carry the caller's limit. + const requested = parsePositiveIntentTokenBudget( + options[INTENT_REQUESTED_LIMIT_OPTION] ?? options.limit, + ); + const effective = receipt.budget_derived_limit!; + if (requested === undefined || requested <= effective) return; + const { overhead, rowCost } = resolveIntentRowCost( + identity.builtInCommand, + identity.intent, + ); + const sufficientBudget = Math.max( + receipt.token_budget, + overhead + rowCost * requested, + ); + receipt.limit_reason = `The ${receipt.token_budget}-token ${identity.intent} budget carries ${effective} rows, so it reduced the caller's --limit ${requested}.`; + receipt.limit_override = { + flag: "--limit", + requested, + effective, + token_budget_for_requested: sufficientBudget, + restore_with: `pm ${identity.command}${contextIntentRecoveryPositional(identity.builtInCommand, result)} --for ${identity.intent} --limit ${requested} --token-budget ${sufficientBudget}`, + }; + const warning = `${CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING}:--limit ${requested}->${effective}`; + result.warnings = Array.isArray(result.warnings) + ? [...(result.warnings as unknown[]), warning] + : [warning]; + if (isOutputRecord(result.filters)) { + result.filters = { ...result.filters, requested_limit: String(requested) }; + } +} + +/** Build a shell-safe bounded retry while retaining required read positionals. */ +function buildContextIntentRecoveryCommand( + command: string, + builtInCommand: BuiltInContextIntentCommand, + intent: string, + receipt: PmContextIntentReceipt, + result: Record, +): string { + const recoveryPositional = contextIntentRecoveryPositional( + builtInCommand, + result, + ); const boundedRowLimit = builtInCommand === "get" ? "" diff --git a/src/sdk/core.ts b/src/sdk/core.ts index 128b39c363..ff805ea6cd 100644 --- a/src/sdk/core.ts +++ b/src/sdk/core.ts @@ -64,6 +64,7 @@ export { type AppendResult, type ClaimNextOptions, type ClaimResult, + type ClaimStartReceipt, type CloseResult, type CommentsCommandOptions, type CommentsResult, diff --git a/src/sdk/generated/generated-error-code-catalog-part-1.ts b/src/sdk/generated/generated-error-code-catalog-part-1.ts index 63953e8ae7..cfb7b1501f 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-1.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-1.ts @@ -106,8 +106,8 @@ export const PM_ERROR_CODE_CATALOG_PART_1: PmErrorCodeContract[] = [ class: "usage", recovery: "Inspect the structured error guidance and retry the suggested command.", - sources: ["sdk/lifecycle/plan.ts"], - emitting_commands: ["plan"], + sources: ["sdk/lifecycle/claim.ts", "sdk/lifecycle/plan.ts"], + emitting_commands: ["claim", "plan"], canonical_code: "ambiguous_option_combination", aliases: [], }, diff --git a/src/sdk/generated/generated-error-code-catalog-part-2.ts b/src/sdk/generated/generated-error-code-catalog-part-2.ts index e33ff90a19..83f11de505 100644 --- a/src/sdk/generated/generated-error-code-catalog-part-2.ts +++ b/src/sdk/generated/generated-error-code-catalog-part-2.ts @@ -206,8 +206,9 @@ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ "sdk/governance/assurance-action.ts", "sdk/history-redact.ts", "sdk/invocation-author.ts", + "sdk/lifecycle/claim.ts", ], - emitting_commands: ["*"], + emitting_commands: ["*", "claim"], canonical_code: "missing_required_argument", aliases: [], }, @@ -1437,8 +1438,8 @@ export const PM_ERROR_CODE_CATALOG_PART_2: PmErrorCodeContract[] = [ class: "conflict", recovery: "Inspect the structured error guidance and retry the suggested command.", - sources: ["core/telemetry/observability.ts"], - emitting_commands: ["*"], + sources: ["core/telemetry/observability.ts", "sdk/lifecycle/claim.ts"], + emitting_commands: ["*", "claim"], canonical_code: "terminal_state_conflict", aliases: [], }, diff --git a/src/sdk/index.ts b/src/sdk/index.ts index ae74e45f81..50796a7ce7 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -986,6 +986,7 @@ export { type CalendarOptions, type CalendarResult, type ClaimResult, + type ClaimStartReceipt, type CloseResult, type CloseTaskResult, type CommitImportedItemParams, diff --git a/src/sdk/init-agent-guidance.ts b/src/sdk/init-agent-guidance.ts index bc722b709f..66e9e9d4a1 100644 --- a/src/sdk/init-agent-guidance.ts +++ b/src/sdk/init-agent-guidance.ts @@ -198,8 +198,8 @@ function buildAgentGuidanceBlock( AGENT_GUIDANCE_START_MARKER, "## pm Workflow (Agent Quickstart)", "", - '- Cold start with the measured canonical orientation: `pm context --limit 10 --for orient`; before item mutation, also run `pm search "" --limit 10`, `pm list --status open --limit 20`, and `pm list --status in_progress --limit 20`.', - "- Claim and start atomically: `pm claim --start`.", + '- Cold start with the measured canonical orientation: `pm context --for orient`; before item mutation, also run `pm search "" --limit 10`, `pm list --status open --limit 20`, and `pm list --status in_progress --limit 20`.', + "- Claim and start: `pm claim --start`, or `pm claim --next --start` to take the next available item in one write.", `- Link evidence while coding: \`pm files --add ...\`, \`pm docs --add ...\`, \`pm test --add command="${projectTestCommand}"\`.`, '- Verify and close: `pm test --run --progress`, then atomically record closure evidence with `pm close "" --resolution "" --expected "" --actual "" --validate-close warn`, then `pm release `.', "- Author identity is automatic for supported agent harnesses; use `--author` only for an explicit override.", diff --git a/src/sdk/lifecycle/claim.ts b/src/sdk/lifecycle/claim.ts index f9eda64d1f..a809d5de75 100644 --- a/src/sdk/lifecycle/claim.ts +++ b/src/sdk/lifecycle/claim.ts @@ -21,6 +21,11 @@ import { import { wrapOwnershipConflict } from "../annotations.js"; import { resolveItemOwnershipConflict } from "../ownership-source.js"; import { applyReleaseHandoff, resolveReleaseHandoff } from "./release-handoff.js"; +import { enforceTypeWorkflowTransition } from "./workflow-transition.js"; +import { resolveTypeWorkflows } from "../../core/schema/type-workflows.js"; +import { resolveStartTaskInProgressStatus } from "../start-task-status.js"; +import type { ItemMetadata, PmSettings } from "../../types/index.js"; +import type { RuntimeStatusRegistry } from "../../core/schema/runtime-schema.js"; import { runNext, type NextRecommendation, @@ -50,6 +55,16 @@ export interface ClaimResult { skipped?: boolean; /** Value that configures or reports warnings for this contract. */ warnings?: string[]; + /** Lifecycle transition applied inside the claim's locked mutation when `start` was requested. */ + start?: ClaimStartReceipt; +} + +/** Status transition recorded by a claim that also started its item. */ +export interface ClaimStartReceipt { + /** Lifecycle status observed under the item lock before the claim. */ + from_status: string; + /** Configured in-progress status the claimed item now carries. */ + to_status: string; } /** Result of atomically selecting and claiming the next caller-available item. */ @@ -147,6 +162,8 @@ export interface ClaimMutationOptions { ifAvailable?: boolean; /** Maximum ranked candidates attempted by `claim --next`. */ maxAttempts?: string | number; + /** Move the claimed item to the configured in-progress status in the same locked mutation. */ + start?: boolean; } /** Documents the release mutation options payload exchanged by command, SDK, and package integrations. */ @@ -172,6 +189,7 @@ export async function runClaim( const claimPrincipal = resolveClaimPrincipal(author); let previousAssignee: string | null = null; let skipped = false; + let startEvidence: Pick = {}; const mutationWarnings: string[] = []; const result = await mutateItem({ @@ -238,8 +256,12 @@ export async function runClaim( (field) => document.metadata[field] !== ownership[field], ); Object.assign(document.metadata, ownership); + const { changedFields: startedFields, warnings: startWarnings, ...started } = + applyClaimStart(document.metadata, options.start === true, settings, statusRegistry); + startEvidence = started; + mutationWarnings.push(...startWarnings); return { - changedFields, + changedFields: [...changedFields, ...startedFields], warnings: mutationWarnings, }; }, @@ -263,9 +285,80 @@ export async function runClaim( forced: force, ...(skipped ? { skipped: true } : {}), ...(mutationWarnings.length > 0 ? { warnings: mutationWarnings } : {}), + ...startEvidence, }; } +/** Enforce the claim target XOR selection contract; refusals never ask for a flag the caller already passed. */ +export function requireClaimTarget( + id: string | undefined, + next: boolean, + start = false, +): void { + const startSuffix = start ? " --start" : ""; + if (!next && !id) { + throw new PmCliError( + start + ? "pm claim --start needs an item id, or --next to start the next available item" + : "Specify an item id or pass --next", + EXIT_CODE.USAGE, + { + code: "missing_required_argument", + field: "id", + required: "Pass an item id, or --next to select the next available item.", + nextSteps: [`Retry as pm claim ${startSuffix}, or pm claim --next${startSuffix}.`], + }, + ); + } + if (next && id) { + throw new PmCliError( + "Specify either an item id or --next, not both", + EXIT_CODE.USAGE, + { + code: "ambiguous_option_combination", + required: "Pass either an item id or --next.", + nextSteps: [`Retry as pm claim ${id}${startSuffix} to claim that item, or drop the id to select with --next.`], + }, + ); + } +} + +/** Advance a just-claimed item to the in-progress status under the claim's lock, enforcing configured type workflows. */ +export function applyClaimStart( + metadata: ItemMetadata, + start: boolean, + settings: PmSettings, + registry: RuntimeStatusRegistry, +): Pick & { changedFields: string[]; warnings: string[] } { + if (!start) return { changedFields: [], warnings: [] }; + if (statusIsTerminal(metadata.status, registry)) { + throw new PmCliError( + `Cannot start terminal item ${metadata.id}; reopen it with pm item reopen before starting it`, + EXIT_CODE.CONFLICT, + { + code: "terminal_state_conflict", + item_id: metadata.id, + why: "Starting work moves an active item into progress; a terminal item must be reopened so its recurrence evidence is recorded.", + nextSteps: [`Run pm item reopen ${metadata.id} "", then pm claim ${metadata.id} --start.`], + }, + ); + } + const startStatus = resolveStartTaskInProgressStatus(registry); + const enforcement = settings.governance.workflow_enforcement ?? "off"; + const warning = enforceTypeWorkflowTransition({ + enforcement, + typeWorkflows: enforcement === "off" ? [] : resolveTypeWorkflows(settings.schema), + statusRegistry: registry, + typeName: metadata.type, + fromStatus: metadata.status, + toStatus: startStatus, + }); + const receipt = { from_status: metadata.status, to_status: startStatus }; + const changedFields = metadata.status === startStatus ? [] : ["status"]; + metadata.status = startStatus; + return { start: receipt, changedFields, warnings: warning ? [warning] : [] }; +} + /** Selects ranked actionable work and claims the first candidate still available under the item lock. Conflicts caused by parallel claimers advance to the next candidate instead of returning a thundering-herd failure. */ export async function runClaimNext( force: boolean, diff --git a/src/sdk/lifecycle/mcp-actions.ts b/src/sdk/lifecycle/mcp-actions.ts index 7eddf6693a..a8449d5d9e 100644 --- a/src/sdk/lifecycle/mcp-actions.ts +++ b/src/sdk/lifecycle/mcp-actions.ts @@ -9,7 +9,7 @@ import type { GlobalOptions } from "../../core/shared/command-types.js"; import { PmCliError } from "../../core/shared/errors.js"; import { runClose } from "./close.js"; import { runReopen } from "./reopen.js"; -import { runClaim, runClaimNext, runRelease } from "./claim.js"; +import { requireClaimTarget, runClaim, runClaimNext, runRelease } from "./claim.js"; import { runStartTask, runPauseTask, runCloseTask } from "./task-composition.js"; import { readRuntimeString, withMutationCompaction, mutationOptionsWithOverrides } from "../runtime-input.js"; @@ -27,15 +27,18 @@ export interface LifecycleMutationActionContext { global: GlobalOptions; } -/** Dispatch explicit and ranked claims through the same compact receipt contract. */ +/** Dispatch explicit, ranked and ranked-start claims through the same compact receipt contract. */ export async function runMcpClaimAction(context: LifecycleMutationActionContext): Promise { const { changedFields, idOnly, runnerOptions } = withMutationCompaction(context.args, context.options); const force = context.force === true || runnerOptions.force === true; const selectionOptions = { ...context.args, ...runnerOptions }; + const next = context.args.next === true || runnerOptions.next === true; + const id = context.id ?? readRuntimeString(runnerOptions, "id"); if (selectionOptions.start === true) { - return runMcpTaskCompositionAction(context, "start_task"); + if (id !== undefined) return runMcpTaskCompositionAction(context, "start_task"); + requireClaimTarget(id, next, true); } - const result = context.args.next === true || runnerOptions.next === true + const result = next ? await runClaimNext(force, context.global, selectionOptions, selectionOptions) : await runClaim(requireLifecycleItemId(context, runnerOptions), force, context.global, selectionOptions); return projectMutationResult(result, { diff --git a/src/sdk/lifecycle/task-composition.ts b/src/sdk/lifecycle/task-composition.ts index 42fc6ac8b1..b754d43658 100644 --- a/src/sdk/lifecycle/task-composition.ts +++ b/src/sdk/lifecycle/task-composition.ts @@ -59,7 +59,8 @@ export async function runStartTask(id: string, options: TaskCompositionOptions, } const settings = await readSettings(resolvePmRoot(process.cwd(), global.path)); const status = resolveStartTaskInProgressStatus(resolveRuntimeStatusRegistry(settings.schema)); - const claim = await runClaim(id, options.force === true, global, options); + // The explicit composition keeps claim and status as separate audited steps; only claim --next --start shares one write. + const claim = await runClaim(id, options.force === true, global, mutationOptionsWithOverrides(options, {}, ["start"])); const canonicalId = claim.item.id as string; const update = await finishComposition(canonicalId, "claim", /** Advance the persisted claim identity using the configured workflow while preserving constituent mutation policy. */ () => runUpdate(canonicalId, mutationOptionsWithOverrides(options, { status }, ["assignee", "start", "next", "ifAvailable", "maxAttempts"]) as UpdateCommandOptions, global)); return { id: canonicalId, action: "start_task", claim, update }; diff --git a/src/sdk/output.ts b/src/sdk/output.ts index a67ec7f524..ef48bed33f 100644 --- a/src/sdk/output.ts +++ b/src/sdk/output.ts @@ -19,7 +19,10 @@ export { type PmEncodedTableRows, } from "./output/table-rows.js"; -export { projectPmBriefListEnvelope } from "./output/list-envelope.js"; +export { + projectPmBriefListEnvelope, + projectPmBriefSearchEnvelope, +} from "./output/list-envelope.js"; export { assertWellFormedOutputText, toWellFormedText } from "../core/shared/output-unicode.js"; diff --git a/src/sdk/output/list-envelope.ts b/src/sdk/output/list-envelope.ts index 67ee62efbf..750b61d6ca 100644 --- a/src/sdk/output/list-envelope.ts +++ b/src/sdk/output/list-envelope.ts @@ -56,8 +56,11 @@ function hasCompactibleSource( ); } -/** Only the default full-item omission has a shorter equivalent restoration pointer. */ -function hasDefaultOmissions(value: unknown): boolean { +/** Only a single default omission group has a shorter equivalent restoration pointer. */ +function hasDefaultOmissions( + value: unknown, + groupName = "full_item_fields", +): boolean { if (!isRecord(value) || !Array.isArray(value.omitted_field_groups)) return false; const groups = value.omitted_field_groups; @@ -69,7 +72,7 @@ function hasDefaultOmissions(value: unknown): boolean { groups.length === 1 && isRecord(group) && Object.keys(group).length === 2 && - group.name === "full_item_fields" && + group.name === groupName && group.restore_with === "--full" ); } @@ -116,12 +119,92 @@ export function projectPmBriefListEnvelope(value: unknown): unknown { ...rest, ...(!completed ? { total, has_more: hasMore, completeness: source } : {}), ...(hasMore ? { next_cursor: nextCursor } : {}), - ...(isRecord(sorting) && - sorting.sort === "default" && - sorting.order === "asc" && - Object.keys(sorting).length === 2 - ? {} - : { sorting }), + ...projectDefaultSorting(sorting), + ...compactEchoedFilters(rest.filters, rest.applied_limit), + details: "--full", + }; +} + +/** Recognize the default ascending sort, with or without its derived comparator list. */ +function isDefaultSorting(value: unknown): value is Record { + return ( + isRecord(value) && + value.sort === "default" && + value.order === "asc" && + Object.keys(value).every( + (key) => key === "sort" || key === "order" || key === "effective_keys", + ) + ); +} + +/** Recognize one `[key, order]` comparator stage. */ +function isSortKeyPair(value: unknown): value is [string, string] { + return ( + Array.isArray(value) && + value.length === 2 && + typeof value[0] === "string" && + typeof value[1] === "string" + ); +} + +/** + * Drop the legacy default sort/order echo; keep the effective default order + * visible (GH-1507) as one comparator line. Other sorts pass through unchanged. + */ +function projectDefaultSorting(value: unknown): { sorting?: unknown } { + if (!isDefaultSorting(value)) return { sorting: value }; + const keys = value.effective_keys; + if (keys === undefined) return {}; + if (!Array.isArray(keys) || !keys.every(isSortKeyPair)) return { sorting: value }; + return { + sorting: { + effective_keys: keys.map(([key, order]) => `${key} ${order}`).join(", "), + }, + }; +} + +/** Drop a filters.limit echo that applied_limit already states, and the filters block once nothing else remains. */ +function compactEchoedFilters( + filters: unknown, + appliedLimit: unknown, +): { filters?: unknown } { + if (!isRecord(filters)) return filters === undefined ? {} : { filters }; + const remaining = Object.fromEntries( + Object.entries(filters).filter( + ([key, value]) => + !(key === "limit" && String(value) === String(appliedLimit)) && + !(key === "runtime_filters" && isRecord(value) && Object.keys(value).length === 0), + ), + ); + return Object.keys(remaining).length === 0 ? {} : { filters: remaining }; +} + +/** + * Compact a standard search page without mutating its typed SDK result: + * drops `truncated` when it repeats `has_more`, a `filters.limit` echo of + * `applied_limit`, and the single default `projection_metadata` omission + * receipt in favour of `details: "--full"`. Custom, partial and inconsistent + * receipts pass through unchanged; JSON hosts should keep the original result. + */ +export function projectPmBriefSearchEnvelope(value: unknown): unknown { + if ( + !isRecord(value) || + typeof value.query !== "string" || + !Array.isArray(value.items) || + Object.hasOwn(value, "details") || + !hasStandardPagination(value, value.items.length) || + !hasDefaultOmissions(value.omission_receipt, "projection_metadata") + ) + return value; + const { + truncated: _truncated, + omission_receipt: _omissions, + filters, + ...rest + } = value; + return { + ...rest, + ...compactEchoedFilters(filters, rest.applied_limit), details: "--full", }; } diff --git a/src/sdk/runtime-input.ts b/src/sdk/runtime-input.ts index 83276e1c2e..0e85551a6c 100644 --- a/src/sdk/runtime-input.ts +++ b/src/sdk/runtime-input.ts @@ -22,6 +22,8 @@ import type { UpdateManyCommandOptions } from "./lifecycle/update-many.js"; import { CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, CREATE_COMMANDER_REPEATABLE_OPTION_CONTRACTS, CREATE_COMMANDER_STRING_OPTION_CONTRACTS, UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS, UPDATE_COMMANDER_REPEATABLE_OPTION_CONTRACTS, UPDATE_COMMANDER_STRING_OPTION_CONTRACTS } from "./cli-contracts/commander-mutation-options.js"; import { TOOL_CREATE_OPTION_CONTRACTS, TOOL_SHARED_CREATE_UPDATE_OPTION_CONTRACTS, TOOL_UPDATE_OPTION_CONTRACTS } from "./cli-contracts/tool-option-contracts.js"; import { PM_TOOL_PARAMETER_PROPERTIES } from "./cli-contracts/tool-parameter-tables.js"; +import { PM_TOOL_ACTION_PARAMETER_CONTRACTS } from "./cli-contracts/tool-schema.js"; +import type { PmToolAction } from "./cli-contracts/enum-contracts.js"; import type { GraphCommandOptions } from "./graph/run.js"; import type { ListOptions } from "./query/list.js"; import { @@ -347,6 +349,36 @@ export const HOISTED_ACTION_OPTION_KEYS: Readonly = new Set([ + "context", + "get", + "list", + "next", + "search", +]); + +/** Positional operands the read handlers take from the top-level arguments rather than options. */ +const READ_ACTION_OPERAND_KEYS: ReadonlySet = new Set(["query", "keywords"]); + +/** Hoist every optional parameter a read action's tool contract declares, so no declared flat argument is silently ignored. */ +function declaredReadActionOptionKeys(action: string): readonly string[] { + return CONTRACT_HOISTED_READ_ACTIONS.has(action) + ? PM_TOOL_ACTION_PARAMETER_CONTRACTS[action as PmToolAction].optional!.filter( + (key) => !READ_ACTION_OPERAND_KEYS.has(key), + ) + : []; +} + +/** Every top-level argument an action reads from options: its hand-listed keys, declared read parameters, and universal read-output controls. */ +function hoistedActionOptionKeys(action: string): readonly string[] { + return [ + ...(HOISTED_ACTION_OPTION_KEYS[action] ?? []), + ...declaredReadActionOptionKeys(action), + ...UNIVERSAL_READ_OUTPUT_OPTION_KEYS, + ]; +} + const UNIVERSAL_READ_OUTPUT_OPTION_KEYS = [ "outputInclude", "outputLimit", @@ -535,10 +567,7 @@ export function optionsWithAuthor( } hoistedTopLevel[key] = args[key]; }; - for (const key of HOISTED_ACTION_OPTION_KEYS[action ?? ""] ?? []) { - hoistKey(key); - } - for (const key of UNIVERSAL_READ_OUTPUT_OPTION_KEYS) { + for (const key of hoistedActionOptionKeys(action ?? "")) { hoistKey(key); } if (action !== undefined && isMcpMutationTransportInput(args)) { diff --git a/src/sdk/runtime-public-contracts.ts b/src/sdk/runtime-public-contracts.ts index acced460c6..1c28f726c6 100644 --- a/src/sdk/runtime-public-contracts.ts +++ b/src/sdk/runtime-public-contracts.ts @@ -170,6 +170,8 @@ export type PmClientFullMutationOptions = Omit { - const query = readRequiredString(ctx.args, "query"); + // keywords is the declared alias of the query operand. + const query = + readString(ctx.args, "query") ?? + readString(ctx.args, "keywords") ?? + readRequiredString(ctx.args, "query"); const searchOptions = applyContextIntentProjection( "search", ctx.options, diff --git a/tests/fixtures/contracts/full.json b/tests/fixtures/contracts/full.json index a252ca11d1..adec076243 100644 --- a/tests/fixtures/contracts/full.json +++ b/tests/fixtures/contracts/full.json @@ -21372,6 +21372,7 @@ "class": "usage", "code": "ambiguous_option_combination", "emitting_commands": [ + "claim", "plan" ], "exit_code": 2, @@ -21379,6 +21380,7 @@ "owned_states": [], "recovery": "Inspect the structured error guidance and retry the suggested command.", "sources": [ + "sdk/lifecycle/claim.ts", "sdk/lifecycle/plan.ts" ], "stability": "stable" @@ -25278,7 +25280,8 @@ "class": "usage", "code": "missing_required_argument", "emitting_commands": [ - "*" + "*", + "claim" ], "exit_code": 2, "meaning": "Missing required argument condition.", @@ -25292,7 +25295,8 @@ "core/telemetry/observability.ts", "sdk/governance/assurance-action.ts", "sdk/history-redact.ts", - "sdk/invocation-author.ts" + "sdk/invocation-author.ts", + "sdk/lifecycle/claim.ts" ], "stability": "stable" }, @@ -26906,14 +26910,16 @@ "class": "conflict", "code": "terminal_state_conflict", "emitting_commands": [ - "*" + "*", + "claim" ], "exit_code": 4, "meaning": "Terminal state conflict condition.", "owned_states": [], "recovery": "Inspect the structured error guidance and retry the suggested command.", "sources": [ - "core/telemetry/observability.ts" + "core/telemetry/observability.ts", + "sdk/lifecycle/claim.ts" ], "stability": "stable" }, @@ -33695,38 +33701,22 @@ }, { "properties": { - "next": { - "const": true - } - }, - "required": [ - "next" - ] - } - ] - } - }, - { - "not": { - "allOf": [ - { - "properties": { - "start": { + "ifAvailable": { "const": true } }, "required": [ - "start" + "ifAvailable" ] }, { "properties": { - "ifAvailable": { - "const": true + "id": { + "type": "string" } }, "required": [ - "ifAvailable" + "id" ] } ] @@ -33955,7 +33945,7 @@ "type": "string" }, "start": { - "description": "Claim an explicit item and move it to its configured in-progress status.", + "description": "Move the claimed item to its configured in-progress status; with next, the selected item is claimed and started in one locked write.", "type": "boolean" }, "tag": { @@ -64965,38 +64955,22 @@ }, { "properties": { - "next": { - "const": true - } - }, - "required": [ - "next" - ] - } - ] - } - }, - { - "not": { - "allOf": [ - { - "properties": { - "start": { + "ifAvailable": { "const": true } }, "required": [ - "start" + "ifAvailable" ] }, { "properties": { - "ifAvailable": { - "const": true + "id": { + "type": "string" } }, "required": [ - "ifAvailable" + "id" ] } ] @@ -65229,7 +65203,7 @@ "type": "string" }, "start": { - "description": "Claim an explicit item and move it to its configured in-progress status.", + "description": "Move the claimed item to its configured in-progress status; with next, the selected item is claimed and started in one locked write.", "type": "boolean" }, "tag": { diff --git a/tests/integration/cli.integration.spec.ts b/tests/integration/cli.integration.spec.ts index b0f1dab472..6de492fe54 100644 --- a/tests/integration/cli.integration.spec.ts +++ b/tests/integration/cli.integration.spec.ts @@ -3504,7 +3504,7 @@ describe("CLI integration (sandboxed PM_PATH)", () => { const guidancePath = path.join(context.tempRoot, "AGENTS.md"); const guidanceBody = await readFile(guidancePath, "utf8"); expect(guidanceBody).toContain(""); - expect(guidanceBody).toContain("pm context --limit 10"); + expect(guidanceBody).toContain("pm context --for orient"); expect(guidanceBody).toContain("pm close "); const guidanceStatusAfter = context.runCli(["init", "--agent-guidance", "status", "--json"], { expectJson: true }); diff --git a/tests/unit/cli/compact-agent-output.spec.ts b/tests/unit/cli/compact-agent-output.spec.ts index 6ba5a9f377..d5a82ddb8e 100644 --- a/tests/unit/cli/compact-agent-output.spec.ts +++ b/tests/unit/cli/compact-agent-output.spec.ts @@ -5,7 +5,10 @@ import { formatCommanderErrorForJson, } from "../../../src/cli/error-guidance.js"; import { attachReadOutputContracts } from "../../../src/sdk/context-intent-contracts.js"; -import { projectPmBriefListEnvelope } from "../../../src/sdk/output.js"; +import { + projectPmBriefListEnvelope, + projectPmBriefSearchEnvelope, +} from "../../../src/sdk/output.js"; const completeList = { items: ["one", "two", "three"].map((title, index) => ({ @@ -368,3 +371,100 @@ describe("compact agent presentation", () => { ); }); }); + +const searchPage = { + query: "release workflow", + mode: "hybrid", + items: [ + { id: "pm-a", title: "A", matched_fields: ["title"] }, + { id: "pm-b", title: "B", matched_fields: ["title", "body"] }, + ], + count: 2, + total: 9, + applied_limit: 2, + has_more: true, + truncated: true, + next_cursor: "next", + filters: { limit: "2" }, + omission_receipt: { + has_omissions: true, + omitted_field_group_count: 1, + omitted_field_groups: [{ name: "projection_metadata", restore_with: "--full" }], + }, +}; + +describe("GH-1642 brief search and list envelopes", () => { + it("drops the repeated pagination alias, limit echo and default omission receipt from a search page", () => { + const before = structuredClone(searchPage); + expect(projectPmBriefSearchEnvelope(searchPage)).toEqual({ + query: "release workflow", + mode: "hybrid", + items: searchPage.items, + count: 2, + total: 9, + applied_limit: 2, + has_more: true, + next_cursor: "next", + details: "--full", + }); + expect(searchPage).toEqual(before); + const text = formatBuiltInOutput(searchPage, "toon"); + expect(text).not.toContain("truncated:"); + expect(text).not.toContain("omission_receipt:"); + expect(text).not.toContain("filters:"); + expect(text).toContain('details: "--full"'); + expect(Buffer.byteLength(text)).toBeLessThan(Buffer.byteLength(formatBuiltInOutput({ ...searchPage, details: { keep: true } }, "toon"))); + expect(JSON.parse(formatBuiltInOutput(searchPage, "json"))).toEqual(searchPage); + }); + + it("keeps filters that carry information and drops only limit echoes and empty runtime filters", () => { + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { type: "Task", limit: "2" } })).toMatchObject({ filters: { type: "Task" } }); + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { limit: "5" } })).toMatchObject({ filters: { limit: "5" } }); + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: null })).toMatchObject({ filters: null }); + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: undefined })).not.toHaveProperty("filters"); + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { limit: "2", runtime_filters: {} } })).not.toHaveProperty("filters"); + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { runtime_filters: { team: "core" } } })).toMatchObject({ filters: { runtime_filters: { team: "core" } } }); + expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { runtime_filters: null } })).toMatchObject({ filters: { runtime_filters: null } }); + }); + + it.each([ + null, + [], + { ...searchPage, query: 1 }, + { ...searchPage, items: null }, + { ...searchPage, details: "producer" }, + { ...searchPage, truncated: false }, + { ...searchPage, omission_receipt: { ...searchPage.omission_receipt, omitted_field_groups: [{ name: "full_item_fields", restore_with: "--full" }] } }, + { ...searchPage, omission_receipt: { ...searchPage.omission_receipt, omitted_field_group_count: 2 } }, + ])("leaves non-standard search envelopes intact: %j", (value) => { + expect(projectPmBriefSearchEnvelope(value)).toBe(value); + }); + + it("keeps the effective default order on one line and drops the legacy sort echo and limit echo", () => { + const value = { + ...completeList, + total: 12, + applied_limit: 3, + has_more: true, + truncated: true, + next_cursor: "continue", + filters: { status: "open", limit: "3", runtime_filters: {} }, + sorting: { sort: "default", order: "asc", effective_keys: [["priority", "asc"], ["id", "asc"]] }, + }; + const projected = projectPmBriefListEnvelope(value) as Record; + expect(projected.sorting).toEqual({ effective_keys: "priority asc, id asc" }); + expect(projected.filters).toEqual({ status: "open" }); + for (const sorting of [ + { ...value.sorting, provider: "keep" }, + { ...value.sorting, effective_keys: "priority" }, + { ...value.sorting, effective_keys: [["priority"]] }, + ]) { + expect(projectPmBriefListEnvelope({ ...value, sorting })).toMatchObject({ sorting }); + } + const text = formatBuiltInOutput(value, "toon"); + expect(text).toContain('effective_keys: "priority asc, id asc"'); + expect(text).not.toContain('sort: "default"'); + expect(text).toContain('status: "open"'); + expect(JSON.parse(formatBuiltInOutput(value, "json"))).toEqual(value); + }); +}); diff --git a/tests/unit/commands/register-commands.spec.ts b/tests/unit/commands/register-commands.spec.ts index 1525a4ba04..c013f77e9d 100644 --- a/tests/unit/commands/register-commands.spec.ts +++ b/tests/unit/commands/register-commands.spec.ts @@ -82,7 +82,10 @@ vi.mock("../../../src/cli/commands/workspace/merge.js", () => ({ vi.mock("../../../src/cli/commands/workspace/contracts.js", () => ({ runContracts: vi.fn(), })); -vi.mock("../../../src/cli/commands/lifecycle/claim.js", () => lifecyclePrimitives); +vi.mock("../../../src/cli/commands/lifecycle/claim.js", async (importOriginal) => ({ + ...lifecyclePrimitives, + requireClaimTarget: (await importOriginal()).requireClaimTarget, +})); vi.mock("../../../src/cli/commands/lifecycle/create.js", () => ({ runCreate: vi.fn() })); vi.mock("../../../src/cli/commands/lifecycle/copy.js", () => ({ runCopy: vi.fn() })); vi.mock("../../../src/cli/commands/lifecycle/focus.js", () => ({ runFocus: vi.fn() })); @@ -93,7 +96,10 @@ vi.mock("../../../src/cli/commands/lifecycle/update-many.js", () => ({ vi.mock("../../../src/cli/commands/lifecycle/close.js", () => lifecyclePrimitives); // Lifecycle composition now lives in the SDK; share the primitive spies so // registration tests continue checking normalized options and operation order. -vi.mock("../../../src/sdk/lifecycle/claim.js", () => lifecyclePrimitives); +vi.mock("../../../src/sdk/lifecycle/claim.js", async (importOriginal) => ({ + ...lifecyclePrimitives, + requireClaimTarget: (await importOriginal()).requireClaimTarget, +})); vi.mock("../../../src/sdk/lifecycle/update.js", () => lifecyclePrimitives); vi.mock("../../../src/sdk/lifecycle/close.js", () => lifecyclePrimitives); vi.mock("../../../src/cli/commands/lifecycle/close-many.js", () => ({ @@ -943,6 +949,18 @@ describe("operation command actions", () => { ).rejects.toThrow("Unknown workspace snapshot action"); }); + it("routes claim --next --start through one ranked selection that also starts the item (GH-1643)", async () => { + vi.mocked(runClaimNext).mockResolvedValueOnce({ id: "pm-next-start" } as never); + await runCli("claim", "--next", "--start", "--type", "Task"); + expect(runClaimNext).toHaveBeenLastCalledWith( + false, + expect.any(Object), + expect.objectContaining({ start: true, ifAvailable: false }), + expect.objectContaining({ type: "Task" }), + ); + await expect(runCli("claim", "--start")).rejects.toThrow("pm claim --start needs an item id"); + }); + it("routes claim --next through ranked atomic selection", async () => { vi.mocked(runClaimNext).mockResolvedValueOnce({ id: "pm-next" } as never); await runCli( diff --git a/tests/unit/commands/workspace/init-command.spec.ts b/tests/unit/commands/workspace/init-command.spec.ts index 91576a2790..f79ee9f0f9 100644 --- a/tests/unit/commands/workspace/init-command.spec.ts +++ b/tests/unit/commands/workspace/init-command.spec.ts @@ -1206,7 +1206,7 @@ describe("runInit", () => { expect(firstGuidance).toContain( "", ); - expect(firstGuidance).toContain("pm context --limit 10"); + expect(firstGuidance).toContain("pm context --for orient"); expect(firstGuidance).toContain("Author identity is automatic"); const readded = await runInit( diff --git a/tests/unit/mcp/read-tool-flat-parameters.spec.ts b/tests/unit/mcp/read-tool-flat-parameters.spec.ts new file mode 100644 index 0000000000..e14d078195 --- /dev/null +++ b/tests/unit/mcp/read-tool-flat-parameters.spec.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from "vitest"; +import { PM_TOOL_ACTION_PARAMETER_CONTRACTS } from "../../../src/sdk/cli-contracts/tool-schema.js"; +import { optionsWithAuthor } from "../../../src/sdk/runtime-input.js"; +import { runAction } from "../../../src/sdk/runtime.js"; +import { createTestItemId } from "../../helpers/itemFactory.js"; +import { withTempPmPath } from "../../helpers/withTempPmPath.js"; + +const READ_ACTIONS = ["context", "get", "list", "next", "search"] as const; + +describe("MCP read tools read every declared flat parameter (pm-hke9z7)", () => { + it.each(READ_ACTIONS)("hoists every optional %s contract parameter from flat arguments", (action) => { + const declared = PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional ?? []; + expect(declared.length).toBeGreaterThan(0); + // Search's query and its keywords alias are operands read from the top-level arguments. + const unread = declared.filter( + (key) => key !== "query" && key !== "keywords" && !(key in optionsWithAuthor({ [key]: "flat" }, action)), + ); + expect(unread).toEqual([]); + expect(optionsWithAuthor({ query: "q", keywords: "k" }, action)).not.toHaveProperty("query"); + }); + + it("lets nested options win over the same flat argument", () => { + expect(optionsWithAuthor({ limit: 5, options: { limit: 2 } }, "context")).toMatchObject({ limit: 2 }); + expect(optionsWithAuthor({ for: "orient" }, "update")).not.toHaveProperty("for"); + }); + + it("applies flat intent and limit arguments exactly like nested options", async () => { + await withTempPmPath(async (context) => { + const id = createTestItemId(context, { title: "Flat MCP argument" }); + const common = { path: context.pmPath, noExtensions: true }; + const read = async (action: string, flat: Record) => { + const [flatResult, nestedResult] = await Promise.all([ + runAction({ ...common, action, ...flat }), + runAction({ ...common, action, options: flat }), + ]); + return [flatResult, nestedResult] as Array>; + }; + for (const [action, args] of [ + ["context", { for: "orient", limit: 10 }], + ["list", { for: "triage" }], + ["next", { for: "execute" }], + ] as const) { + const [flat, nested] = await read(action, args); + expect(flat.context_intent).toMatchObject({ intent: args.for }); + expect(flat.context_intent).toEqual(nested.context_intent); + } + const [flatContext] = await read("context", { for: "orient", limit: 10 }); + expect(flatContext).toMatchObject({ filters: { limit: "3", requested_limit: "10" } }); + const [flatGet, nestedGet] = await Promise.all([ + runAction({ ...common, action: "get", id, for: "inspect" }), + runAction({ ...common, action: "get", id, options: { for: "inspect" } }), + ]) as Array>; + expect(flatGet.context_intent).toMatchObject({ intent: "inspect" }); + expect(flatGet.context_intent).toEqual(nestedGet.context_intent); + const flatSearch = (await runAction({ ...common, action: "search", query: "Flat", for: "discover" })) as Record; + expect(flatSearch.context_intent).toMatchObject({ intent: "discover" }); + const aliased = (await runAction({ ...common, action: "search", keywords: "Flat" })) as { items: Array<{ id: string }> }; + expect(aliased.items.map((item) => item.id)).toContain(id); + await expect(runAction({ ...common, action: "search" })).rejects.toThrow("Missing required argument: query"); + }); + }); +}); diff --git a/tests/unit/scripts/release/agent-task-token-gate.spec.ts b/tests/unit/scripts/release/agent-task-token-gate.spec.ts index 45832f3041..2254607d01 100644 --- a/tests/unit/scripts/release/agent-task-token-gate.spec.ts +++ b/tests/unit/scripts/release/agent-task-token-gate.spec.ts @@ -1096,7 +1096,7 @@ describe("agent-task transcript token gate", () => { vi.spyOn(childProcess, "spawnSync").mockImplementation((...args) => { const argv = Array.isArray(args[1]) ? args[1] : []; if (argv.includes("--token-accounting")) accountedInvocations += 1; - const baselineArgs = [path.resolve("dist/cli.js"), "--json", "context", "--for", "orient", "--limit", "10"]; + const baselineArgs = [path.resolve("dist/cli.js"), "--json", "context", "--for", "orient"]; if (args[0] === process.execPath && JSON.stringify(argv) === JSON.stringify(baselineArgs)) { refusedBaselines += 1; return { status: 7, stdout: "", stderr: "", pid: 0, signal: null } as ReturnType; diff --git a/tests/unit/sdk/context/intent-limit-override.spec.ts b/tests/unit/sdk/context/intent-limit-override.spec.ts new file mode 100644 index 0000000000..b5adb22f9c --- /dev/null +++ b/tests/unit/sdk/context/intent-limit-override.spec.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from "vitest"; +import { + CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING, + applyContextIntentProjection, + attachContextIntentReceipt, +} from "../../../../src/sdk/context-intent-contracts.js"; + +describe("GH-1644 intent budgets never silently reduce an explicit --limit", () => { + it("remembers a reduced explicit limit only when the budget actually reduced it", () => { + expect(applyContextIntentProjection("context", { for: "orient", limit: "10" })).toMatchObject({ + limit: "3", + activityLimit: "3", + intentRequestedLimit: "10", + }); + expect(applyContextIntentProjection("context", { for: "orient", limit: "2" })).not.toHaveProperty("intentRequestedLimit"); + expect(applyContextIntentProjection("context", { for: "orient" })).not.toHaveProperty("intentRequestedLimit"); + expect(applyContextIntentProjection("list", { for: "triage", limit: "150" })).toMatchObject({ limit: "100", intentRequestedLimit: "150" }); + expect(applyContextIntentProjection("search", { for: "discover", limit: "200" }, ["q"])).toMatchObject({ limit: "82", intentRequestedLimit: "200" }); + }); + + it("discloses the reduction in the receipt, warnings and filters with a retry that keeps the limit", () => { + const options = applyContextIntentProjection("context", { for: "orient", limit: "10" }); + const result = attachContextIntentReceipt("context", options, { + filters: { limit: "3" }, + high_level: [], + low_level: [], + warnings: ["producer_warning"], + }); + expect(result.context_intent).toMatchObject({ + binding_constraint: "token_budget", + budget_derived_limit: 3, + limit_reason: "The 3000-token orient budget carries 3 rows, so it reduced the caller's --limit 10.", + limit_override: { + flag: "--limit", + requested: 10, + effective: 3, + token_budget_for_requested: 7200, + restore_with: "pm context --for orient --limit 10 --token-budget 7200", + }, + }); + expect(result).toMatchObject({ + filters: { limit: "3", requested_limit: "10" }, + warnings: ["producer_warning", `${CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING}:--limit 10->3`], + }); + const retry = applyContextIntentProjection("context", { for: "orient", limit: "10", tokenBudget: "7200" }); + expect(retry).toMatchObject({ limit: "10" }); + expect(retry).not.toHaveProperty("intentRequestedLimit"); + }); + + it("reads the caller's limit from raw host options and quotes search positionals in the retry", () => { + const listed = attachContextIntentReceipt("list", { for: "triage", limit: "150" }, { items: [] }); + expect(listed.context_intent?.limit_override).toMatchObject({ + requested: 150, + effective: 100, + token_budget_for_requested: 3200, + restore_with: "pm list --for triage --limit 150 --token-budget 3200", + }); + expect(listed).toMatchObject({ warnings: [`${CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING}:--limit 150->100`] }); + expect(listed).not.toHaveProperty("filters"); + const searched = attachContextIntentReceipt("search", { for: "discover", limit: "200" }, { query: "it's", items: [] }); + expect(searched.context_intent?.limit_override?.restore_with).toBe( + `pm search 'it'"'"'s' --for discover --limit 200 --token-budget 3680`, + ); + const honoured = attachContextIntentReceipt("context", { for: "orient", limit: "2" }, { filters: { limit: "2" } }); + expect(honoured.context_intent).not.toHaveProperty("limit_override"); + expect(honoured).not.toHaveProperty("warnings"); + expect(honoured.filters).toEqual({ limit: "2" }); + }); +}); diff --git a/tests/unit/sdk/lifecycle/claim-start.spec.ts b/tests/unit/sdk/lifecycle/claim-start.spec.ts new file mode 100644 index 0000000000..afb9f7c510 --- /dev/null +++ b/tests/unit/sdk/lifecycle/claim-start.spec.ts @@ -0,0 +1,139 @@ +import { describe, expect, it } from "vitest"; +import { resolveRuntimeStatusRegistry } from "../../../../src/core/schema/runtime-schema.js"; +import { readSettings } from "../../../../src/core/store/settings.js"; +import { EXIT_CODE } from "../../../../src/core/shared/constants.js"; +import type { PmCliError } from "../../../../src/core/shared/errors.js"; +import { applyClaimStart, requireClaimTarget, runClaimNext } from "../../../../src/sdk/lifecycle/claim.js"; +import { PmClient, runAction } from "../../../../src/sdk/runtime.js"; +import type { ItemMetadata } from "../../../../src/types/index.js"; +import { createTestItemId } from "../../../helpers/itemFactory.js"; +import { withTempPmPath } from "../../../helpers/withTempPmPath.js"; + +/** Collect the guidance a refusal gives so it can be checked against the flags the caller passed. */ +function refusalGuidance(run: () => void): { code: unknown; text: string } { + try { + run(); + } catch (error) { + const context = (error as PmCliError).context; + return { + code: context.code, + text: [(error as Error).message, context.required ?? "", ...(context.nextSteps ?? [])].join(" "), + }; + } + throw new Error("expected a refusal"); +} + +describe("GH-1643 claim target refusals", () => { + it("accepts exactly one of an id or --next", () => { + expect(requireClaimTarget(undefined, true)).toBeUndefined(); + expect(requireClaimTarget("pm-a", false)).toBeUndefined(); + expect(requireClaimTarget(undefined, true, true)).toBeUndefined(); + }); + + it("types a missing target and suggests --next only when the caller did not pass it", () => { + const plain = refusalGuidance(() => requireClaimTarget(undefined, false)); + expect(plain).toMatchObject({ code: "missing_required_argument" }); + expect(plain.text).toContain("Specify an item id or pass --next"); + const start = refusalGuidance(() => requireClaimTarget(undefined, false, true)); + expect(start.code).toBe("missing_required_argument"); + expect(start.text).toContain("pm claim --next --start"); + expect(start.text).toContain("pm claim --start"); + }); + + it("never tells a caller who passed --next to pass it again", () => { + for (const start of [false, true]) { + const refusal = refusalGuidance(() => requireClaimTarget("pm-a", true, start)); + expect(refusal.code).toBe("ambiguous_option_combination"); + expect(refusal.text).not.toMatch(/pass --next|add --next/u); + expect(refusal.text).toContain(`pm claim pm-a${start ? " --start" : ""}`); + } + }); +}); + +describe("GH-1643 applyClaimStart", () => { + it("is a no-op without a start status and refuses terminal items", async () => { + await withTempPmPath(async (context) => { + const settings = await readSettings(context.pmPath); + const registry = resolveRuntimeStatusRegistry(settings.schema); + const open = { id: "pm-a", type: "Task", status: "open" } as ItemMetadata; + expect(applyClaimStart(open, false, settings, registry)).toEqual({ changedFields: [], warnings: [] }); + expect(open.status).toBe("open"); + expect(() => + applyClaimStart({ id: "pm-b", type: "Task", status: "closed" } as ItemMetadata, true, settings, registry), + ).toThrow(expect.objectContaining({ exitCode: EXIT_CODE.CONFLICT, context: expect.objectContaining({ code: "terminal_state_conflict" }) })); + const running = { id: "pm-c", type: "Task", status: "in_progress" } as ItemMetadata; + expect(applyClaimStart(running, true, settings, registry)).toEqual({ + start: { from_status: "in_progress", to_status: "in_progress" }, + changedFields: [], + warnings: [], + }); + expect(applyClaimStart(open, true, settings, registry)).toMatchObject({ + start: { from_status: "open", to_status: "in_progress" }, + changedFields: ["status"], + }); + expect(open.status).toBe("in_progress"); + }); + }); + + it("enforces configured type workflows with a warning or a refusal", async () => { + await withTempPmPath(async (context) => { + const settings = await readSettings(context.pmPath); + const registry = resolveRuntimeStatusRegistry(settings.schema); + settings.schema = { ...settings.schema, type_workflows: [{ type: "Task", allowed_transitions: [["open", "closed"]] }] }; + settings.governance = { ...settings.governance, workflow_enforcement: "warn" }; + const warned = applyClaimStart({ id: "pm-a", type: "Task", status: "open" } as ItemMetadata, true, settings, registry); + expect(warned.warnings).toHaveLength(1); + expect(warned.changedFields).toEqual(["status"]); + settings.governance = { ...settings.governance, workflow_enforcement: "strict" }; + expect(() => + applyClaimStart({ id: "pm-a", type: "Task", status: "open" } as ItemMetadata, true, settings, registry), + ).toThrow("Disallowed transition"); + }); + }); +}); + +describe("GH-1643 claim --next --start", () => { + it("selects, claims and starts in one claim history event through the SDK", async () => { + await withTempPmPath(async (context) => { + const id = createTestItemId(context, { title: "Next start", priority: "0" }); + const result = await runClaimNext(false, { path: context.pmPath }, { start: true }); + expect(result).toMatchObject({ + available: true, + item: { id, status: "in_progress", assignee: "test-author" }, + start: { from_status: "open", to_status: "in_progress" }, + }); + expect(result.changed_fields).toEqual(["assignee", "claim_principal", "status"]); + const history = context.runCli(["history", id, "--full", "--json"], { expectJson: true }).json as { + history: Array<{ op: string }>; + }; + expect(history.history.map((entry) => entry.op)).toEqual(["create", "claim"]); + expect(await runClaimNext(false, { path: context.pmPath }, { start: true, ifAvailable: true }, { assigneeFilter: "unassigned" })).toMatchObject({ + available: false, + item: null, + }); + }); + }); + + it("starts through the CLI, MCP and the typed client, and refuses a start without a target", async () => { + await withTempPmPath(async (context) => { + const first = createTestItemId(context, { title: "CLI start", priority: "0" }); + const cli = await context.runCliInProcess(["claim", "--next", "--start", "--json"]); + expect(cli.code, cli.stderr).toBe(0); + expect(JSON.parse(cli.stdout)).toMatchObject({ item: { id: first, status: "in_progress" }, available: true }); + + const second = createTestItemId(context, { title: "MCP start", priority: "0" }); + const common = { path: context.pmPath, noExtensions: true, author: "mcp-author" }; + expect(await runAction({ ...common, action: "claim", next: true, start: true })).toMatchObject({ id: second, status: "in_progress" }); + + const third = createTestItemId(context, { title: "Client start", priority: "0" }); + const client = new PmClient({ pmRoot: context.pmPath, noExtensions: true, author: "client-author" }); + expect(await client.claimNext({ start: true })).toMatchObject({ item: { id: third, status: "in_progress" }, start: { to_status: "in_progress" } }); + + const refused = await context.runCliInProcess(["claim", "--start", "--json"]); + expect(refused.code).toBe(EXIT_CODE.USAGE); + expect(refused.stderr).toContain("missing_required_argument"); + expect(refused.stderr).toContain("pm claim --next --start"); + await expect(runAction({ ...common, action: "claim", start: true })).rejects.toMatchObject({ context: { code: "missing_required_argument" } }); + }); + }); +}); diff --git a/tests/unit/sdk/recovery/recovery-guidance.spec.ts b/tests/unit/sdk/recovery/recovery-guidance.spec.ts index 166e208ade..b0b3c87913 100644 --- a/tests/unit/sdk/recovery/recovery-guidance.spec.ts +++ b/tests/unit/sdk/recovery/recovery-guidance.spec.ts @@ -28,7 +28,7 @@ describe("executable recovery guidance", () => { expect(await resolveProjectTestCommand(projectRoot)).toBe("pnpm test"); const guidance = agentGuidance.buildAgentGuidanceBlock("\n", "pnpm test"); expect(guidance).toContain('pm test --add command="pnpm test"'); - const contextCommand = "pm context --limit 10 --for orient"; + const contextCommand = "pm context --for orient"; const mutationGuard = "before item mutation"; const inProgressCommand = "pm list --status in_progress --limit 20"; expect(guidance).toContain(contextCommand); From 3e66b2330ab5af494ac5d8a56ab255adefe0c89c Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 1 Nov 2026 00:18:14 +0100 Subject: [PATCH 2/6] =?UTF-8?q?fix(agent-ux):=20address=20review=20?= =?UTF-8?q?=E2=80=94=20start=20into=20configured=20in-progress=20status,?= =?UTF-8?q?=20keep=20compact=20start=20receipt,=20flag-fragment=20restore?= =?UTF-8?q?=5Fwith?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review round 1 on #1645: - resolveStartTaskInProgressStatus ignored a configured workflow.in_progress_status, so a workspace whose in-progress status is "working" started items into its open status. That bug predates this PR and affected both explicit and ranked starts. The helper now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup that custom alias workflows rely on. - The compact mutation receipt now keeps a plain-object `start`, so default CLI JSON and MCP claim responses show start { from_status, to_status }. - Semantic attribution after a committed claim is best-effort. A failure is reported as the claim_semantic_attribution_unrecorded warning instead of an error for ownership that already persisted. - limit_override.restore_with is now the flag fragment "--token-budget N" to add to the original command, following the omission receipt's restore_with convention. A rebuilt full command dropped named options and inline search field:value filters. Both restore fields are omitted when no safe-integer budget can carry the request. - The brief list projection destructures filters before compaction, so a limit-only or empty-runtime filters block no longer survives. - Docs state that pm_search's query/keywords operand stays top-level. - A test pins that MCP claim {id, next, start} is refused with invalid_argument_value. Starting an item that has an open blocker matches update semantics and is filed separately as pm-urzubi. --- .agents/pm/history/pm-gh1642.jsonl | 1 + .agents/pm/history/pm-gh1643.jsonl | 4 +++ .agents/pm/history/pm-gh1644.jsonl | 1 + .agents/pm/history/pm-hke9z7.jsonl | 1 + .agents/pm/history/pm-urzubi.jsonl | 1 + .agents/pm/issues/pm-gh1642.toon | 5 ++-- .agents/pm/issues/pm-gh1643.toon | 10 +++++-- .agents/pm/issues/pm-gh1644.toon | 5 ++-- .agents/pm/issues/pm-hke9z7.toon | 5 ++-- .agents/pm/issues/pm-urzubi.toon | 20 +++++++++++++ docs/SDK_ACTION_CONFORMANCE.md | 8 +++-- sdk/public-surface.json | 12 ++++++-- src/core/output/mutation-projection.ts | 2 +- src/sdk/context-intent-contracts.ts | 20 ++++++++----- src/sdk/lifecycle/claim.ts | 29 +++++++++++++++---- src/sdk/output/list-envelope.ts | 3 +- src/sdk/start-task-status.ts | 5 +++- tests/unit/cli/compact-agent-output.spec.ts | 4 +++ tests/unit/commands/register-commands.spec.ts | 18 ++++++++++++ .../sdk/context/intent-limit-override.spec.ts | 18 +++++++----- .../sdk/lifecycle/claim-attribution.spec.ts | 28 ++++++++++++++++++ tests/unit/sdk/lifecycle/claim-start.spec.ts | 4 +++ 22 files changed, 167 insertions(+), 37 deletions(-) create mode 100644 .agents/pm/history/pm-urzubi.jsonl create mode 100644 .agents/pm/issues/pm-urzubi.toon create mode 100644 tests/unit/sdk/lifecycle/claim-attribution.spec.ts diff --git a/.agents/pm/history/pm-gh1642.jsonl b/.agents/pm/history/pm-gh1642.jsonl index 9e2fd1e4de..102d967a54 100644 --- a/.agents/pm/history/pm-gh1642.jsonl +++ b/.agents/pm/history/pm-gh1642.jsonl @@ -15,3 +15,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:30:07.457Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:30:07.457Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz55qw-51axji","kind":"test","status":"passed","started_at":"2026-10-31T22:29:59.138Z","finished_at":"2026-10-31T22:30:07.448Z","recorded_at":"2026-10-31T22:30:07.448Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"cbb323f9d890c82623536a8597ceb7fa473484f6e2b93cf749cb80429b7ea805","after_hash":"6781ea340c88a027084c46ef83d087576d93f0e736738fb5d7ed91a9c2f261c8","item_hash_version":3,"message":"Track test run summary (test-local-mvwz55qw-51axji)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"1a1c314e8bc2fa8ef846fb5f4d4c0991e3f515d3e946fce3d2e4e56e73d58de4"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:34.358Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:34.358Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:34.335Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:34.335Z"},{"op":"add","path":"/metadata/resolution","value":"projectPmBriefListEnvelope now recognizes the default ascending sort even with effective_keys. It replaces the legacy sort/order echo with one effective_keys comparator line, keeping the GH-1507 disclosure, and drops a filters.limit equal to applied_limit and empty runtime_filters. The new public SDK projectPmBriefSearchEnvelope drops truncated when it repeats has_more and limit-only filters, and replaces the default projection_metadata omission receipt with details --full. formatBuiltInOutput chains both for TOON only. completeness unchecked and items_encoding stay by contract."},{"op":"add","path":"/metadata/actual_result","value":"Same tracker, published 2026.10.31 vs this branch: pm search 'release workflow' --limit 3 drops from 1,033 to 863 bytes and pm list --status open --limit 3 from 988 to 782 bytes. compact-agent-output spec 59/59 and default-list-order integration pass; the GH-1642 cases fail with the projection stashed. SDK surface update is additive for the new export."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: brief pm list and pm search TOON pages drop redundant envelope fields"}],"before_hash":"6781ea340c88a027084c46ef83d087576d93f0e736738fb5d7ed91a9c2f261c8","after_hash":"fd4bb7a8925c3606f9b7aaeb1f92daf781b9d23c5cd8f9cfb4cdba139f2d4951","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:34.335Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"597f8ffd7c8592bed9985297e478b5365ed80bbb1146b94e9bf7af7c7451a5b1"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:37.050Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-hke9z7","release:pm-gh1644"]},"topic":{"value":"workset:pm-gh1642+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-hke9z7","release:pm-gh1644"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:37.050Z"}],"before_hash":"fd4bb7a8925c3606f9b7aaeb1f92daf781b9d23c5cd8f9cfb4cdba139f2d4951","after_hash":"627e202bef981e72032fa181cd400f4ac75740bb8390ad552e041a5c58b73155","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0b8c11ade15bb2e05fbe4d4a77091f4f64e2bb07b3c075c20625c2b775f3e86e"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:02:11.868Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-31T23:02:11.868Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: the list projection left the raw filters key in place when the compacted filters became empty, so a limit-only or empty-runtime filters block still printed. filters is now destructured out of the envelope before compaction, and specs cover {limit}, {runtime_filters:{}} and a non-echo limit."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:11.868Z"}],"before_hash":"627e202bef981e72032fa181cd400f4ac75740bb8390ad552e041a5c58b73155","after_hash":"c6637b7d0558a5cf113b4292278bda45c83cd1ca4be8454671ecca0244734187","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3e6260740bf4b73e6414dcc7e78b88eadca4aec190d00bb5f0bcea7087b6cba2"} diff --git a/.agents/pm/history/pm-gh1643.jsonl b/.agents/pm/history/pm-gh1643.jsonl index c9e98ef107..d78c40fd16 100644 --- a/.agents/pm/history/pm-gh1643.jsonl +++ b/.agents/pm/history/pm-gh1643.jsonl @@ -29,3 +29,7 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:29:48.579Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:29:48.579Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz4r6h-yrwyti","kind":"test","status":"passed","started_at":"2026-10-31T22:29:33.718Z","finished_at":"2026-10-31T22:29:48.568Z","recorded_at":"2026-10-31T22:29:48.568Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"1824dd491107af5c58293cbaaaa4a390933bf593a4b0571963e5906cdbbc9f7c","after_hash":"ee2a77cf030267de2614814bd70d47e2d6083e14e1bd91128cd11a92b1baba9e","item_hash_version":3,"message":"Track test run summary (test-local-mvwz4r6h-yrwyti)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"bf54e63809726f36ae5f0f0bb3c160f892857542190e9e6df36fadfd983907f0"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:19.316Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"replace","path":"/metadata/expected_result","value":"pm claim --next --start claims and starts the next available item in one write on every transport, and no claim-target refusal recommends a flag the caller supplied."},{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:19.316Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:19.294Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:19.294Z"},{"op":"add","path":"/metadata/resolution","value":"runClaim accepts start and applyClaimStart moves the claimed item to the configured in-progress status inside the same mutateItem callback. That gives one claim history event, type-workflow enforcement, terminal_state_conflict for terminal items, and a start {from_status,to_status} receipt. runClaimNext forwards start through the race-loss walk. The CLI runClaimAction and MCP runMcpClaimAction route an id-less start to that path, while an explicit-id start keeps the claim-then-update composition and its typed refusal. requireClaimTarget now lives in the SDK claim module with missing_required_argument and ambiguous_option_combination codes and never names a flag the caller already passed. PmClient.claimNext accepts start. The MCP schema drops the start+next exclusion, and start+ifAvailable is exclusive only with an id."},{"op":"add","path":"/metadata/actual_result","value":"Sandbox: pm claim --next --start returns status in_progress with changed_field_count 3 and history create then claim. MCP claim {next,start} and PmClient.claimNext({start:true}) return in_progress. pm claim --start refuses with missing_required_argument, suggesting pm claim --next --start. claim-start spec 7/7, register-commands 91/91, and lifecycle integration specs pass. With the routing stashed, the CLI/MCP start test fails. Full suite 11,748 passed."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: claim --next --start is one locked select-claim-start write on CLI, MCP and SDK"}],"before_hash":"ee2a77cf030267de2614814bd70d47e2d6083e14e1bd91128cd11a92b1baba9e","after_hash":"065140ff634b467fa999693a1a3660e132d5b6c8c791d0325d527b35dec738d2","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:19.294Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2ebf8ce0a74563221e3b28982ca6a676366966aa20c3ba6bbef32ae94300bf01"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:35.818Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:35.818Z"}],"before_hash":"065140ff634b467fa999693a1a3660e132d5b6c8c791d0325d527b35dec738d2","after_hash":"af534f93266d1c1d470f4b168a59227bbe92e6f2f8b18be86fe6825a59f884f6","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0be7ee2c239d055d56b5abd635e86d77b85daeee7e6113038ee48ea8a827d8df"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:01:50.658Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/15/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/14/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/13/path","value":"src/sdk/start-task-status.ts"},{"op":"add","path":"/metadata/files/16","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:01:50.658Z"}],"before_hash":"af534f93266d1c1d470f4b168a59227bbe92e6f2f8b18be86fe6825a59f884f6","after_hash":"91af23eb1db7178f50bcd3b4f83ac880e13bca8487da36ff1acaba3cb84cd938","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"250f127deeac0cf86fd545fff659b622f0577cb3afed7a1caa0c7ad015d9acea"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:01:52.139Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/16/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/15/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/14/path","value":"src/sdk/start-task-status.ts"},{"op":"replace","path":"/metadata/files/13/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/12/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/11/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/core.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/core/output/mutation-projection.ts"},{"op":"add","path":"/metadata/files/17","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:01:52.139Z"}],"before_hash":"91af23eb1db7178f50bcd3b4f83ac880e13bca8487da36ff1acaba3cb84cd938","after_hash":"ff6ddda29e874e14c7003434d097a35b38a7f90e28e10c266551d707ae23fd6b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"596d76738a0193e498f11b1c72b4a6ad446e8a42bef7a245990fafbecd0184bc"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:01:53.896Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/17/path","value":"tests/unit/sdk/lifecycle/claim-attribution.spec.ts"},{"op":"add","path":"/metadata/files/18","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:01:53.896Z"}],"before_hash":"ff6ddda29e874e14c7003434d097a35b38a7f90e28e10c266551d707ae23fd6b","after_hash":"fc532b444810eb74add7b894d39c62e4fb2149d657b131cc89466ae45c6925c5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"027024aea2395e19460ec3d8126b49f2ab798c52e8b8369aa67e7bff7b73f72b"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:02:07.901Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:07.900Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:07.901Z"}],"before_hash":"fc532b444810eb74add7b894d39c62e4fb2149d657b131cc89466ae45c6925c5","after_hash":"49047307204c49ea959e67b704a2ae82efca32f73dc3168b1327a0dd6bc7f1b8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"fe9b990be6bff337cc1cbf1c4affc0d4f2985f124a22cc1ded44c7b16c34c670"} diff --git a/.agents/pm/history/pm-gh1644.jsonl b/.agents/pm/history/pm-gh1644.jsonl index f8f1ef7367..30e60d3153 100644 --- a/.agents/pm/history/pm-gh1644.jsonl +++ b/.agents/pm/history/pm-gh1644.jsonl @@ -23,3 +23,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:29:58.500Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:29:58.500Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz4yu4-gbikjl","kind":"test","status":"passed","started_at":"2026-10-31T22:29:49.127Z","finished_at":"2026-10-31T22:29:58.492Z","recorded_at":"2026-10-31T22:29:58.492Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"ad12283fcfc2badd6bc167dbc0c87863824761b35f8cc6bd6b5b2d0839e80f82","after_hash":"acf2c6ba2acf7f46243970de22e6f6afad9c32684ccd47f71572c9f2cc1827bf","item_hash_version":3,"message":"Track test run summary (test-local-mvwz4yu4-gbikjl)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"d0d19b26efcb4247093f738661ecb798197d3e52e231011d2e2c443265f7a238"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:33.712Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"replace","path":"/metadata/expected_result","value":"Every row-bounded --for intent either honours an explicit --limit or reports a typed override with the requested and effective values and a retry that keeps the limit; pm's quickstart recommends no silently overridden flag."},{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:33.712Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:33.693Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:33.693Z"},{"op":"add","path":"/metadata/resolution","value":"The three row-bounded intent appliers share applyIntentRowLimit, which keeps the budget-derived ceiling and records a reduced request. attachContextIntentReceipt writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning limit_overridden_by_intent:--limit N->M for context, list and search intents, reading either projected or raw host options. The quickstart generator, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and both replayed agent-task transcripts use pm context --for orient. The quickstart no longer calls explicit --start atomic."},{"op":"add","path":"/metadata/actual_result","value":"Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning, and restore_with pm context --for orient --limit 10 --token-budget 7200. That retry returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100. The agent-task token baseline changed only its transcript digest (identical output). intent-limit-override spec 3/3 passes; 3/3 fail with the fix stashed."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: an intent budget that reduces an explicit --limit now discloses the override, and the quickstart recommends the measured canonical orientation"}],"before_hash":"acf2c6ba2acf7f46243970de22e6f6afad9c32684ccd47f71572c9f2cc1827bf","after_hash":"7912de92c4e92670b8de69abe871b765f67f62c3c906b3d5a9a960911409874c","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:33.693Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"96adbb1f390a825d3f60f3490486770f7b6da938c34fa546b500b73bb56f442d"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:36.414Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1644","claim:pm-hke9z7","release:pm-gh1643"]},"topic":{"value":"workset:pm-gh1642+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1644","claim:pm-hke9z7","release:pm-gh1643"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:36.414Z"}],"before_hash":"7912de92c4e92670b8de69abe871b765f67f62c3c906b3d5a9a960911409874c","after_hash":"3af5415b0896757a4f376196cef6875357d653157167a31a9fbbaabdb141632a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"83318603431cc4b37507ee923d6e4821fdc6597bf7dfddb8375bb90bb923e712"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:02:09.740Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:09.739Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: limit_override.restore_with is now the flag fragment '--token-budget N' to add to (or replace in) the original command, following the omission-receipt convention (restore_with '--full'). The full rebuilt command had dropped named options (--status, --sort) and inline search field:value filters. token_budget_for_requested and restore_with are omitted when the sufficient budget is not a safe integer (spec covers Number.MAX_SAFE_INTEGER)."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:09.740Z"}],"before_hash":"3af5415b0896757a4f376196cef6875357d653157167a31a9fbbaabdb141632a","after_hash":"174e3ee96abb589fe5e2d2ba32fbf8f2c4987bd116f31d50c36cea610ca35e20","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"75cabacce552d87e270cccc683daef73b5c44be0793b01c889f984100aefd4e7"} diff --git a/.agents/pm/history/pm-hke9z7.jsonl b/.agents/pm/history/pm-hke9z7.jsonl index 4ccba6d407..cded2252d0 100644 --- a/.agents/pm/history/pm-hke9z7.jsonl +++ b/.agents/pm/history/pm-hke9z7.jsonl @@ -11,3 +11,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:30:21.461Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:30:21.461Z"},{"op":"add","path":"/metadata/test_runs","value":[{"run_id":"test-local-mvwz5gjx-0ayygj","kind":"test","status":"passed","started_at":"2026-10-31T22:30:08.071Z","finished_at":"2026-10-31T22:30:21.453Z","recorded_at":"2026-10-31T22:30:21.453Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}]}],"before_hash":"c0e7cd115e8e46bb44abd346bd43913bfff90ccb617e12d26232137c90064179","after_hash":"a2afdffa5b525c61c291a77a756911d0d9ee332f9482821ce48758019960b245","item_hash_version":3,"message":"Track test run summary (test-local-mvwz5gjx-0ayygj)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"3c2b5252ab4053b440645359e17d2f10ed09f55ea105c3dfdd5131ba2c42d191"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:35.103Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:35.103Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:35.081Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:35.081Z"},{"op":"add","path":"/metadata/resolution","value":"runtime-input.ts derives flat-to-options hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional, excluding the query/keywords operands. One hoisted-key list replaces three loops, and nested options still win. runMcpSearchAction reads query, then the declared keywords alias, then refuses with the unchanged missing-query error."},{"op":"add","path":"/metadata/actual_result","value":"Through the MCP tools/call handler, flat and nested now agree for pm_context depth/for/limit, for on pm_list/pm_search/pm_next/pm_get, pm_get depth and pm_next limit. The flat orient call also discloses the limit override. read-tool-flat-parameters spec 7/7 iterates every declared optional key; 6/7 fail with the fix stashed. mcp-handshake hoist expectations pass."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: MCP read tools read every declared flat parameter and pm_search honours its keywords alias"}],"before_hash":"a2afdffa5b525c61c291a77a756911d0d9ee332f9482821ce48758019960b245","after_hash":"50e1db555085a5bc7e76ea6f1038f330ebd57ee081ce3feab59f8912fce21ceb","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:35.081Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"75aaf770cabd5ed75a190c93e47c30786c7109187d49f4b36d24e1b3e41bb080"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:37.663Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-hke9z7","release:pm-gh1642"]},"topic":{"value":"pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-hke9z7","release:pm-gh1642"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:37.663Z"}],"before_hash":"50e1db555085a5bc7e76ea6f1038f330ebd57ee081ce3feab59f8912fce21ceb","after_hash":"b979ea17c98bcfc1cd9490f8b018fc4b0abeb1060a77e54936c2260063147d1c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"7423ce52a1be6976c564295433427ba17f6a5e653660c3aab8d616db459bc238"} +{"hash_algorithm":"sha256","ts":"2026-10-31T23:02:13.787Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:13.786Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: the docs now state that search's query operand (and its keywords alias) stays top-level like pm_get's id, while every other declared optional parameter is accepted flat or under options."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:13.787Z"}],"before_hash":"b979ea17c98bcfc1cd9490f8b018fc4b0abeb1060a77e54936c2260063147d1c","after_hash":"5f4ffe016bb41b6c5eaf69106d572ce1bfd5f0b959961221758bfaa62fdc67e5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c66a8a024cdb7c969208ec1a9fda1024d8121fdcc6b59532c04f8dc9f34c539d"} diff --git a/.agents/pm/history/pm-urzubi.jsonl b/.agents/pm/history/pm-urzubi.jsonl new file mode 100644 index 0000000000..d28aa52fc9 --- /dev/null +++ b/.agents/pm/history/pm-urzubi.jsonl @@ -0,0 +1 @@ +{"hash_algorithm":"sha256","ts":"2026-10-31T23:01:47.892Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-urzubi"},{"op":"add","path":"/metadata/title","value":"Starting work ignores open blockers: claim --start and update --status in_progress move a blocked item into progress without a refusal or warning"},{"op":"add","path":"/metadata/description","value":"Raised by CodeRabbit on PR #1645 and verified on 2026.10.31 source in a fresh sandbox. An item created with --blocked-by is stored as status blocked, and pm update --status in_progress then succeeds with no refusal or warning. pm claim --start runs the same claim-then-update composition, so it behaves the same. pm validate later reports non_blocked_status_has_blocked_by for the in-progress item whose blocker is still open. pm next and claim --next --start are unaffected because ready selection already excludes items with open blockers. Starting an explicitly blocked item is a governance decision, not a claim defect, so changing only --start would make start and update disagree.\n\nDuplicate search: 'start blocked item open blocker in_progress refuse', 'non_blocked_status_has_blocked_by' — no open item. Nearest is the dependency-aware actionability decision (closed)."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":3},{"op":"add","path":"/metadata/tags","value":["agent-ux","dependencies","governance","lifecycle"]},{"op":"add","path":"/metadata/created_at","value":"2026-10-31T23:01:47.892Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-10-31T23:01:47.892Z"},{"op":"add","path":"/metadata/author","value":"harness:claude-code"},{"op":"add","path":"/metadata/estimated_minutes","value":150},{"op":"add","path":"/metadata/acceptance_criteria","value":"Moving an item with an open blocked_by target into the in-progress status, through update, claim --start or start-task, either refuses with a typed code naming the open blocker and the unblock or --force recovery, or warns, as governed by a declared setting; the CLI, MCP and SDK paths share one check that runs before any write, so a refused start never leaves a claim; a terminal blocker does not block; tests cover refuse, warn and terminal-blocker cases"},{"op":"add","path":"/metadata/parent","value":"pm-33cw"},{"op":"add","path":"/metadata/risk","value":"medium"},{"op":"add","path":"/metadata/confidence","value":"medium"},{"op":"add","path":"/metadata/expected_result","value":"No transport can silently move an item with an open blocker into progress; the behaviour is declared by governance settings and consistent across update and start."},{"op":"add","path":"/metadata/dependencies","value":[{"id":"pm-9x6k","kind":"related","created_at":"2026-10-31T23:01:47.892Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"},{"id":"pm-gh1643","kind":"discovered_from","created_at":"2026-10-31T23:01:47.892Z","author":"harness:claude-code","source_kind":"cli:create:dep","author_source":"detected"}]}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"acf3efb6d2679c95d815ce0d1449b31ba8de0d5f3998cac2b66018e7a6eeb34c","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"78e459c1ad3a4c39cb8e09d4363ef82c38c919943c4ca264df8a64ac3c63d341"} diff --git a/.agents/pm/issues/pm-gh1642.toon b/.agents/pm/issues/pm-gh1642.toon index 5f944ea48e..49405e30de 100644 --- a/.agents/pm/issues/pm-gh1642.toon +++ b/.agents/pm/issues/pm-gh1642.toon @@ -6,7 +6,7 @@ status: closed priority: 2 tags[4]: agent-ux,github,output,token-efficiency created_at: "2026-10-31T21:14:02.437Z" -updated_at: "2026-10-31T22:31:37.050Z" +updated_at: "2026-10-31T23:02:11.868Z" closed_at: "2026-10-31T22:31:34.335Z" completed_at: "2026-10-31T22:31:34.335Z" author: "harness:claude-code" @@ -22,9 +22,10 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-6pycus,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected pm-gh1627,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected pm-zd46m7,related,"2026-10-31T21:49:42.569Z","harness:claude-code","cli:update:dep",detected -comments[2]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-10-31T21:47:00.908Z","harness:claude-code","Implementation and measurement: projectPmBriefListEnvelope now drops the default ascending sort when it carries effective_keys (the regression), and drops a filters.limit equal to applied_limit and empty runtime_filters. New public projectPmBriefSearchEnvelope (sdk/output) drops truncated when it equals has_more and limit-only filters, and replaces the single default projection_metadata omission receipt with details --full. It is chained after the list projection in formatBuiltInOutput (TOON only). Measured with the published 2026.10.31 package against this branch on this tracker: pm search 'release workflow' --limit 3 drops from 1,033 to 863 B (-16%) and pm list --status open --limit 3 from 988 to 782 B (-21%). completeness unchecked and items_encoding remain by design (see description)." "2026-10-31T21:49:41.855Z","harness:claude-code","Design correction while testing: the 7-line default sort block is not only redundant metadata. GH-1507's accepted contract (pm-zd46m7, integration spec default-list-order) requires brief TOON to disclose the effective default comparator order so an agent knows why rows arrive in that order. Dropping it would undo that decision. The projection therefore drops only the legacy sort/order echo and renders the effective keys on one line, for example sorting.effective_keys 'status_registry:nonterminal_before_terminal asc, priority asc, updated_at desc, id asc'. Seven lines become two, and JSON keeps the full pair array. A malformed or extended sorting block passes through unchanged." + "2026-10-31T23:02:11.868Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: the list projection left the raw filters key in place when the compacted filters became empty, so a limit-only or empty-runtime filters block still printed. filters is now destructured out of the envelope before compaction, and specs cover {limit}, {runtime_filters:{}} and a non-echo limit." files[5]{path,scope}: sdk/public-surface.json,project src/core/output/output.ts,project diff --git a/.agents/pm/issues/pm-gh1643.toon b/.agents/pm/issues/pm-gh1643.toon index 8516a9debb..55f87dc0b5 100644 --- a/.agents/pm/issues/pm-gh1643.toon +++ b/.agents/pm/issues/pm-gh1643.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: agent-ux,claim,github,refusal created_at: "2026-10-31T21:13:57.945Z" -updated_at: "2026-10-31T22:31:35.818Z" +updated_at: "2026-10-31T23:02:07.901Z" closed_at: "2026-10-31T22:31:19.294Z" completed_at: "2026-10-31T22:31:19.294Z" author: "harness:claude-code" @@ -24,12 +24,14 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-fjxm,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-gh1369,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-ixi1,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected -comments[1]{created_at,author,text}: +comments[2]{created_at,author,text}: "2026-10-31T21:46:58.955Z","harness:claude-code","Implementation: runClaim gained an optional start that calls applyClaimStart inside the same mutateItem callback (the release --handoff pattern). It resolves the in-progress status through resolveStartTaskInProgressStatus, refuses a terminal item with terminal_state_conflict even under --force, enforces type workflows via enforceTypeWorkflowTransition, and returns start {from_status,to_status}. runClaimNext forwards start to every candidate in the race-loss walk, so --if-available and filters behave as before. CLI runClaimAction and MCP runMcpClaimAction route start with an explicit id to the unchanged two-step composition, which keeps its typed 'Remove --next and --if-available' refusal; start without an id uses the atomic path or the new requireClaimTarget in src/sdk/lifecycle/claim-target.ts (missing_required_argument / ambiguous_option_combination, attributed to claim through the catalog generator's module map). runStartTask now strips start before its claim step so explicit --start stays claim-then-update. MCP schema: the start+next exclusion is gone and start+ifAvailable is exclusive only with an id. PmClient.claimNext({start:true}) is typed. Verified in a sandbox: claim --next --start → status in_progress, changed_field_count 3, history create→claim (one event)." -files[16]{path,scope}: + "2026-10-31T23:02:07.900Z","harness:claude-code","CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi." +files[19]{path,scope}: scripts/release/surface-replication-sets.json,project sdk/public-surface.json,project src/cli/register-operations.ts,project + src/core/output/mutation-projection.ts,project src/sdk/cli-contracts/tool-schema.ts,project src/sdk/core.ts,project src/sdk/generated/generated-error-code-catalog-part-1.ts,project @@ -40,8 +42,10 @@ files[16]{path,scope}: src/sdk/lifecycle/task-composition.ts,project src/sdk/runtime-public-contracts.ts,project src/sdk/runtime.ts,project + src/sdk/start-task-status.ts,project tests/fixtures/contracts/full.json,project tests/unit/commands/register-commands.spec.ts,project + tests/unit/sdk/lifecycle/claim-attribution.spec.ts,project tests/unit/sdk/lifecycle/claim-start.spec.ts,project tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,project,"harness:claude-code","2026-10-31T21:46:33.577Z",local_mutation,fix/agent-ux-orient-claim-envelopes diff --git a/.agents/pm/issues/pm-gh1644.toon b/.agents/pm/issues/pm-gh1644.toon index d1489fd68f..e122b1ca1b 100644 --- a/.agents/pm/issues/pm-gh1644.toon +++ b/.agents/pm/issues/pm-gh1644.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: agent-ux,context,github,token-efficiency created_at: "2026-10-31T21:13:36.728Z" -updated_at: "2026-10-31T22:31:36.414Z" +updated_at: "2026-10-31T23:02:09.740Z" closed_at: "2026-10-31T22:31:33.693Z" completed_at: "2026-10-31T22:31:33.693Z" author: "harness:claude-code" @@ -22,8 +22,9 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-3crymx,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected pm-gy885b,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected pm-ka6d,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected -comments[1]{created_at,author,text}: +comments[2]{created_at,author,text}: "2026-10-31T21:46:59.575Z","harness:claude-code","Implementation: the three row-bounded intent appliers share applyIntentRowLimit, which keeps the existing min(requested, budget-derived) clamp but stashes a reduced request as intentRequestedLimit. attachContextIntentReceipt calls discloseIntentLimitOverride, which reads that key or the raw host --limit and writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning 'limit_overridden_by_intent:--limit N->M'. token_budget_for_requested = max(current budget, overhead + rowCost*requested) from the shared resolveIntentRowCost table. Measured on this tracker: pm context --for orient --limit 10 now prints the override with restore_with 'pm context --for orient --limit 10 --token-budget 7200'. That retry returns 10 rows at an estimated 4,637 tokens within budget. list --for triage --limit 150 → effective 100, retry with --token-budget 3200 honours 150. The quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, pm-user ECOSYSTEM_PLANNING) and the replayed agent-task transcript now use the measured canonical pm context --for orient." + "2026-10-31T23:02:09.739Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: limit_override.restore_with is now the flag fragment '--token-budget N' to add to (or replace in) the original command, following the omission-receipt convention (restore_with '--full'). The full rebuilt command had dropped named options (--status, --sort) and inline search field:value filters. token_budget_for_requested and restore_with are omitted when the sufficient budget is not a safe integer (spec covers Number.MAX_SAFE_INTEGER)." files[10]{path,scope}: docs/agent-task-token-baseline.json,project docs/agent-task-transcripts.json,project diff --git a/.agents/pm/issues/pm-hke9z7.toon b/.agents/pm/issues/pm-hke9z7.toon index c10ce7742a..307dfcc225 100644 --- a/.agents/pm/issues/pm-hke9z7.toon +++ b/.agents/pm/issues/pm-hke9z7.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: agent-ux,context,contracts,mcp created_at: "2026-10-31T21:27:47.547Z" -updated_at: "2026-10-31T22:31:37.663Z" +updated_at: "2026-10-31T23:02:13.787Z" closed_at: "2026-10-31T22:31:35.081Z" completed_at: "2026-10-31T22:31:35.081Z" author: "harness:claude-code" @@ -22,8 +22,9 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-gh1644,discovered_from,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected pm-hno5,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected pm-wcaa,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected -comments[1]{created_at,author,text}: +comments[2]{created_at,author,text}: "2026-10-31T21:47:01.751Z","harness:claude-code","Implementation: runtime-input.ts derives hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional (declaredReadActionOptionKeys), after the hand table, so nested options still win. Verified through the MCP tools/call handler: flat and nested calls now agree for pm_context depth/for/limit, pm_list/pm_search/pm_next/pm_get for, pm_get depth and pm_next limit. The regression spec iterates every declared optional key of the five actions, so a new declared parameter can no longer be silently dropped." + "2026-10-31T23:02:13.786Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: the docs now state that search's query operand (and its keywords alias) stays top-level like pm_get's id, while every other declared optional parameter is accepted flat or under options." files[3]{path,scope}: src/sdk/runtime-input.ts,project src/sdk/runtime/actions.ts,project diff --git a/.agents/pm/issues/pm-urzubi.toon b/.agents/pm/issues/pm-urzubi.toon new file mode 100644 index 0000000000..2087dd6e6f --- /dev/null +++ b/.agents/pm/issues/pm-urzubi.toon @@ -0,0 +1,20 @@ +id: pm-urzubi +title: "Starting work ignores open blockers: claim --start and update --status in_progress move a blocked item into progress without a refusal or warning" +description: "Raised by CodeRabbit on PR #1645 and verified on 2026.10.31 source in a fresh sandbox. An item created with --blocked-by is stored as status blocked, and pm update --status in_progress then succeeds with no refusal or warning. pm claim --start runs the same claim-then-update composition, so it behaves the same. pm validate later reports non_blocked_status_has_blocked_by for the in-progress item whose blocker is still open. pm next and claim --next --start are unaffected because ready selection already excludes items with open blockers. Starting an explicitly blocked item is a governance decision, not a claim defect, so changing only --start would make start and update disagree.\n\nDuplicate search: 'start blocked item open blocker in_progress refuse', 'non_blocked_status_has_blocked_by' — no open item. Nearest is the dependency-aware actionability decision (closed)." +type: Issue +status: open +priority: 3 +tags[4]: agent-ux,dependencies,governance,lifecycle +created_at: "2026-10-31T23:01:47.892Z" +updated_at: "2026-10-31T23:01:47.892Z" +author: "harness:claude-code" +estimated_minutes: 150 +acceptance_criteria: "Moving an item with an open blocked_by target into the in-progress status, through update, claim --start or start-task, either refuses with a typed code naming the open blocker and the unblock or --force recovery, or warns, as governed by a declared setting; the CLI, MCP and SDK paths share one check that runs before any write, so a refused start never leaves a claim; a terminal blocker does not block; tests cover refuse, warn and terminal-blocker cases" +parent: pm-33cw +risk: medium +confidence: medium +expected_result: No transport can silently move an item with an open blocker into progress; the behaviour is declared by governance settings and consistent across update and start. +dependencies[2]{id,kind,created_at,author,source_kind,author_source}: + pm-9x6k,related,"2026-10-31T23:01:47.892Z","harness:claude-code","cli:create:dep",detected + pm-gh1643,discovered_from,"2026-10-31T23:01:47.892Z","harness:claude-code","cli:create:dep",detected +body: "" diff --git a/docs/SDK_ACTION_CONFORMANCE.md b/docs/SDK_ACTION_CONFORMANCE.md index a17f6733a5..e757926877 100644 --- a/docs/SDK_ACTION_CONFORMANCE.md +++ b/docs/SDK_ACTION_CONFORMANCE.md @@ -94,7 +94,9 @@ When the intent budget reduces an explicit `--limit`, as with `pm context --for orient --limit 10` (the 3000-token orient budget carries 3 rows), the reduction is never silent. `context_intent.limit_override` records `requested`, `effective`, the `token_budget_for_requested` that would carry the -request, and a `restore_with` retry. `filters.requested_limit` sits beside the +request, and `restore_with`: the flag to add to (or replace in) the original +command, so every original positional, inline filter, and option is kept. Both +fields are omitted when no safe-integer budget can carry the request. `filters.requested_limit` sits beside the effective `filters.limit`, and `warnings` carries `limit_overridden_by_intent:--limit ->`. The same applies to `context`, `list`, and `search` intents. @@ -104,7 +106,9 @@ accept every optional parameter their action contract declares either flat or under `options`. The runtime derives the flat-to-options mapping from `PM_TOOL_ACTION_PARAMETER_CONTRACTS`, so a declared argument such as `for`, `depth`, `limit`, or `tokenBudget` is never silently ignored; nested `options` -win when both are present. +win when both are present. Positional operands stay top-level like `pm_get`'s +`id`: `pm_search` reads `query` (or its declared alias `keywords`) only from the +top-level arguments. Default intent budgets retain the ordinary page cap. Explicit budgets may derive larger pages; recursive compaction and resumable cursor safeguards still diff --git a/sdk/public-surface.json b/sdk/public-surface.json index c3b3ebe829..38678bf2aa 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -2985,6 +2985,14 @@ ], "package_version": "2026.10.31", "reason": "Optional additions only: ClaimResult.start (claim --next --start receipt), ClaimNextOptions.start, and PmContextIntentReceipt.limit_override (GH-1643/GH-1644); no existing field changed type or became required." + }, + { + "changes": [ + "signature ./sdk:PmContextIntentLimitOverride", + "signature ./sdk/contracts:PmContextIntentLimitOverride" + ], + "package_version": "2026.10.31", + "reason": "PmContextIntentLimitOverride is new in this PR: token_budget_for_requested and restore_with become optional because they are omitted when no safe-integer budget can carry the request, and restore_with is now a flag fragment (review feedback)." } ], "entrypoints": { @@ -14467,7 +14475,7 @@ "classification": "contract_data", "kind": "interface", "name": "PmContextIntentLimitOverride", - "signature": "export interface PmContextIntentLimitOverride { flag: \"--limit\"; requested: number; effective: number; token_budget_for_requested: number; restore_with: string; }" + "signature": "export interface PmContextIntentLimitOverride { flag: \"--limit\"; requested: number; effective: number; token_budget_for_requested?: number; restore_with?: string; }" }, { "classification": "advanced_export", @@ -25375,7 +25383,7 @@ "classification": "contract_data", "kind": "interface", "name": "PmContextIntentLimitOverride", - "signature": "export interface PmContextIntentLimitOverride { flag: \"--limit\"; requested: number; effective: number; token_budget_for_requested: number; restore_with: string; }" + "signature": "export interface PmContextIntentLimitOverride { flag: \"--limit\"; requested: number; effective: number; token_budget_for_requested?: number; restore_with?: string; }" }, { "classification": "contract_data", diff --git a/src/core/output/mutation-projection.ts b/src/core/output/mutation-projection.ts index 5136ac0a11..3bb0ecc13e 100644 --- a/src/core/output/mutation-projection.ts +++ b/src/core/output/mutation-projection.ts @@ -224,7 +224,7 @@ function appendCompactMutationEvidence(result: Record, item: Re if (Array.isArray(result.warnings) && result.warnings.length > 0) compact.warnings = result.warnings; - for (const key of ["recovery", "follow_up", "handoff", "completion"]) { + for (const key of ["recovery", "follow_up", "handoff", "completion", "start"]) { if (isPlainObject(result[key])) compact[key] = result[key]; } if (typeof result.claimed_by === "string" || typeof result.released_by === "string") { diff --git a/src/sdk/context-intent-contracts.ts b/src/sdk/context-intent-contracts.ts index 5a4e7ff933..9757d7731a 100644 --- a/src/sdk/context-intent-contracts.ts +++ b/src/sdk/context-intent-contracts.ts @@ -98,10 +98,10 @@ export interface PmContextIntentLimitOverride { requested: number; /** Row limit the intent budget applied. */ effective: number; - /** Smallest token budget, never below the current one, whose derived ceiling carries the requested rows. */ - token_budget_for_requested: number; - /** Bounded retry that keeps the requested limit under a sufficient explicit budget. */ - restore_with: string; + /** Smallest token budget, never below the current one, whose derived ceiling carries the requested rows; absent when no safe integer budget can. */ + token_budget_for_requested?: number; + /** Flag to add to (or replace in) the original command so it keeps the requested limit, like an omission receipt's `restore_with`. */ + restore_with?: string; } /** Stable warning code emitted when an intent budget reduces an explicit row limit. */ @@ -932,7 +932,7 @@ export function attachContextIntentReceipt< : "The selected intent token budget constrains the effective row ceiling."; resultWithDiagnostics.budget_derived_limit = budgetDerivedLimit; discloseIntentLimitOverride( - { command, builtInCommand, intent: contract.intent }, + { builtInCommand, intent: contract.intent }, receipt, resultWithDiagnostics, options, @@ -1058,7 +1058,6 @@ function contextIntentRecoveryPositional( /** Record a reduced explicit row limit in the receipt, warnings and filters, with the budget that keeps it. */ function discloseIntentLimitOverride( identity: { - command: string; builtInCommand: BuiltInContextIntentCommand; intent: string; }, @@ -1081,12 +1080,17 @@ function discloseIntentLimitOverride( overhead + rowCost * requested, ); receipt.limit_reason = `The ${receipt.token_budget}-token ${identity.intent} budget carries ${effective} rows, so it reduced the caller's --limit ${requested}.`; + // A flag fragment keeps every original positional, inline filter and option; an unparseable budget is not offered. receipt.limit_override = { flag: "--limit", requested, effective, - token_budget_for_requested: sufficientBudget, - restore_with: `pm ${identity.command}${contextIntentRecoveryPositional(identity.builtInCommand, result)} --for ${identity.intent} --limit ${requested} --token-budget ${sufficientBudget}`, + ...(Number.isSafeInteger(sufficientBudget) + ? { + token_budget_for_requested: sufficientBudget, + restore_with: `--token-budget ${sufficientBudget}`, + } + : {}), }; const warning = `${CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING}:--limit ${requested}->${effective}`; result.warnings = Array.isArray(result.warnings) diff --git a/src/sdk/lifecycle/claim.ts b/src/sdk/lifecycle/claim.ts index a809d5de75..c6de5054cc 100644 --- a/src/sdk/lifecycle/claim.ts +++ b/src/sdk/lifecycle/claim.ts @@ -268,12 +268,14 @@ export async function runClaim( }); if (!skipped) { - await recordClaimSemanticAttribution({ - pmRoot, - settings, - principal: claimPrincipal, - itemId: result.item.id, - }); + mutationWarnings.push( + ...(await recordClaimAttributionAfterCommit({ + pmRoot, + settings, + principal: claimPrincipal, + itemId: result.item.id, + })), + ); } return { @@ -289,6 +291,21 @@ export async function runClaim( }; } +/** Stable warning when session attribution could not be recorded for a claim that already committed. */ +export const CLAIM_ATTRIBUTION_UNRECORDED_WARNING = "claim_semantic_attribution_unrecorded"; + +/** Record semantic attribution after the claim committed; a failure becomes a warning, never an error for persisted ownership. */ +async function recordClaimAttributionAfterCommit( + params: Parameters[0], +): Promise { + try { + await recordClaimSemanticAttribution(params); + return []; + } catch { + return [CLAIM_ATTRIBUTION_UNRECORDED_WARNING]; + } +} + /** Enforce the claim target XOR selection contract; refusals never ask for a flag the caller already passed. */ export function requireClaimTarget( id: string | undefined, diff --git a/src/sdk/output/list-envelope.ts b/src/sdk/output/list-envelope.ts index 750b61d6ca..1bd892589f 100644 --- a/src/sdk/output/list-envelope.ts +++ b/src/sdk/output/list-envelope.ts @@ -104,6 +104,7 @@ export function projectPmBriefListEnvelope(value: unknown): unknown { now: _now, omission_receipt: _omissions, sorting, + filters, ...rest } = value; const completed = @@ -120,7 +121,7 @@ export function projectPmBriefListEnvelope(value: unknown): unknown { ...(!completed ? { total, has_more: hasMore, completeness: source } : {}), ...(hasMore ? { next_cursor: nextCursor } : {}), ...projectDefaultSorting(sorting), - ...compactEchoedFilters(rest.filters, rest.applied_limit), + ...compactEchoedFilters(filters, rest.applied_limit), details: "--full", }; } diff --git a/src/sdk/start-task-status.ts b/src/sdk/start-task-status.ts index 98c508a802..c3cec21645 100644 --- a/src/sdk/start-task-status.ts +++ b/src/sdk/start-task-status.ts @@ -8,10 +8,13 @@ import { type RuntimeStatusRegistry, } from "../core/schema/runtime-schema.js"; -/** Resolve the status the `start-task` lifecycle alias should move an item to. Resolves `in_progress` strictly through the workspace registry so a custom workflow that omits in_progress falls back to its open status instead of setting a status the workflow does not define. */ +/** Resolve the status a start moves an item to. A configured in-progress status distinct from the open status wins (for example `workflow.in_progress_status: "working"`); otherwise `in_progress` resolves through the registry's ids and aliases, and a workflow without one falls back to its open status instead of inventing a status it does not define. */ export function resolveStartTaskInProgressStatus( statusRegistry: RuntimeStatusRegistry, ): string { + if (statusRegistry.in_progress_status !== statusRegistry.open_status) { + return statusRegistry.in_progress_status; + } return ( normalizeStatusInputWithRegistry("in_progress", statusRegistry) ?? statusRegistry.open_status diff --git a/tests/unit/cli/compact-agent-output.spec.ts b/tests/unit/cli/compact-agent-output.spec.ts index d5a82ddb8e..2847473c7c 100644 --- a/tests/unit/cli/compact-agent-output.spec.ts +++ b/tests/unit/cli/compact-agent-output.spec.ts @@ -461,6 +461,10 @@ describe("GH-1642 brief search and list envelopes", () => { ]) { expect(projectPmBriefListEnvelope({ ...value, sorting })).toMatchObject({ sorting }); } + for (const filters of [{ limit: "3" }, { runtime_filters: {} }, { limit: "3", runtime_filters: {} }]) { + expect(projectPmBriefListEnvelope({ ...value, filters })).not.toHaveProperty("filters"); + } + expect(projectPmBriefListEnvelope({ ...value, filters: { limit: "9" } })).toMatchObject({ filters: { limit: "9" } }); const text = formatBuiltInOutput(value, "toon"); expect(text).toContain('effective_keys: "priority asc, id asc"'); expect(text).not.toContain('sort: "default"'); diff --git a/tests/unit/commands/register-commands.spec.ts b/tests/unit/commands/register-commands.spec.ts index c013f77e9d..a777ecdcef 100644 --- a/tests/unit/commands/register-commands.spec.ts +++ b/tests/unit/commands/register-commands.spec.ts @@ -1454,6 +1454,24 @@ describe("operation command actions", () => { expect(resolveStartTaskInProgressStatus(registry)).toBe("open"); }); + it("starts into a configured custom in-progress status and still resolves in_progress aliases", () => { + const terminal = [ + { id: "done", roles: ["terminal", "terminal_done", "default_close"] }, + { id: "dropped", roles: ["terminal", "terminal_canceled", "default_cancel"] }, + ]; + const explicit = resolveRuntimeStatusRegistry({ + statuses: [{ id: "todo", roles: ["active", "default_open"] }, { id: "working", roles: ["active"] }, ...terminal], + workflow: { open_status: "todo", in_progress_status: "working", close_status: "done", canceled_status: "dropped" }, + } as never); + expect(resolveStartTaskInProgressStatus(explicit)).toBe("working"); + const aliased = resolveRuntimeStatusRegistry({ + statuses: [{ id: "queued", roles: ["active", "default_open"] }, { id: "building", aliases: ["in_progress"], roles: ["active"] }, ...terminal], + workflow: { open_status: "queued", close_status: "done", canceled_status: "dropped" }, + } as never); + expect(aliased.in_progress_status).toBe("queued"); + expect(resolveStartTaskInProgressStatus(aliased)).toBe("building"); + }); + it("maps stats, gc, and contracts options", async () => { await runCli("stats", "--storage"); expect( diff --git a/tests/unit/sdk/context/intent-limit-override.spec.ts b/tests/unit/sdk/context/intent-limit-override.spec.ts index b5adb22f9c..18d84b530f 100644 --- a/tests/unit/sdk/context/intent-limit-override.spec.ts +++ b/tests/unit/sdk/context/intent-limit-override.spec.ts @@ -35,7 +35,7 @@ describe("GH-1644 intent budgets never silently reduce an explicit --limit", () requested: 10, effective: 3, token_budget_for_requested: 7200, - restore_with: "pm context --for orient --limit 10 --token-budget 7200", + restore_with: "--token-budget 7200", }, }); expect(result).toMatchObject({ @@ -47,20 +47,24 @@ describe("GH-1644 intent budgets never silently reduce an explicit --limit", () expect(retry).not.toHaveProperty("intentRequestedLimit"); }); - it("reads the caller's limit from raw host options and quotes search positionals in the retry", () => { - const listed = attachContextIntentReceipt("list", { for: "triage", limit: "150" }, { items: [] }); + it("reads the caller's limit from raw host options and restores it with a flag the original command keeps", () => { + const listed = attachContextIntentReceipt("list", { for: "triage", limit: "150", status: "closed", sort: "updated" }, { items: [] }); expect(listed.context_intent?.limit_override).toMatchObject({ requested: 150, effective: 100, token_budget_for_requested: 3200, - restore_with: "pm list --for triage --limit 150 --token-budget 3200", + restore_with: "--token-budget 3200", }); expect(listed).toMatchObject({ warnings: [`${CONTEXT_INTENT_LIMIT_OVERRIDE_WARNING}:--limit 150->100`] }); expect(listed).not.toHaveProperty("filters"); const searched = attachContextIntentReceipt("search", { for: "discover", limit: "200" }, { query: "it's", items: [] }); - expect(searched.context_intent?.limit_override?.restore_with).toBe( - `pm search 'it'"'"'s' --for discover --limit 200 --token-budget 3680`, - ); + expect(searched.context_intent?.limit_override?.restore_with).toBe("--token-budget 3680"); + const unbounded = attachContextIntentReceipt("context", { for: "orient", limit: String(Number.MAX_SAFE_INTEGER) }, {}); + expect(unbounded.context_intent?.limit_override).toEqual({ + flag: "--limit", + requested: Number.MAX_SAFE_INTEGER, + effective: 3, + }); const honoured = attachContextIntentReceipt("context", { for: "orient", limit: "2" }, { filters: { limit: "2" } }); expect(honoured.context_intent).not.toHaveProperty("limit_override"); expect(honoured).not.toHaveProperty("warnings"); diff --git a/tests/unit/sdk/lifecycle/claim-attribution.spec.ts b/tests/unit/sdk/lifecycle/claim-attribution.spec.ts new file mode 100644 index 0000000000..ee86ba087e --- /dev/null +++ b/tests/unit/sdk/lifecycle/claim-attribution.spec.ts @@ -0,0 +1,28 @@ +import { describe, expect, it, vi } from "vitest"; + +vi.mock("../../../../src/sdk/context/semantic-session-attribution.js", async (importOriginal) => ({ + ...(await importOriginal()), + recordClaimSemanticAttribution: vi.fn().mockRejectedValue(new Error("session state lock busy")), +})); + +import { CLAIM_ATTRIBUTION_UNRECORDED_WARNING, runClaim, runClaimNext } from "../../../../src/sdk/lifecycle/claim.js"; +import { createTestItemId } from "../../../helpers/itemFactory.js"; +import { withTempPmPath } from "../../../helpers/withTempPmPath.js"; + +describe("claim attribution after commit", () => { + it("reports a committed claim and start with a warning when session attribution fails", async () => { + await withTempPmPath(async (context) => { + const explicit = createTestItemId(context, { title: "Explicit attribution", priority: "1" }); + expect(await runClaim(explicit, false, { path: context.pmPath })).toMatchObject({ + item: { id: explicit, assignee: "test-author" }, + warnings: [CLAIM_ATTRIBUTION_UNRECORDED_WARNING], + }); + const ranked = createTestItemId(context, { title: "Ranked attribution", priority: "0" }); + expect(await runClaimNext(false, { path: context.pmPath }, { start: true })).toMatchObject({ + item: { id: ranked, status: "in_progress" }, + start: { to_status: "in_progress" }, + warnings: [CLAIM_ATTRIBUTION_UNRECORDED_WARNING], + }); + }); + }); +}); diff --git a/tests/unit/sdk/lifecycle/claim-start.spec.ts b/tests/unit/sdk/lifecycle/claim-start.spec.ts index afb9f7c510..a38070054d 100644 --- a/tests/unit/sdk/lifecycle/claim-start.spec.ts +++ b/tests/unit/sdk/lifecycle/claim-start.spec.ts @@ -134,6 +134,10 @@ describe("GH-1643 claim --next --start", () => { expect(refused.stderr).toContain("missing_required_argument"); expect(refused.stderr).toContain("pm claim --next --start"); await expect(runAction({ ...common, action: "claim", start: true })).rejects.toMatchObject({ context: { code: "missing_required_argument" } }); + // An explicit id with next and start is refused by the explicit-start composition before any write. + await expect(runAction({ ...common, action: "claim", id: third, next: true, start: true })).rejects.toMatchObject({ + context: { code: "invalid_argument_value" }, + }); }); }); }); From 8351a7acaddd8ec611b2ca5129881ce34bbbcdc8 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 1 Nov 2026 02:43:46 +0100 Subject: [PATCH 3/6] =?UTF-8?q?fix(agent-ux):=20address=20review=20round?= =?UTF-8?q?=202=20=E2=80=94=20filtered=20context=20keeps=20dependency=20ed?= =?UTF-8?q?ges,=20terminal=20search=20compaction,=20nested=20option=20prec?= =?UTF-8?q?edence?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review round 2 on #1645: - pm context with a selection filter (e.g. --type Task) built its dependency corpus from the filtered list. A blocker excluded by the filter therefore dropped its reverse `blocks` edge, and the blocked item was reported as unblocked. This predates the PR and reproduces on published 2026.10.31; it became reachable from MCP once pm_context hoisted flat filters. runContext now resolves blocker ids, containers, the blockers section and the --parent subtree from an edge corpus: the listed corpus when the list echo shows no selection filter, otherwise one unfiltered scan. Displayed rows, scale and workspace memory stay filtered. As a side effect, --parent --type Task no longer reports the epic as not found. - projectPmBriefSearchEnvelope also compacts a terminal search page, which omits has_more, truncated, next_cursor and total, when its count matches the rows; inconsistent shapes pass through. - Flat-to-options hoisting treats a nested snake_case spelling (e.g. options.token_budget) as already present, so nested values keep precedence over flat camelCase arguments. - runMcpClaimAction reads `next` only from the normalized runner options, so nested next:false overrides a flat next:true for both dispatch and the target check. - Tracker: the pm-gh1642 acceptance criterion now states that the effective default order stays visible, and the pm-7zs0 release-health reminder is re-armed for 2026-11-02 06:00 UTC. --- .agents/pm/history/pm-7zs0.jsonl | 1 + .agents/pm/history/pm-gh1642.jsonl | 2 + .agents/pm/history/pm-gh1643.jsonl | 1 + .agents/pm/history/pm-hke9z7.jsonl | 4 ++ .agents/pm/issues/pm-gh1642.toon | 7 +-- .agents/pm/issues/pm-gh1643.toon | 5 +- .agents/pm/issues/pm-hke9z7.toon | 12 +++-- .agents/pm/reminders/pm-7zs0.toon | 6 +-- src/sdk/lifecycle/mcp-actions.ts | 3 +- src/sdk/output/list-envelope.ts | 19 +++++++- src/sdk/query/context.ts | 47 +++++++++++++++++-- src/sdk/runtime-input.ts | 12 ++++- tests/unit/cli/compact-agent-output.spec.ts | 17 +++++++ .../mcp/read-tool-flat-parameters.spec.ts | 4 ++ .../context/filtered-context-edges.spec.ts | 24 ++++++++++ tests/unit/sdk/lifecycle/claim-start.spec.ts | 4 ++ 16 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 tests/unit/sdk/context/filtered-context-edges.spec.ts diff --git a/.agents/pm/history/pm-7zs0.jsonl b/.agents/pm/history/pm-7zs0.jsonl index 9df73c4136..0f69bdaae8 100644 --- a/.agents/pm/history/pm-7zs0.jsonl +++ b/.agents/pm/history/pm-7zs0.jsonl @@ -580,3 +580,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-30T17:18:07.453Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"7b0e58428f53e3aaeaafb372","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.312","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/expected_result","value":"At October 31 06:00 UTC verify the next eligible daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes."},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-30T17:18:07.453Z"}],"before_hash":"ed3825cf0237eaec733c5699030cfce4d351e709a0c08651e9a179fdb21eece6","after_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","item_hash_version":3,"message":"Align the release-health expected result with the rearmed October 31 reminder","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"2251442c1d82111b7c9ac9c605d14f71ad1f1ad1f0855a6f25108160779225a1","reanchor_evidence":[{"before_hash":"6b69ce088b398cc97ec761a311b01654b7dbff970bdeadcf17cf87c5ecda87b0","after_hash":"eec31ea294980d7fe3bd9669dd82dc9502c2f2d67ce2bd31739e51cd9c9caf72","patch_hash":"8415e8b1b08ac08fb872b01e06a2c6322ade6d26de5a1cd4fda62d56a6a1cbc5","hash_algorithm":"sha256","item_hash_version":3,"record_hash_version":1,"record_hash":"ae50f6d1abbb2313aa5f6d3a23b0b8e80cd44b5b89a6662eda211d088af72c4f","record":{"hash_algorithm":"sha256","ts":"2026-10-30T17:18:07.453Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"7b0e58428f53e3aaeaafb372","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.312","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/expected_result","value":"At October 31 06:00 UTC verify the next eligible daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes."},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-30T17:18:07.453Z"}],"before_hash":"6b69ce088b398cc97ec761a311b01654b7dbff970bdeadcf17cf87c5ecda87b0","after_hash":"eec31ea294980d7fe3bd9669dd82dc9502c2f2d67ce2bd31739e51cd9c9caf72","item_hash_version":3,"message":"Align the release-health expected result with the rearmed October 31 reminder","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"ae50f6d1abbb2313aa5f6d3a23b0b8e80cd44b5b89a6662eda211d088af72c4f"}}]} {"hash_algorithm":"sha256","ts":"2026-10-31T00:01:31.558Z","author":"harness:claude-code","op":"merge_reconcile","patch":[],"before_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","after_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","message":"record field-aware branch merge provenance","item_hash_version":3,"context":{"merge":{"receipts":[{"receipt_id":"fe024a5f-0084-4e67-882f-b85ebc7b88c8","item_id":"pm-7zs0","item_path":".agents/pm/reminders/pm-7zs0.toon","conflict_fields":[],"fields_from_theirs":["expected_result"],"union_fields":[],"requested_preference":"ours","requested_preference_applied":false,"conflict_resolution":"latest_document_update","decisions":[]}]}},"event_class":"substantive","record_hash_version":1,"record_hash":"1a1377a900f98fb21321bbfc1e4a41d5223b4f4f67cba66b208ef6ef487aab66"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:12:47.452Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/258","value":{"created_at":"2026-10-31T22:12:47.451Z","author":"harness:claude-code","text":"October 31 checkpoint (verified live 22:10 UTC): the 03:45 UTC manual run 41165698315 and the 08:47 UTC scheduled run 41185994587 both stopped at static-quality-gate on 1a72d089d, before publication. The causes were two registry advisories published that morning (follow-redirects GHSA-mrwp-f3q8-3g9p, minimatch GHSA-g3hv-75wg-m249) and the pm-changelog 2026.10.31 grammar census drift. PR #1638 (3ff44fb6a) fixed both. Closing blocker #1639 at 19:16 UTC fired Auto Release (blocker_retry) 41233462967 → Release 41236628626 → v2026.10.31: npm latest 2026.10.31, GitHub Release published 20:51 UTC. Today has exactly one cut and no duplicate automatic release, and the nine other blocker_retry runs were skipped. The nightly quality alert #1641 had the same root cause and is closed."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:12:47.452Z"}],"before_hash":"726a9d13392bbff0d67d3ef6335573bd5af0e5d687a1d5d5ced38a40cb0a0a33","after_hash":"6d04e973eae1c0d9d96628dae8004240edaa87f87417fc5635e5d06a303f4517","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"61afdba7c0cf5bad087c45a9ae165d91962a9db958c0b42e9260221a51c81961"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:28.301Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/expected_result","value":"At November 2 06:00 UTC verify the November 1 daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes."},{"op":"replace","path":"/metadata/reminders/0/text","value":"Verify the 2026-11-01 daily cut (the first after PR #1645) against npm latest, GitHub Release, npm/npx/Bun/bunx consumers and fresh Sentry/telemetry; record any scheduled failure stage"},{"op":"replace","path":"/metadata/reminders/0/at","value":"2026-11-02T06:00:00.000Z"},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:28.301Z"}],"before_hash":"6d04e973eae1c0d9d96628dae8004240edaa87f87417fc5635e5d06a303f4517","after_hash":"c407e03337718ac338b979547e99cef8482dda365ce59e1605c33d7227469f0b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"3db186f945e46857ab10b909dbec5b17abca9020bb7d34777259c6cbc5324930"} diff --git a/.agents/pm/history/pm-gh1642.jsonl b/.agents/pm/history/pm-gh1642.jsonl index 102d967a54..d5e161011a 100644 --- a/.agents/pm/history/pm-gh1642.jsonl +++ b/.agents/pm/history/pm-gh1642.jsonl @@ -16,3 +16,5 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:34.358Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:34.358Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:34.335Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:34.335Z"},{"op":"add","path":"/metadata/resolution","value":"projectPmBriefListEnvelope now recognizes the default ascending sort even with effective_keys. It replaces the legacy sort/order echo with one effective_keys comparator line, keeping the GH-1507 disclosure, and drops a filters.limit equal to applied_limit and empty runtime_filters. The new public SDK projectPmBriefSearchEnvelope drops truncated when it repeats has_more and limit-only filters, and replaces the default projection_metadata omission receipt with details --full. formatBuiltInOutput chains both for TOON only. completeness unchecked and items_encoding stay by contract."},{"op":"add","path":"/metadata/actual_result","value":"Same tracker, published 2026.10.31 vs this branch: pm search 'release workflow' --limit 3 drops from 1,033 to 863 bytes and pm list --status open --limit 3 from 988 to 782 bytes. compact-agent-output spec 59/59 and default-list-order integration pass; the GH-1642 cases fail with the projection stashed. SDK surface update is additive for the new export."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: brief pm list and pm search TOON pages drop redundant envelope fields"}],"before_hash":"6781ea340c88a027084c46ef83d087576d93f0e736738fb5d7ed91a9c2f261c8","after_hash":"fd4bb7a8925c3606f9b7aaeb1f92daf781b9d23c5cd8f9cfb4cdba139f2d4951","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:34.335Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"597f8ffd7c8592bed9985297e478b5365ed80bbb1146b94e9bf7af7c7451a5b1"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:37.050Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-hke9z7","release:pm-gh1644"]},"topic":{"value":"workset:pm-gh1642+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-hke9z7","release:pm-gh1644"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:37.050Z"}],"before_hash":"fd4bb7a8925c3606f9b7aaeb1f92daf781b9d23c5cd8f9cfb4cdba139f2d4951","after_hash":"627e202bef981e72032fa181cd400f4ac75740bb8390ad552e041a5c58b73155","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"0b8c11ade15bb2e05fbe4d4a77091f4f64e2bb07b3c075c20625c2b775f3e86e"} {"hash_algorithm":"sha256","ts":"2026-10-31T23:02:11.868Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-31T23:02:11.868Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: the list projection left the raw filters key in place when the compacted filters became empty, so a limit-only or empty-runtime filters block still printed. filters is now destructured out of the envelope before compaction, and specs cover {limit}, {runtime_filters:{}} and a non-echo limit."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:11.868Z"}],"before_hash":"627e202bef981e72032fa181cd400f4ac75740bb8390ad552e041a5c58b73155","after_hash":"c6637b7d0558a5cf113b4292278bda45c83cd1ca4be8454671ecca0244734187","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3e6260740bf4b73e6414dcc7e78b88eadca4aec190d00bb5f0bcea7087b6cba2"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:27.639Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"A default brief pm list page drops the legacy default sort/order echo but keeps the effective default comparator order visible on one effective_keys line (the GH-1507 disclosure), and omits a filters.limit equal to applied_limit and empty runtime_filters; a default pm search page, including a terminal page without continuation fields, omits truncated when equal to has_more and a filters block that only echoes applied_limit, and replaces the single default projection_metadata omission receipt with details --full; unchecked or partial completeness, non-default sorting, non-limit filters, custom omission receipts, JSON, --full and SDK results are unchanged; the search projection is a reusable SDK export that leaves its input unchanged; measured envelope bytes drop on a small page"},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:27.639Z"}],"before_hash":"c6637b7d0558a5cf113b4292278bda45c83cd1ca4be8454671ecca0244734187","after_hash":"cd26a2531ee526bb79b0742106d05765a52337a90a2e604a43d80bcd08d8297c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"6e1d995b83b9c7f908e26f7e352c0ba54dfdd528e9e566d7d2ab80d7cadeebe6"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:44.606Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T01:17:44.606Z","author":"harness:claude-code","text":"CodeRabbit review round 2 on PR #1645: a final search page omits has_more, truncated, next_cursor and total, so the projection skipped it. projectPmBriefSearchEnvelope now also accepts that terminal shape when count matches the rows, there is no cursor, and any total equals count; inconsistent shapes still pass through. The acceptance criterion now states that the effective default sort order stays visible on one line, matching the resolution."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:44.606Z"}],"before_hash":"cd26a2531ee526bb79b0742106d05765a52337a90a2e604a43d80bcd08d8297c","after_hash":"4ebb1e9242650bf4b2a3ca7a173323d9746385c0a1466d05fdd6d822df5155ae","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8e9c8d49e27437447444622ef9b1a87c46a6cfe023e91a5b1881394a921e0187"} diff --git a/.agents/pm/history/pm-gh1643.jsonl b/.agents/pm/history/pm-gh1643.jsonl index d78c40fd16..5ce98596eb 100644 --- a/.agents/pm/history/pm-gh1643.jsonl +++ b/.agents/pm/history/pm-gh1643.jsonl @@ -33,3 +33,4 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T23:01:52.139Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/16/path","value":"tests/unit/commands/register-commands.spec.ts"},{"op":"replace","path":"/metadata/files/15/path","value":"tests/fixtures/contracts/full.json"},{"op":"replace","path":"/metadata/files/14/path","value":"src/sdk/start-task-status.ts"},{"op":"replace","path":"/metadata/files/13/path","value":"src/sdk/runtime.ts"},{"op":"replace","path":"/metadata/files/12/path","value":"src/sdk/runtime-public-contracts.ts"},{"op":"replace","path":"/metadata/files/11/path","value":"src/sdk/lifecycle/task-composition.ts"},{"op":"replace","path":"/metadata/files/10/path","value":"src/sdk/lifecycle/mcp-actions.ts"},{"op":"replace","path":"/metadata/files/9/path","value":"src/sdk/lifecycle/claim.ts"},{"op":"replace","path":"/metadata/files/8/path","value":"src/sdk/index.ts"},{"op":"replace","path":"/metadata/files/7/path","value":"src/sdk/generated/generated-error-code-catalog-part-2.ts"},{"op":"replace","path":"/metadata/files/6/path","value":"src/sdk/generated/generated-error-code-catalog-part-1.ts"},{"op":"replace","path":"/metadata/files/5/path","value":"src/sdk/core.ts"},{"op":"replace","path":"/metadata/files/4/path","value":"src/sdk/cli-contracts/tool-schema.ts"},{"op":"replace","path":"/metadata/files/3/path","value":"src/core/output/mutation-projection.ts"},{"op":"add","path":"/metadata/files/17","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:01:52.139Z"}],"before_hash":"91af23eb1db7178f50bcd3b4f83ac880e13bca8487da36ff1acaba3cb84cd938","after_hash":"ff6ddda29e874e14c7003434d097a35b38a7f90e28e10c266551d707ae23fd6b","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"596d76738a0193e498f11b1c72b4a6ad446e8a42bef7a245990fafbecd0184bc"} {"hash_algorithm":"sha256","ts":"2026-10-31T23:01:53.896Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/17/path","value":"tests/unit/sdk/lifecycle/claim-attribution.spec.ts"},{"op":"add","path":"/metadata/files/18","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:01:53.896Z"}],"before_hash":"ff6ddda29e874e14c7003434d097a35b38a7f90e28e10c266551d707ae23fd6b","after_hash":"fc532b444810eb74add7b894d39c62e4fb2149d657b131cc89466ae45c6925c5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"027024aea2395e19460ec3d8126b49f2ab798c52e8b8369aa67e7bff7b73f72b"} {"hash_algorithm":"sha256","ts":"2026-10-31T23:02:07.901Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:07.900Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:07.901Z"}],"before_hash":"fc532b444810eb74add7b894d39c62e4fb2149d657b131cc89466ae45c6925c5","after_hash":"49047307204c49ea959e67b704a2ae82efca32f73dc3168b1327a0dd6bc7f1b8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"fe9b990be6bff337cc1cbf1c4affc0d4f2985f124a22cc1ded44c7b16c34c670"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:43.802Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-11-01T01:17:43.802Z","author":"harness:claude-code","text":"CodeRabbit review round 2 on PR #1645: runMcpClaimAction now reads next only from the normalized runner options, so nested next:false overrides a flat next:true for both dispatch and the target check. The spec asserts {next:true, options:{next:false,start:true}} is refused with missing_required_argument instead of claiming an item."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:43.802Z"}],"before_hash":"49047307204c49ea959e67b704a2ae82efca32f73dc3168b1327a0dd6bc7f1b8","after_hash":"f677667536f449c94a11eb7302e541871b6f281974896af8ee39fd4015feca8c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f0cee1913393543d85e9ab86649991c7397be0eff3cca41d65d2531d0b97332f"} diff --git a/.agents/pm/history/pm-hke9z7.jsonl b/.agents/pm/history/pm-hke9z7.jsonl index cded2252d0..077bd7c628 100644 --- a/.agents/pm/history/pm-hke9z7.jsonl +++ b/.agents/pm/history/pm-hke9z7.jsonl @@ -12,3 +12,7 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:35.103Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:35.103Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:35.081Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:35.081Z"},{"op":"add","path":"/metadata/resolution","value":"runtime-input.ts derives flat-to-options hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional, excluding the query/keywords operands. One hoisted-key list replaces three loops, and nested options still win. runMcpSearchAction reads query, then the declared keywords alias, then refuses with the unchanged missing-query error."},{"op":"add","path":"/metadata/actual_result","value":"Through the MCP tools/call handler, flat and nested now agree for pm_context depth/for/limit, for on pm_list/pm_search/pm_next/pm_get, pm_get depth and pm_next limit. The flat orient call also discloses the limit override. read-tool-flat-parameters spec 7/7 iterates every declared optional key; 6/7 fail with the fix stashed. mcp-handshake hoist expectations pass."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: MCP read tools read every declared flat parameter and pm_search honours its keywords alias"}],"before_hash":"a2afdffa5b525c61c291a77a756911d0d9ee332f9482821ce48758019960b245","after_hash":"50e1db555085a5bc7e76ea6f1038f330ebd57ee081ce3feab59f8912fce21ceb","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:35.081Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"75aaf770cabd5ed75a190c93e47c30786c7109187d49f4b36d24e1b3e41bb080"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:37.663Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-hke9z7","release:pm-gh1642"]},"topic":{"value":"pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-hke9z7","release:pm-gh1642"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:37.663Z"}],"before_hash":"50e1db555085a5bc7e76ea6f1038f330ebd57ee081ce3feab59f8912fce21ceb","after_hash":"b979ea17c98bcfc1cd9490f8b018fc4b0abeb1060a77e54936c2260063147d1c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"7423ce52a1be6976c564295433427ba17f6a5e653660c3aab8d616db459bc238"} {"hash_algorithm":"sha256","ts":"2026-10-31T23:02:13.787Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:13.786Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: the docs now state that search's query operand (and its keywords alias) stays top-level like pm_get's id, while every other declared optional parameter is accepted flat or under options."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:13.787Z"}],"before_hash":"b979ea17c98bcfc1cd9490f8b018fc4b0abeb1060a77e54936c2260063147d1c","after_hash":"5f4ffe016bb41b6c5eaf69106d572ce1bfd5f0b959961221758bfaa62fdc67e5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c66a8a024cdb7c969208ec1a9fda1024d8121fdcc6b59532c04f8dc9f34c539d"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:40.826Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/2/path","value":"src/sdk/runtime/actions.ts"},{"op":"replace","path":"/metadata/files/1/path","value":"src/sdk/runtime-input.ts"},{"op":"replace","path":"/metadata/files/0/path","value":"src/sdk/query/context.ts"},{"op":"add","path":"/metadata/files/3","value":{"path":"tests/unit/mcp/read-tool-flat-parameters.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:40.826Z"}],"before_hash":"5f4ffe016bb41b6c5eaf69106d572ce1bfd5f0b959961221758bfaa62fdc67e5","after_hash":"c3fa8ab01859b337700a5f156c163f06e6c196e4f60926e32555136756bbd60d","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"525e76b0780d8ec7ce7fcece8dda04247c1343287ba9e39c482e2d71bbe9a0d7"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:41.597Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/4","value":{"path":"tests/unit/sdk/context/filtered-context-edges.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:41.597Z"}],"before_hash":"c3fa8ab01859b337700a5f156c163f06e6c196e4f60926e32555136756bbd60d","after_hash":"7af736b9331ca2b57be0f805a2a21a5ebd0314ba402db51880809fdc628a7e31","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"219095d6b5c6c78752827c40035795b91362f19f8e029c68140b18431ae247d6"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:42.401Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/filtered-context-edges.spec.ts","scope":"project","provenance":{"author":"harness:claude-code","created_at":"2026-11-01T01:17:42.357Z","source_kind":"local_mutation","source_ref":"fix/agent-ux-orient-claim-envelopes"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:42.401Z"}],"before_hash":"7af736b9331ca2b57be0f805a2a21a5ebd0314ba402db51880809fdc628a7e31","after_hash":"8590e65550af3120e06bd357066bdc101ace48b38f53575651e99dfacc733c26","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"edc53662518da315e422de6deedbda926395fb5c576868f45c70981d9d396f5f"} +{"hash_algorithm":"sha256","ts":"2026-11-01T01:17:43.180Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-11-01T01:17:43.180Z","author":"harness:claude-code","text":"CodeRabbit review round 2 on PR #1645. (1) Major, predates this PR (published 2026.10.31 reproduces via pm context --type Task): when a context filter removed a blocker that declares a reverse blocks edge, the blocked item was reported as not blocked, because the dependency corpus was the filtered list. pm_context now hoists flat filters, which made this reachable from MCP too. runContext keeps the filtered list for displayed rows, scale and workspace memory, but resolves blocker ids, containers, blocker sections and the --parent subtree from an edge corpus. That corpus is the same list when no filter applied, or one unfiltered scan when the list echo shows a selection filter. A side effect fixed: --parent combined with --type Task no longer reports the epic as not found. The filtered-context-edges spec fails with the fix stashed. (2) Hoisting treats a nested snake_case spelling (options.token_budget) as already present, so it wins over a flat tokenBudget."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:43.180Z"}],"before_hash":"8590e65550af3120e06bd357066bdc101ace48b38f53575651e99dfacc733c26","after_hash":"210873a32c455e25a08fdb4dabb15069d62adba838a5f060cbbcb3f620cece49","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"a241ac9bccaf0db634962579070ff0df56e719ecc29153f138f45bc5285fcd42"} diff --git a/.agents/pm/issues/pm-gh1642.toon b/.agents/pm/issues/pm-gh1642.toon index 49405e30de..26e4804f7e 100644 --- a/.agents/pm/issues/pm-gh1642.toon +++ b/.agents/pm/issues/pm-gh1642.toon @@ -6,12 +6,12 @@ status: closed priority: 2 tags[4]: agent-ux,github,output,token-efficiency created_at: "2026-10-31T21:14:02.437Z" -updated_at: "2026-10-31T23:02:11.868Z" +updated_at: "2026-11-01T01:17:44.606Z" closed_at: "2026-10-31T22:31:34.335Z" completed_at: "2026-10-31T22:31:34.335Z" author: "harness:claude-code" estimated_minutes: 120 -acceptance_criteria: "A default brief pm list page omits the default sorting block including effective_keys and a filters.limit equal to applied_limit; a default pm search page omits truncated when equal to has_more, a filters block that only echoes applied_limit, and replaces the single default projection_metadata omission receipt with details --full; unchecked or partial completeness, non-default sorting, non-limit filters, custom omission receipts, JSON, --full and SDK results are unchanged; the search projection is a reusable SDK export that leaves its input unchanged; measured envelope bytes drop on a small page" +acceptance_criteria: "A default brief pm list page drops the legacy default sort/order echo but keeps the effective default comparator order visible on one effective_keys line (the GH-1507 disclosure), and omits a filters.limit equal to applied_limit and empty runtime_filters; a default pm search page, including a terminal page without continuation fields, omits truncated when equal to has_more and a filters block that only echoes applied_limit, and replaces the single default projection_metadata omission receipt with details --full; unchecked or partial completeness, non-default sorting, non-limit filters, custom omission receipts, JSON, --full and SDK results are unchanged; the search projection is a reusable SDK export that leaves its input unchanged; measured envelope bytes drop on a small page" parent: pm-5t33or risk: low confidence: high @@ -22,10 +22,11 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-6pycus,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected pm-gh1627,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected pm-zd46m7,related,"2026-10-31T21:49:42.569Z","harness:claude-code","cli:update:dep",detected -comments[3]{created_at,author,text}: +comments[4]{created_at,author,text}: "2026-10-31T21:47:00.908Z","harness:claude-code","Implementation and measurement: projectPmBriefListEnvelope now drops the default ascending sort when it carries effective_keys (the regression), and drops a filters.limit equal to applied_limit and empty runtime_filters. New public projectPmBriefSearchEnvelope (sdk/output) drops truncated when it equals has_more and limit-only filters, and replaces the single default projection_metadata omission receipt with details --full. It is chained after the list projection in formatBuiltInOutput (TOON only). Measured with the published 2026.10.31 package against this branch on this tracker: pm search 'release workflow' --limit 3 drops from 1,033 to 863 B (-16%) and pm list --status open --limit 3 from 988 to 782 B (-21%). completeness unchecked and items_encoding remain by design (see description)." "2026-10-31T21:49:41.855Z","harness:claude-code","Design correction while testing: the 7-line default sort block is not only redundant metadata. GH-1507's accepted contract (pm-zd46m7, integration spec default-list-order) requires brief TOON to disclose the effective default comparator order so an agent knows why rows arrive in that order. Dropping it would undo that decision. The projection therefore drops only the legacy sort/order echo and renders the effective keys on one line, for example sorting.effective_keys 'status_registry:nonterminal_before_terminal asc, priority asc, updated_at desc, id asc'. Seven lines become two, and JSON keeps the full pair array. A malformed or extended sorting block passes through unchanged." "2026-10-31T23:02:11.868Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: the list projection left the raw filters key in place when the compacted filters became empty, so a limit-only or empty-runtime filters block still printed. filters is now destructured out of the envelope before compaction, and specs cover {limit}, {runtime_filters:{}} and a non-echo limit." + "2026-11-01T01:17:44.606Z","harness:claude-code","CodeRabbit review round 2 on PR #1645: a final search page omits has_more, truncated, next_cursor and total, so the projection skipped it. projectPmBriefSearchEnvelope now also accepts that terminal shape when count matches the rows, there is no cursor, and any total equals count; inconsistent shapes still pass through. The acceptance criterion now states that the effective default sort order stays visible on one line, matching the resolution." files[5]{path,scope}: sdk/public-surface.json,project src/core/output/output.ts,project diff --git a/.agents/pm/issues/pm-gh1643.toon b/.agents/pm/issues/pm-gh1643.toon index 55f87dc0b5..93e0f9bf0f 100644 --- a/.agents/pm/issues/pm-gh1643.toon +++ b/.agents/pm/issues/pm-gh1643.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: agent-ux,claim,github,refusal created_at: "2026-10-31T21:13:57.945Z" -updated_at: "2026-10-31T23:02:07.901Z" +updated_at: "2026-11-01T01:17:43.802Z" closed_at: "2026-10-31T22:31:19.294Z" completed_at: "2026-10-31T22:31:19.294Z" author: "harness:claude-code" @@ -24,9 +24,10 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-fjxm,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-gh1369,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-ixi1,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected -comments[2]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-10-31T21:46:58.955Z","harness:claude-code","Implementation: runClaim gained an optional start that calls applyClaimStart inside the same mutateItem callback (the release --handoff pattern). It resolves the in-progress status through resolveStartTaskInProgressStatus, refuses a terminal item with terminal_state_conflict even under --force, enforces type workflows via enforceTypeWorkflowTransition, and returns start {from_status,to_status}. runClaimNext forwards start to every candidate in the race-loss walk, so --if-available and filters behave as before. CLI runClaimAction and MCP runMcpClaimAction route start with an explicit id to the unchanged two-step composition, which keeps its typed 'Remove --next and --if-available' refusal; start without an id uses the atomic path or the new requireClaimTarget in src/sdk/lifecycle/claim-target.ts (missing_required_argument / ambiguous_option_combination, attributed to claim through the catalog generator's module map). runStartTask now strips start before its claim step so explicit --start stays claim-then-update. MCP schema: the start+next exclusion is gone and start+ifAvailable is exclusive only with an id. PmClient.claimNext({start:true}) is typed. Verified in a sandbox: claim --next --start → status in_progress, changed_field_count 3, history create→claim (one event)." "2026-10-31T23:02:07.900Z","harness:claude-code","CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi." + "2026-11-01T01:17:43.802Z","harness:claude-code","CodeRabbit review round 2 on PR #1645: runMcpClaimAction now reads next only from the normalized runner options, so nested next:false overrides a flat next:true for both dispatch and the target check. The spec asserts {next:true, options:{next:false,start:true}} is refused with missing_required_argument instead of claiming an item." files[19]{path,scope}: scripts/release/surface-replication-sets.json,project sdk/public-surface.json,project diff --git a/.agents/pm/issues/pm-hke9z7.toon b/.agents/pm/issues/pm-hke9z7.toon index 307dfcc225..02a98b6ee8 100644 --- a/.agents/pm/issues/pm-hke9z7.toon +++ b/.agents/pm/issues/pm-hke9z7.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: agent-ux,context,contracts,mcp created_at: "2026-10-31T21:27:47.547Z" -updated_at: "2026-10-31T23:02:13.787Z" +updated_at: "2026-11-01T01:17:43.180Z" closed_at: "2026-10-31T22:31:35.081Z" completed_at: "2026-10-31T22:31:35.081Z" author: "harness:claude-code" @@ -22,15 +22,19 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-gh1644,discovered_from,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected pm-hno5,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected pm-wcaa,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected -comments[2]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-10-31T21:47:01.751Z","harness:claude-code","Implementation: runtime-input.ts derives hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional (declaredReadActionOptionKeys), after the hand table, so nested options still win. Verified through the MCP tools/call handler: flat and nested calls now agree for pm_context depth/for/limit, pm_list/pm_search/pm_next/pm_get for, pm_get depth and pm_next limit. The regression spec iterates every declared optional key of the five actions, so a new declared parameter can no longer be silently dropped." "2026-10-31T23:02:13.786Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: the docs now state that search's query operand (and its keywords alias) stays top-level like pm_get's id, while every other declared optional parameter is accepted flat or under options." -files[3]{path,scope}: + "2026-11-01T01:17:43.180Z","harness:claude-code","CodeRabbit review round 2 on PR #1645. (1) Major, predates this PR (published 2026.10.31 reproduces via pm context --type Task): when a context filter removed a blocker that declares a reverse blocks edge, the blocked item was reported as not blocked, because the dependency corpus was the filtered list. pm_context now hoists flat filters, which made this reachable from MCP too. runContext keeps the filtered list for displayed rows, scale and workspace memory, but resolves blocker ids, containers, blocker sections and the --parent subtree from an edge corpus. That corpus is the same list when no filter applied, or one unfiltered scan when the list echo shows a selection filter. A side effect fixed: --parent combined with --type Task no longer reports the epic as not found. The filtered-context-edges spec fails with the fix stashed. (2) Hoisting treats a nested snake_case spelling (options.token_budget) as already present, so it wins over a flat tokenBudget." +files[5]{path,scope}: + src/sdk/query/context.ts,project src/sdk/runtime-input.ts,project src/sdk/runtime/actions.ts,project tests/unit/mcp/read-tool-flat-parameters.spec.ts,project -tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: + tests/unit/sdk/context/filtered-context-edges.spec.ts,project +tests[2]{command,scope,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts,project,"harness:claude-code","2026-10-31T21:46:36.177Z",local_mutation,fix/agent-ux-orient-claim-envelopes + node scripts/run-tests.mjs test -- tests/unit/sdk/context/filtered-context-edges.spec.ts,project,"harness:claude-code","2026-11-01T01:17:42.357Z",local_mutation,fix/agent-ux-orient-claim-envelopes test_runs[1]: - run_id: test-local-mvwz5gjx-0ayygj kind: test diff --git a/.agents/pm/reminders/pm-7zs0.toon b/.agents/pm/reminders/pm-7zs0.toon index e9f63f2b7d..1aa5873a71 100644 --- a/.agents/pm/reminders/pm-7zs0.toon +++ b/.agents/pm/reminders/pm-7zs0.toon @@ -6,16 +6,16 @@ status: open priority: 2 tags[3]: "area:release",living-map,reliability created_at: "2026-06-07T10:24:45.889Z" -updated_at: "2026-10-31T22:12:47.452Z" +updated_at: "2026-11-01T01:17:28.301Z" reminders[1]{at,text}: - "2026-10-31T06:00:00.000Z",Verify the next daily release and npm/npx/Bun/bunx consumers; retain pm-gh1622 until installed-producer remediation and publication are verified + "2026-11-02T06:00:00.000Z","Verify the 2026-11-01 daily cut (the first after PR #1645) against npm latest, GitHub Release, npm/npx/Bun/bunx consumers and fresh Sentry/telemetry; record any scheduled failure stage" author: maintainer-agent estimated_minutes: 15 acceptance_criteria: "After each merge to main, the next daily release tags vYYYY.M.D and npm latest advances; any discrepancy is triaged the same day.; Every daily check distinguishes scheduled or guarded dispatch attempts, failed original runs, same-version recovery, immutable publication identity and all supported consumer acceptance results" parent: pm-u9d0 risk: low confidence: high -expected_result: "At October 31 06:00 UTC verify the next eligible daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes." +expected_result: "At November 2 06:00 UTC verify the November 1 daily cut against the latest merged delivery, including tag/npm identity, npm/Bun/npx/bunx consumers and fresh observability; preserve original reliability outcomes." actual_result: "October28 morning Auto Release40628452727 and Release40631211289 succeed. Exactly v2026.10.28 is published; independent registry integrity, actual npx/bunx, all bins, MCP Skills/Apps and HTTP acceptance pass. This immutable release precedes PR1616 SDK changes. Fresh real read spans are accepted/indexed in Sentry; required observability gate passes with zero unresolved issues, zero missing error codes and5.91percent finish-error rate. Recent installation-scoped telemetry has28 matched starts/finishes, zero settled mismatches and empty broker queue. Historical reliability remains separate." dependencies[14]: - id: pm-7n5a diff --git a/src/sdk/lifecycle/mcp-actions.ts b/src/sdk/lifecycle/mcp-actions.ts index a8449d5d9e..4df4922f4a 100644 --- a/src/sdk/lifecycle/mcp-actions.ts +++ b/src/sdk/lifecycle/mcp-actions.ts @@ -32,7 +32,8 @@ export async function runMcpClaimAction(context: LifecycleMutationActionContext) const { changedFields, idOnly, runnerOptions } = withMutationCompaction(context.args, context.options); const force = context.force === true || runnerOptions.force === true; const selectionOptions = { ...context.args, ...runnerOptions }; - const next = context.args.next === true || runnerOptions.next === true; + // runnerOptions already applies nested-over-flat precedence, so dispatch and target checks share one value. + const next = runnerOptions.next === true; const id = context.id ?? readRuntimeString(runnerOptions, "id"); if (selectionOptions.start === true) { if (id !== undefined) return runMcpTaskCompositionAction(context, "start_task"); diff --git a/src/sdk/output/list-envelope.ts b/src/sdk/output/list-envelope.ts index 1bd892589f..b7ca717afc 100644 --- a/src/sdk/output/list-envelope.ts +++ b/src/sdk/output/list-envelope.ts @@ -180,6 +180,20 @@ function compactEchoedFilters( return Object.keys(remaining).length === 0 ? {} : { filters: remaining }; } +/** A final search page omits the continuation fields entirely; accept it only when nothing suggests more rows. */ +function hasTerminalSearchPagination( + value: Record, + count: number, +): boolean { + return ( + value.count === count && + !Object.hasOwn(value, "has_more") && + !Object.hasOwn(value, "truncated") && + value.next_cursor == null && + (value.total === undefined || value.total === count) + ); +} + /** * Compact a standard search page without mutating its typed SDK result: * drops `truncated` when it repeats `has_more`, a `filters.limit` echo of @@ -193,7 +207,10 @@ export function projectPmBriefSearchEnvelope(value: unknown): unknown { typeof value.query !== "string" || !Array.isArray(value.items) || Object.hasOwn(value, "details") || - !hasStandardPagination(value, value.items.length) || + !( + hasStandardPagination(value, value.items.length) || + hasTerminalSearchPagination(value, value.items.length) + ) || !hasDefaultOmissions(value.omission_receipt, "projection_metadata") ) return value; diff --git a/src/sdk/query/context.ts b/src/sdk/query/context.ts index a94fe9d018..bc64b6809f 100644 --- a/src/sdk/query/context.ts +++ b/src/sdk/query/context.ts @@ -2065,9 +2065,45 @@ interface ContextCorpus { listedItemMetadata: ItemMetadata[]; allItems: ItemMetadata[]; fullCorpus: ItemMetadata[]; + /** Unfiltered corpus for blocker, container and subtree resolution, so edges outside a filter still resolve. */ + edgeCorpus: ItemMetadata[]; subtreeIds: Set | undefined; } +/** Filter echo keys a context list reports even when no selection filter applied. */ +const CONTEXT_UNFILTERED_LIST_ECHO_KEYS: ReadonlySet = new Set([ + "status", + "no_truncate", + "limit", + "sort", + "order", +]); + +/** Whether the list echo shows a selection filter that removed items from the corpus. */ +function contextListWasFiltered(filters: Record): boolean { + const { runtime_filters: runtimeFilters, ...selection } = filters; + return ( + Object.keys(selection).some((key) => !CONTEXT_UNFILTERED_LIST_ECHO_KEYS.has(key)) || + Object.keys(Object(runtimeFilters)).length > 0 + ); +} + +/** Reuse the listed corpus when unfiltered; otherwise scan once without filters so reverse `blocks` edges and parents outside the filter resolve. */ +async function loadContextEdgeCorpus( + global: GlobalOptions, + listed: Awaited>, +): Promise { + if (!contextListWasFiltered(listed.filters)) { + return listed.items as ItemMetadata[]; + } + const unfiltered = await runList( + undefined, + buildUnpaginatedContextListOptions({}, { excludeTerminal: false }), + global, + ); + return unfiltered.items as ItemMetadata[]; +} + interface ContextFocusGroups { activeItems: ItemMetadata[]; blockedItems: ItemMetadata[]; @@ -2176,11 +2212,12 @@ async function loadContextCorpus( global, ); const fullCorpus = listed.items as ItemMetadata[]; + const edgeCorpus = await loadContextEdgeCorpus(global, listed); let listedItemMetadata = fullCorpus.filter( (item) => !isTerminalStatus(item.status, runtime.statusRegistry), ); let allItems: ItemMetadata[] = fullCorpus; - const subtreeIds = resolveContextSubtreeIds(runtime.parentScope, fullCorpus); + const subtreeIds = resolveContextSubtreeIds(runtime.parentScope, edgeCorpus); if (subtreeIds) { listedItemMetadata = listedItemMetadata.filter((item) => subtreeIds.has(item.id.trim().toLowerCase()), @@ -2189,7 +2226,7 @@ async function loadContextCorpus( subtreeIds.has(item.id.trim().toLowerCase()), ); } - return { listed, listedItemMetadata, allItems, fullCorpus, subtreeIds }; + return { listed, listedItemMetadata, allItems, fullCorpus, edgeCorpus, subtreeIds }; } function resolveContextSubtreeIds( @@ -2822,13 +2859,13 @@ export async function runContext( // Single shared edge-aware blocked classification (GH-578): resolved against // the full corpus so terminal blocker targets count as satisfied at every depth. const blockedIds = collectDependencyBlockedIds( - corpus.fullCorpus, + corpus.edgeCorpus, runtime.statusRegistry, ); const focusGroups = await resolveContextFocusGroups( corpus.listedItemMetadata, corpus.allItems, - corpus.fullCorpus, + corpus.edgeCorpus, blockedIds, runtime.statusRegistry, runtime.sectionsIncluded, @@ -2876,7 +2913,7 @@ export async function runContext( const sections = await buildOptionalContextSections({ runtime, allItems: corpus.allItems, - fullCorpus: corpus.fullCorpus, + fullCorpus: corpus.edgeCorpus, blockedIds, focusGroups, now: agendaContext.agenda.now, diff --git a/src/sdk/runtime-input.ts b/src/sdk/runtime-input.ts index 0e85551a6c..fbf077d1a1 100644 --- a/src/sdk/runtime-input.ts +++ b/src/sdk/runtime-input.ts @@ -370,6 +370,15 @@ function declaredReadActionOptionKeys(action: string): readonly string[] { : []; } +/** Whether nested options already carry a key under its camelCase or snake_case spelling. */ +function nestedOptionPresent( + nested: Record, + key: string, +): boolean { + const snakeKey = key.replace(/[A-Z]/gu, (letter) => `_${letter.toLowerCase()}`); + return nested[key] !== undefined || nested[snakeKey] !== undefined; +} + /** Every top-level argument an action reads from options: its hand-listed keys, declared read parameters, and universal read-output controls. */ function hoistedActionOptionKeys(action: string): readonly string[] { return [ @@ -562,7 +571,8 @@ export function optionsWithAuthor( const baseOptions = asRecordClone(args.options); const hoistedTopLevel: Record = {}; const hoistKey = (key: string): void => { - if (baseOptions[key] !== undefined || args[key] === undefined) { + // A nested value under either spelling (tokenBudget or token_budget) wins over the flat argument. + if (nestedOptionPresent(baseOptions, key) || args[key] === undefined) { return; } hoistedTopLevel[key] = args[key]; diff --git a/tests/unit/cli/compact-agent-output.spec.ts b/tests/unit/cli/compact-agent-output.spec.ts index 2847473c7c..238c2289be 100644 --- a/tests/unit/cli/compact-agent-output.spec.ts +++ b/tests/unit/cli/compact-agent-output.spec.ts @@ -427,6 +427,23 @@ describe("GH-1642 brief search and list envelopes", () => { expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { runtime_filters: null } })).toMatchObject({ filters: { runtime_filters: null } }); }); + it("compacts a terminal search page that omits every continuation field", () => { + const { has_more: _more, truncated: _truncated, next_cursor: _cursor, total: _total, ...rest } = searchPage; + const terminal = { ...rest, count: 2, applied_limit: 10, filters: { limit: "10" } }; + expect(projectPmBriefSearchEnvelope(terminal)).toEqual({ + query: "release workflow", + mode: "hybrid", + items: searchPage.items, + count: 2, + applied_limit: 10, + details: "--full", + }); + expect(projectPmBriefSearchEnvelope({ ...terminal, total: 2 })).toMatchObject({ total: 2, details: "--full" }); + for (const inconsistent of [{ ...terminal, total: 5 }, { ...terminal, next_cursor: "more" }, { ...terminal, truncated: false }, { ...terminal, count: 3 }]) { + expect(projectPmBriefSearchEnvelope(inconsistent)).toBe(inconsistent); + } + }); + it.each([ null, [], diff --git a/tests/unit/mcp/read-tool-flat-parameters.spec.ts b/tests/unit/mcp/read-tool-flat-parameters.spec.ts index e14d078195..7e9e010551 100644 --- a/tests/unit/mcp/read-tool-flat-parameters.spec.ts +++ b/tests/unit/mcp/read-tool-flat-parameters.spec.ts @@ -21,6 +21,10 @@ describe("MCP read tools read every declared flat parameter (pm-hke9z7)", () => it("lets nested options win over the same flat argument", () => { expect(optionsWithAuthor({ limit: 5, options: { limit: 2 } }, "context")).toMatchObject({ limit: 2 }); + // A nested snake_case spelling also wins over the flat camelCase argument. + const budgeted = optionsWithAuthor({ for: "orient", tokenBudget: 2000, options: { token_budget: 400 } }, "context"); + expect(budgeted).toMatchObject({ for: "orient", token_budget: 400 }); + expect(budgeted).not.toHaveProperty("tokenBudget"); expect(optionsWithAuthor({ for: "orient" }, "update")).not.toHaveProperty("for"); }); diff --git a/tests/unit/sdk/context/filtered-context-edges.spec.ts b/tests/unit/sdk/context/filtered-context-edges.spec.ts new file mode 100644 index 0000000000..514e7fbe05 --- /dev/null +++ b/tests/unit/sdk/context/filtered-context-edges.spec.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; +import { runUpdate } from "../../../../src/sdk/lifecycle/update.js"; +import { runContext } from "../../../../src/sdk/query/context.js"; +import { createTestItemId } from "../../../helpers/itemFactory.js"; +import { withTempPmPath } from "../../../helpers/withTempPmPath.js"; + +describe("filtered context keeps dependency edges outside the filter", () => { + it("reports a task blocked through a reverse blocks edge declared by an item the filter excludes", async () => { + await withTempPmPath(async (context) => { + const global = { path: context.pmPath }; + const epic = createTestItemId(context, { title: "Scope epic", type: "Epic" }); + const task = createTestItemId(context, { title: "Reverse blocked task", type: "Task", priority: "0", parent: epic }); + const blocker = createTestItemId(context, { title: "Blocking issue", type: "Issue" }); + await runUpdate(blocker, { dep: [`id=${task},kind=blocks`] }, global); + for (const options of [{}, { type: "Task" }]) { + const result = await runContext(options, global); + const row = [...result.high_level, ...result.low_level].find((item) => item.id === task); + expect(row, JSON.stringify(options)).toMatchObject({ blocked: true, blocker_ids: [blocker] }); + } + const scoped = await runContext({ type: "Task", parent: epic }, global); + expect([...scoped.high_level, ...scoped.low_level].map((item) => item.id)).toEqual([task]); + }); + }); +}); diff --git a/tests/unit/sdk/lifecycle/claim-start.spec.ts b/tests/unit/sdk/lifecycle/claim-start.spec.ts index a38070054d..a6e0695036 100644 --- a/tests/unit/sdk/lifecycle/claim-start.spec.ts +++ b/tests/unit/sdk/lifecycle/claim-start.spec.ts @@ -134,6 +134,10 @@ describe("GH-1643 claim --next --start", () => { expect(refused.stderr).toContain("missing_required_argument"); expect(refused.stderr).toContain("pm claim --next --start"); await expect(runAction({ ...common, action: "claim", start: true })).rejects.toMatchObject({ context: { code: "missing_required_argument" } }); + // Nested options win over flat arguments for both dispatch and the target check. + await expect(runAction({ ...common, action: "claim", next: true, options: { next: false, start: true } })).rejects.toMatchObject({ + context: { code: "missing_required_argument" }, + }); // An explicit id with next and start is refused by the explicit-start composition before any write. await expect(runAction({ ...common, action: "claim", id: third, next: true, start: true })).rejects.toMatchObject({ context: { code: "invalid_argument_value" }, From 7f24a7f4ff8eee919b815e2d5c794b2f77c1c40a Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 1 Nov 2026 03:21:40 +0100 Subject: [PATCH 4/6] =?UTF-8?q?fix(agent-ux):=20address=20cubic=20review?= =?UTF-8?q?=20=E2=80=94=20dangling=20start=20status,=20empty-id=20refusal,?= =?UTF-8?q?=20no=20undeclared=20intent=20option,=20accurate=20receipts=20a?= =?UTF-8?q?nd=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - resolveStartTaskInProgressStatus no longer returns a configured in-progress status that the registry does not define; `pm schema remove-status` can leave that dangling reference. It falls back to the in_progress alias lookup, then to the open status. - A quoted empty id (`pm claim "" --start`) now reaches the typed missing_required_argument refusal on CLI and MCP. - The intentRequestedLimit marker option is removed. Hosts attach intent receipts with the caller's own options, so the requested limit is read from options.limit and no undeclared option leaks into, or can be spoofed through, list/search options. - projectPmBriefSearchEnvelope refuses envelopes that carry a projection block, and the effective_keys line is rendered only for asc/desc comparator pairs. - The SDK surface snapshot is regenerated from main with one acknowledgement covering only the optional additions. - Docs: the effective-order line applies to flat default lists only; limit_override.flag is documented; the explicit-start invalid_argument_value refusal is named; and the runClaim `start` docstring states it is the single-write variant used by ranked selection. - Tests: exact filter assertions; the filtered-context spec checks that the blocker row stays hidden; registry tests cover a dangling in-progress status; empty-id refusal. - Tracker: item titles now describe the fixes, so the generated changelog reads correctly. pm-gh1644's recorded result matches the flag-fragment restore_with, and fresh linked-test runs are recorded at this head. --- .agents/pm/history/pm-gh1642.jsonl | 3 +++ .agents/pm/history/pm-gh1643.jsonl | 3 +++ .agents/pm/history/pm-gh1644.jsonl | 3 +++ .agents/pm/history/pm-hke9z7.jsonl | 3 +++ .agents/pm/issues/pm-gh1642.toon | 20 +++++++++++++---- .agents/pm/issues/pm-gh1643.toon | 20 +++++++++++++---- .agents/pm/issues/pm-gh1644.toon | 22 ++++++++++++++----- .agents/pm/issues/pm-hke9z7.toon | 21 ++++++++++++++---- CHANGELOG.md | 8 +++---- docs/LIFECYCLE_COMMANDS.md | 8 ++++--- docs/SDK.md | 8 ++++--- docs/SDK_ACTION_CONFORMANCE.md | 5 +++-- sdk/public-surface.json | 8 ------- src/cli/register-operations.ts | 2 +- src/sdk/context-intent-contracts.ts | 14 +++--------- src/sdk/lifecycle/claim.ts | 7 +++++- src/sdk/lifecycle/mcp-actions.ts | 2 +- src/sdk/output/list-envelope.ts | 3 ++- src/sdk/start-task-status.ts | 6 ++++- tests/unit/cli/compact-agent-output.spec.ts | 4 +++- tests/unit/commands/register-commands.spec.ts | 8 +++++++ .../context/filtered-context-edges.spec.ts | 6 +++-- .../sdk/context/intent-limit-override.spec.ts | 22 ++++++++----------- 23 files changed, 137 insertions(+), 69 deletions(-) diff --git a/.agents/pm/history/pm-gh1642.jsonl b/.agents/pm/history/pm-gh1642.jsonl index d5e161011a..1adc4813b3 100644 --- a/.agents/pm/history/pm-gh1642.jsonl +++ b/.agents/pm/history/pm-gh1642.jsonl @@ -18,3 +18,6 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T23:02:11.868Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-10-31T23:02:11.868Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: the list projection left the raw filters key in place when the compacted filters became empty, so a limit-only or empty-runtime filters block still printed. filters is now destructured out of the envelope before compaction, and specs cover {limit}, {runtime_filters:{}} and a non-echo limit."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:11.868Z"}],"before_hash":"627e202bef981e72032fa181cd400f4ac75740bb8390ad552e041a5c58b73155","after_hash":"c6637b7d0558a5cf113b4292278bda45c83cd1ca4be8454671ecca0244734187","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"3e6260740bf4b73e6414dcc7e78b88eadca4aec190d00bb5f0bcea7087b6cba2"} {"hash_algorithm":"sha256","ts":"2026-11-01T01:17:27.639Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"A default brief pm list page drops the legacy default sort/order echo but keeps the effective default comparator order visible on one effective_keys line (the GH-1507 disclosure), and omits a filters.limit equal to applied_limit and empty runtime_filters; a default pm search page, including a terminal page without continuation fields, omits truncated when equal to has_more and a filters block that only echoes applied_limit, and replaces the single default projection_metadata omission receipt with details --full; unchecked or partial completeness, non-default sorting, non-limit filters, custom omission receipts, JSON, --full and SDK results are unchanged; the search projection is a reusable SDK export that leaves its input unchanged; measured envelope bytes drop on a small page"},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:27.639Z"}],"before_hash":"c6637b7d0558a5cf113b4292278bda45c83cd1ca4be8454671ecca0244734187","after_hash":"cd26a2531ee526bb79b0742106d05765a52337a90a2e604a43d80bcd08d8297c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"6e1d995b83b9c7f908e26f7e352c0ba54dfdd528e9e566d7d2ab80d7cadeebe6"} {"hash_algorithm":"sha256","ts":"2026-11-01T01:17:44.606Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T01:17:44.606Z","author":"harness:claude-code","text":"CodeRabbit review round 2 on PR #1645: a final search page omits has_more, truncated, next_cursor and total, so the projection skipped it. projectPmBriefSearchEnvelope now also accepts that terminal shape when count matches the rows, there is no cursor, and any total equals count; inconsistent shapes still pass through. The acceptance criterion now states that the effective default sort order stays visible on one line, matching the resolution."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:44.606Z"}],"before_hash":"cd26a2531ee526bb79b0742106d05765a52337a90a2e604a43d80bcd08d8297c","after_hash":"4ebb1e9242650bf4b2a3ca7a173323d9746385c0a1466d05fdd6d822df5155ae","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8e9c8d49e27437447444622ef9b1a87c46a6cfe023e91a5b1881394a921e0187"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:03.542Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:03.542Z"},{"op":"replace","path":"/metadata/title","value":"GH-1642: compact brief pm list and pm search TOON envelopes while keeping the effective default sort visible"}],"before_hash":"4ebb1e9242650bf4b2a3ca7a173323d9746385c0a1466d05fdd6d822df5155ae","after_hash":"4443433d90df30b9e714cfd6453f0d296a9d48e5473047db13535f7f9cf8ff51","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"765c8b0bf4dda535d33037ccbf186aaa6b9be9f9ebb8137ad7a4c3674ca6072a"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:53.192Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/4","value":{"created_at":"2026-11-01T02:03:53.192Z","author":"harness:claude-code","text":"cubic review on PR #1645: the search projection now refuses envelopes that carry a projection block, and the effective_keys line is rendered only for asc/desc comparator pairs (malformed receipts pass through). The SDK doc qualifies the effective-order line as a flat-default-list behaviour, since tree-mode lists carry no effective_keys. The item title now describes the fix, so the generated changelog no longer reads backwards."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:53.192Z"}],"before_hash":"4443433d90df30b9e714cfd6453f0d296a9d48e5473047db13535f7f9cf8ff51","after_hash":"a2d5cde42cab9fc0564150d497495b66203461d472c4f925708a92c3b3878b6c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"404ea02fc15207d3c1561cf7c6c55f786e499b22cee1dcf7f60ac935dc8bddb4"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:19:51.917Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7clwi-d2xpmi","kind":"test","status":"passed","started_at":"2026-11-01T02:19:44.817Z","finished_at":"2026-11-01T02:19:51.906Z","recorded_at":"2026-11-01T02:19:51.906Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:19:51.917Z"}],"before_hash":"a2d5cde42cab9fc0564150d497495b66203461d472c4f925708a92c3b3878b6c","after_hash":"9f539a1227fc761c4c6a4ea22f552b09e19fef794b8e664f7a69bf55ed0042a1","item_hash_version":3,"message":"Track test run summary (test-local-mvx7clwi-d2xpmi)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"14f3a009b4ffddf71bed3f30242b50470bdccd39dec3609c9c2aa26d5087f6d1"} diff --git a/.agents/pm/history/pm-gh1643.jsonl b/.agents/pm/history/pm-gh1643.jsonl index 5ce98596eb..829e79d83b 100644 --- a/.agents/pm/history/pm-gh1643.jsonl +++ b/.agents/pm/history/pm-gh1643.jsonl @@ -34,3 +34,6 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T23:01:53.896Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"replace","path":"/metadata/files/17/path","value":"tests/unit/sdk/lifecycle/claim-attribution.spec.ts"},{"op":"add","path":"/metadata/files/18","value":{"path":"tests/unit/sdk/lifecycle/claim-start.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:01:53.896Z"}],"before_hash":"ff6ddda29e874e14c7003434d097a35b38a7f90e28e10c266551d707ae23fd6b","after_hash":"fc532b444810eb74add7b894d39c62e4fb2149d657b131cc89466ae45c6925c5","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"027024aea2395e19460ec3d8126b49f2ab798c52e8b8369aa67e7bff7b73f72b"} {"hash_algorithm":"sha256","ts":"2026-10-31T23:02:07.901Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:07.900Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:07.901Z"}],"before_hash":"fc532b444810eb74add7b894d39c62e4fb2149d657b131cc89466ae45c6925c5","after_hash":"49047307204c49ea959e67b704a2ae82efca32f73dc3168b1327a0dd6bc7f1b8","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"fe9b990be6bff337cc1cbf1c4affc0d4f2985f124a22cc1ded44c7b16c34c670"} {"hash_algorithm":"sha256","ts":"2026-11-01T01:17:43.802Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-11-01T01:17:43.802Z","author":"harness:claude-code","text":"CodeRabbit review round 2 on PR #1645: runMcpClaimAction now reads next only from the normalized runner options, so nested next:false overrides a flat next:true for both dispatch and the target check. The spec asserts {next:true, options:{next:false,start:true}} is refused with missing_required_argument instead of claiming an item."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:43.802Z"}],"before_hash":"49047307204c49ea959e67b704a2ae82efca32f73dc3168b1327a0dd6bc7f1b8","after_hash":"f677667536f449c94a11eb7302e541871b6f281974896af8ee39fd4015feca8c","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f0cee1913393543d85e9ab86649991c7397be0eff3cca41d65d2531d0b97332f"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:02.437Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:02.437Z"},{"op":"replace","path":"/metadata/title","value":"GH-1643: make pm claim --next --start one atomic select-claim-start write and type the claim-target refusals"}],"before_hash":"f677667536f449c94a11eb7302e541871b6f281974896af8ee39fd4015feca8c","after_hash":"21ec4e3c70d8266e7cd2f03cae34ea103b245919b415613c2e79f9b94e2756b1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"dd0ed5558014412faaf62eda42b01d56829c045dba301cd38eccf63e2f957766"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:51.162Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T02:03:51.160Z","author":"harness:claude-code","text":"cubic review on PR #1645. Fixed: a quoted empty id (pm claim '' --start) now reaches the typed missing_required_argument refusal on CLI and MCP instead of the explicit composition. resolveStartTaskInProgressStatus never returns a configured in-progress status that the registry no longer defines (pm schema remove-status leaves the workflow reference); it falls back to the alias lookup and open status. The runClaim start option's docstring states it is the single-write variant used by ranked selection, while explicit --start keeps the composition. LIFECYCLE_COMMANDS now names the invalid_argument_value refusal for --start with --next or --if-available. Kept as is: MCP id precedence (flat id first) matches every lifecycle action's shared item-id resolution."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:51.162Z"}],"before_hash":"21ec4e3c70d8266e7cd2f03cae34ea103b245919b415613c2e79f9b94e2756b1","after_hash":"5003f860a7ab5335a2a7cb60c542b8a2969b6ce0c36f2dccc8ff101ed2f20305","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e62d9997c9a42b98fd92f3f4f2655dd7941d6ca77c4d2fb2e6111f0d2bab3ff0"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:19:34.344Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7c8cf-1iiij0","kind":"test","status":"passed","started_at":"2026-11-01T02:19:19.242Z","finished_at":"2026-11-01T02:19:34.335Z","recorded_at":"2026-11-01T02:19:34.335Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:19:34.344Z"}],"before_hash":"5003f860a7ab5335a2a7cb60c542b8a2969b6ce0c36f2dccc8ff101ed2f20305","after_hash":"76119df4685f56d3c5b524d2b43b9c675015b139f960618e6d72134d51d18e0d","item_hash_version":3,"message":"Track test run summary (test-local-mvx7c8cf-1iiij0)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a6737bfb5f8555d1695bc8db45c30ffb60c03ebacb7466a8e1e9b2fc1b3b31f3"} diff --git a/.agents/pm/history/pm-gh1644.jsonl b/.agents/pm/history/pm-gh1644.jsonl index 30e60d3153..132312c61d 100644 --- a/.agents/pm/history/pm-gh1644.jsonl +++ b/.agents/pm/history/pm-gh1644.jsonl @@ -24,3 +24,6 @@ {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:33.712Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"topic":{"value":"workset:pm-gh1642+pm-gh1643+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1643","claim:pm-gh1644","claim:pm-hke9z7","lineage:pm-hke9z7","lineage:pm-5k4v","lineage:pm-doxj"]},"version":{"value":"2.1.314","source":"probe"}},"op":"close","patch":[{"op":"replace","path":"/metadata/expected_result","value":"Every row-bounded --for intent either honours an explicit --limit or reports a typed override with the requested and effective values and a retry that keeps the limit; pm's quickstart recommends no silently overridden flag."},{"op":"remove","path":"/metadata/claim_principal"},{"op":"remove","path":"/metadata/assignee"},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:33.712Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-10-31T22:31:33.693Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-10-31T22:31:33.693Z"},{"op":"add","path":"/metadata/resolution","value":"The three row-bounded intent appliers share applyIntentRowLimit, which keeps the budget-derived ceiling and records a reduced request. attachContextIntentReceipt writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning limit_overridden_by_intent:--limit N->M for context, list and search intents, reading either projected or raw host options. The quickstart generator, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and both replayed agent-task transcripts use pm context --for orient. The quickstart no longer calls explicit --start atomic."},{"op":"add","path":"/metadata/actual_result","value":"Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning, and restore_with pm context --for orient --limit 10 --token-budget 7200. That retry returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100. The agent-task token baseline changed only its transcript digest (identical output). intent-limit-override spec 3/3 passes; 3/3 fail with the fix stashed."},{"op":"add","path":"/metadata/close_reason","value":"Fixed: an intent budget that reduces an explicit --limit now discloses the override, and the quickstart recommends the measured canonical orientation"}],"before_hash":"acf2c6ba2acf7f46243970de22e6f6afad9c32684ccd47f71572c9f2cc1827bf","after_hash":"7912de92c4e92670b8de69abe871b765f67f62c3c906b3d5a9a960911409874c","item_hash_version":3,"context":{"completion":{"timestamp":"2026-10-31T22:31:33.693Z","source":"close_time"},"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"96adbb1f390a825d3f60f3490486770f7b6da938c34fa546b500b73bb56f442d"} {"hash_algorithm":"sha256","ts":"2026-10-31T22:31:36.414Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"release-operator","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1644","claim:pm-hke9z7","release:pm-gh1643"]},"topic":{"value":"workset:pm-gh1642+pm-gh1644+pm-hke9z7","source":"inferred","confidence":"medium","rule_version":"v2","evidence":["claim:pm-gh1642","claim:pm-gh1644","claim:pm-hke9z7","release:pm-gh1643"]},"version":{"value":"2.1.314","source":"probe"}},"op":"release","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T22:31:36.414Z"}],"before_hash":"7912de92c4e92670b8de69abe871b765f67f62c3c906b3d5a9a960911409874c","after_hash":"3af5415b0896757a4f376196cef6875357d653157167a31a9fbbaabdb141632a","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"83318603431cc4b37507ee923d6e4821fdc6597bf7dfddb8375bb90bb923e712"} {"hash_algorithm":"sha256","ts":"2026-10-31T23:02:09.740Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-10-31T23:02:09.739Z","author":"harness:claude-code","text":"CodeRabbit review round 1 on PR #1645: limit_override.restore_with is now the flag fragment '--token-budget N' to add to (or replace in) the original command, following the omission-receipt convention (restore_with '--full'). The full rebuilt command had dropped named options (--status, --sort) and inline search field:value filters. token_budget_for_requested and restore_with are omitted when the sufficient budget is not a safe integer (spec covers Number.MAX_SAFE_INTEGER)."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-10-31T23:02:09.740Z"}],"before_hash":"3af5415b0896757a4f376196cef6875357d653157167a31a9fbbaabdb141632a","after_hash":"174e3ee96abb589fe5e2d2ba32fbf8f2c4987bd116f31d50c36cea610ca35e20","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"75cabacce552d87e270cccc683daef73b5c44be0793b01c889f984100aefd4e7"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:02.992Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning limit_overridden_by_intent:--limit 10->3, and limit_override.restore_with '--token-budget 7200' (a flag fragment added to the original command, so every original option and inline filter is kept). Adding that flag returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100 with restore_with '--token-budget 3200'. Both restore fields are omitted when no safe-integer budget can carry the request. The agent-task token baseline changed only its transcript digest (identical output). The intent-limit-override spec passes and fails with the fix stashed."},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:02.992Z"},{"op":"replace","path":"/metadata/title","value":"GH-1644: disclose when a --for intent reduces an explicit --limit and recommend the measured canonical pm context --for orient"}],"before_hash":"174e3ee96abb589fe5e2d2ba32fbf8f2c4987bd116f31d50c36cea610ca35e20","after_hash":"f4014d298d15355ae31aec709cbab3d9d9d25653410e3077059e478c5fc21093","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"1b52d2f197a6599cc3c936014adde4a5f96045d7a7eba1ac0011a43dad536e07"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:52.127Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-11-01T02:03:52.127Z","author":"harness:claude-code","text":"cubic review on PR #1645: the intentRequestedLimit marker option is removed. Every host attaches the receipt with the caller's own options (CLI output context and SDK runtime both pass unprojected options), so the requested limit is read from options.limit and no undeclared option leaks into list/search options or can be spoofed. The conformance doc names limit_override.flag."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:52.127Z"}],"before_hash":"f4014d298d15355ae31aec709cbab3d9d9d25653410e3077059e478c5fc21093","after_hash":"9cbd903eb85dfd87ecf24bcd0860298bec07530d22bace46196fa303a570cbde","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2e81abde9ce67a4f86cf2eedfa1a1c8d89e172a0fd9c985d5468e3dab6d7fb0e"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:19:44.315Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7cg1g-jj5ioa","kind":"test","status":"passed","started_at":"2026-11-01T02:19:34.855Z","finished_at":"2026-11-01T02:19:44.307Z","recorded_at":"2026-11-01T02:19:44.307Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:19:44.315Z"}],"before_hash":"9cbd903eb85dfd87ecf24bcd0860298bec07530d22bace46196fa303a570cbde","after_hash":"a22ae0c682b70a8fe25c887718c6a26bb1c367758c1172a1ac056b8c0552a3f6","item_hash_version":3,"message":"Track test run summary (test-local-mvx7cg1g-jj5ioa)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"5f767e629c1684aca843a3f0cbe8f925f9d231cf374859e0cf0b8422e15fcca1"} diff --git a/.agents/pm/history/pm-hke9z7.jsonl b/.agents/pm/history/pm-hke9z7.jsonl index 077bd7c628..9bd5c50517 100644 --- a/.agents/pm/history/pm-hke9z7.jsonl +++ b/.agents/pm/history/pm-hke9z7.jsonl @@ -16,3 +16,6 @@ {"hash_algorithm":"sha256","ts":"2026-11-01T01:17:41.597Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/4","value":{"path":"tests/unit/sdk/context/filtered-context-edges.spec.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:41.597Z"}],"before_hash":"c3fa8ab01859b337700a5f156c163f06e6c196e4f60926e32555136756bbd60d","after_hash":"7af736b9331ca2b57be0f805a2a21a5ebd0314ba402db51880809fdc628a7e31","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"219095d6b5c6c78752827c40035795b91362f19f8e029c68140b18431ae247d6"} {"hash_algorithm":"sha256","ts":"2026-11-01T01:17:42.401Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"tests_add","patch":[{"op":"add","path":"/metadata/tests/1","value":{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/filtered-context-edges.spec.ts","scope":"project","provenance":{"author":"harness:claude-code","created_at":"2026-11-01T01:17:42.357Z","source_kind":"local_mutation","source_ref":"fix/agent-ux-orient-claim-envelopes"}}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:42.401Z"}],"before_hash":"7af736b9331ca2b57be0f805a2a21a5ebd0314ba402db51880809fdc628a7e31","after_hash":"8590e65550af3120e06bd357066bdc101ace48b38f53575651e99dfacc733c26","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"edc53662518da315e422de6deedbda926395fb5c576868f45c70981d9d396f5f"} {"hash_algorithm":"sha256","ts":"2026-11-01T01:17:43.180Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-11-01T01:17:43.180Z","author":"harness:claude-code","text":"CodeRabbit review round 2 on PR #1645. (1) Major, predates this PR (published 2026.10.31 reproduces via pm context --type Task): when a context filter removed a blocker that declares a reverse blocks edge, the blocked item was reported as not blocked, because the dependency corpus was the filtered list. pm_context now hoists flat filters, which made this reachable from MCP too. runContext keeps the filtered list for displayed rows, scale and workspace memory, but resolves blocker ids, containers, blocker sections and the --parent subtree from an edge corpus. That corpus is the same list when no filter applied, or one unfiltered scan when the list echo shows a selection filter. A side effect fixed: --parent combined with --type Task no longer reports the epic as not found. The filtered-context-edges spec fails with the fix stashed. (2) Hoisting treats a nested snake_case spelling (options.token_budget) as already present, so it wins over a flat tokenBudget."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T01:17:43.180Z"}],"before_hash":"8590e65550af3120e06bd357066bdc101ace48b38f53575651e99dfacc733c26","after_hash":"210873a32c455e25a08fdb4dabb15069d62adba838a5f060cbbcb3f620cece49","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"a241ac9bccaf0db634962579070ff0df56e719ecc29153f138f45bc5285fcd42"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:04.112Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:04.112Z"},{"op":"replace","path":"/metadata/title","value":"MCP read tools read every declared flat parameter, pm_search honours its keywords alias, and filtered pm context keeps dependency edges"}],"before_hash":"210873a32c455e25a08fdb4dabb15069d62adba838a5f060cbbcb3f620cece49","after_hash":"3f87f8f354dc0e5020f1f6da4a25ec79e2c99e4c449d4c56eee6d76634cf0664","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bb811a978b6f9344f653ec8e2113086f0a5277b846ee0e9c9b82cc7604b790b5"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:03:54.628Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T02:03:54.628Z","author":"harness:claude-code","text":"cubic review on PR #1645. The filtered-context edge corpus costs one extra list scan only when a selection filter applied. Measured on this 3,177-item tracker (best of 5), pm context --type Task takes 896 ms on published 2026.10.31 and 901 ms on this branch, and unfiltered context is unchanged. The second scan is served by the list index and OS cache, so the list-loader API was not widened. The spec now also asserts the filter still hides the blocker row."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:54.628Z"}],"before_hash":"3f87f8f354dc0e5020f1f6da4a25ec79e2c99e4c449d4c56eee6d76634cf0664","after_hash":"ceecf45219940c3f09dc3924c2c3ba8bcf3aeb6b194e26f707a1698f774b4fe4","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"eea3787d8f49383942026339a7fc5d761bf3ba8d42ae9a131c9e72d0334a339f"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:20:14.430Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7d39y-av0wrn","kind":"test","status":"passed","started_at":"2026-11-01T02:19:52.407Z","finished_at":"2026-11-01T02:20:14.422Z","recorded_at":"2026-11-01T02:20:14.422Z","passed":2,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"},{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/filtered-context-edges.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:20:14.430Z"}],"before_hash":"ceecf45219940c3f09dc3924c2c3ba8bcf3aeb6b194e26f707a1698f774b4fe4","after_hash":"cab0ac49f52c9709bad82b1a75491ea0b0f29caf9d879afae3026738118999ce","item_hash_version":3,"message":"Track test run summary (test-local-mvx7d39y-av0wrn)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"951543666783043eb3b5d071c0420207846c956715f2cdd4bb3d11e6fa9490ab"} diff --git a/.agents/pm/issues/pm-gh1642.toon b/.agents/pm/issues/pm-gh1642.toon index 26e4804f7e..b8b1896a4a 100644 --- a/.agents/pm/issues/pm-gh1642.toon +++ b/.agents/pm/issues/pm-gh1642.toon @@ -1,12 +1,12 @@ id: pm-gh1642 -title: "GH-1642: brief pm list and pm search pages print an envelope as large as their rows; the default sort and search's default omission receipt are no longer compacted" +title: "GH-1642: compact brief pm list and pm search TOON envelopes while keeping the effective default sort visible" description: "https://github.com/unbraind/pm-cli/issues/1642\n\nReproduced on the current source build (2026.10.31). pm list: projectPmBriefListEnvelope (src/sdk/output/list-envelope.ts, pm-6pycus) only drops the default sorting block when it has exactly the keys sort and order. Sorting later gained effective_keys, so the 7-line default sort is printed on every brief list page — the compaction silently stopped matching its own default. filters echoes limit, which applied_limit already states. pm search has no brief projection at all: it prints truncated (equal to has_more), filters {limit} (equal to applied_limit) and a 4-line omission_receipt whose only group is projection_metadata restorable with --full.\n\nDeliberate exclusions, with reasons: completeness unchecked stays visible (pm-6pycus AC: unchecked and partial source evidence cannot become complete or disappear); the items_encoding block stays because it is the lossless decode contract for the matched_fields list column, and the formatter round-trip tests require json_columns and absent to restore rows; JSON, --full and SDK result types keep the full receipts.\n\nDuplicate search: 'brief envelope list', GH-1501, GH-1642; nearest is pm-6pycus (GH-1501 brief list envelope, closed) which this extends." type: Issue status: closed priority: 2 tags[4]: agent-ux,github,output,token-efficiency created_at: "2026-10-31T21:14:02.437Z" -updated_at: "2026-11-01T01:17:44.606Z" +updated_at: "2026-11-01T02:19:51.917Z" closed_at: "2026-10-31T22:31:34.335Z" completed_at: "2026-10-31T22:31:34.335Z" author: "harness:claude-code" @@ -22,11 +22,12 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-6pycus,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected pm-gh1627,related,"2026-10-31T21:14:02.437Z","harness:claude-code","cli:create:dep",detected pm-zd46m7,related,"2026-10-31T21:49:42.569Z","harness:claude-code","cli:update:dep",detected -comments[4]{created_at,author,text}: +comments[5]{created_at,author,text}: "2026-10-31T21:47:00.908Z","harness:claude-code","Implementation and measurement: projectPmBriefListEnvelope now drops the default ascending sort when it carries effective_keys (the regression), and drops a filters.limit equal to applied_limit and empty runtime_filters. New public projectPmBriefSearchEnvelope (sdk/output) drops truncated when it equals has_more and limit-only filters, and replaces the single default projection_metadata omission receipt with details --full. It is chained after the list projection in formatBuiltInOutput (TOON only). Measured with the published 2026.10.31 package against this branch on this tracker: pm search 'release workflow' --limit 3 drops from 1,033 to 863 B (-16%) and pm list --status open --limit 3 from 988 to 782 B (-21%). completeness unchecked and items_encoding remain by design (see description)." "2026-10-31T21:49:41.855Z","harness:claude-code","Design correction while testing: the 7-line default sort block is not only redundant metadata. GH-1507's accepted contract (pm-zd46m7, integration spec default-list-order) requires brief TOON to disclose the effective default comparator order so an agent knows why rows arrive in that order. Dropping it would undo that decision. The projection therefore drops only the legacy sort/order echo and renders the effective keys on one line, for example sorting.effective_keys 'status_registry:nonterminal_before_terminal asc, priority asc, updated_at desc, id asc'. Seven lines become two, and JSON keeps the full pair array. A malformed or extended sorting block passes through unchanged." "2026-10-31T23:02:11.868Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: the list projection left the raw filters key in place when the compacted filters became empty, so a limit-only or empty-runtime filters block still printed. filters is now destructured out of the envelope before compaction, and specs cover {limit}, {runtime_filters:{}} and a non-echo limit." "2026-11-01T01:17:44.606Z","harness:claude-code","CodeRabbit review round 2 on PR #1645: a final search page omits has_more, truncated, next_cursor and total, so the projection skipped it. projectPmBriefSearchEnvelope now also accepts that terminal shape when count matches the rows, there is no cursor, and any total equals count; inconsistent shapes still pass through. The acceptance criterion now states that the effective default sort order stays visible on one line, matching the resolution." + "2026-11-01T02:03:53.192Z","harness:claude-code","cubic review on PR #1645: the search projection now refuses envelopes that carry a projection block, and the effective_keys line is rendered only for asc/desc comparator pairs (malformed receipts pass through). The SDK doc qualifies the effective-order line as a flat-default-list behaviour, since tree-mode lists carry no effective_keys. The item title now describes the fix, so the generated changelog no longer reads backwards." files[5]{path,scope}: sdk/public-surface.json,project src/core/output/output.ts,project @@ -35,7 +36,7 @@ files[5]{path,scope}: tests/unit/cli/compact-agent-output.spec.ts,project tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts,project,"harness:claude-code","2026-10-31T21:46:35.370Z",local_mutation,fix/agent-ux-orient-claim-envelopes -test_runs[1]: +test_runs[2]: - run_id: test-local-mvwz55qw-51axji kind: test status: passed @@ -47,6 +48,17 @@ test_runs[1]: skipped: 0 executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-mvx7clwi-d2xpmi + kind: test + status: passed + started_at: "2026-11-01T02:19:44.817Z" + finished_at: "2026-11-01T02:19:51.906Z" + recorded_at: "2026-11-01T02:19:51.906Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/cli/compact-agent-output.spec.ts,schema,schema,source,local_source_ref docs[1]{path,scope}: docs/SDK.md,project close_reason: "Fixed: brief pm list and pm search TOON pages drop redundant envelope fields" diff --git a/.agents/pm/issues/pm-gh1643.toon b/.agents/pm/issues/pm-gh1643.toon index 93e0f9bf0f..72bdacf7eb 100644 --- a/.agents/pm/issues/pm-gh1643.toon +++ b/.agents/pm/issues/pm-gh1643.toon @@ -1,12 +1,12 @@ id: pm-gh1643 -title: "GH-1643: pm claim --next --start is refused with 'Specify an item id or pass --next' although --next was passed; select-claim-start needs one atomic write" +title: "GH-1643: make pm claim --next --start one atomic select-claim-start write and type the claim-target refusals" description: "https://github.com/unbraind/pm-cli/issues/1643\n\nReproduced on the current source build (2026.10.31) in a fresh non-git sandbox: `pm claim --next --start` exits 2 with the generic command_failed refusal 'Specify an item id or pass --next' and refusal.surface --next. Root cause: runClaimAction in src/cli/register-operations.ts routes any --start to requireClaimTarget(id, false), which ignores --next and emits the hint to pass the flag already passed. The MCP transport has the same defect: runMcpClaimAction (src/sdk/lifecycle/mcp-actions.ts) routes start to runMcpTaskCompositionAction, which requires an explicit id and refuses with 'Missing required argument: id'.\n\nSelecting, claiming and starting the next item is one agent intent. Today it takes claim --next followed by update --status in_progress: a crash between the two leaves a claimed-but-open item. claim --next already selects and claims under the item lock with an advancing race-loss walk (pm-114v, pm-fjxm). release --handoff already changes status inside the release mutation with enforceTypeWorkflowTransition (src/sdk/lifecycle/release-handoff.ts), so the same pattern gives an atomic claim-and-start.\n\nDuplicate search: 'claim next start', 'refusal suggests flag already provided', GH-1352, GH-1643; nearest are pm-114v and pm-fjxm (claim --next, closed), pm-gh1369 (claim --start compact receipts, closed), pm-ixi1 (provided flag listed as missing, closed) and pm-f05lsg (refusal closure gate, open)." type: Issue status: closed priority: 1 tags[4]: agent-ux,claim,github,refusal created_at: "2026-10-31T21:13:57.945Z" -updated_at: "2026-11-01T01:17:43.802Z" +updated_at: "2026-11-01T02:19:34.344Z" closed_at: "2026-10-31T22:31:19.294Z" completed_at: "2026-10-31T22:31:19.294Z" author: "harness:claude-code" @@ -24,10 +24,11 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-fjxm,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-gh1369,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-ixi1,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected -comments[3]{created_at,author,text}: +comments[4]{created_at,author,text}: "2026-10-31T21:46:58.955Z","harness:claude-code","Implementation: runClaim gained an optional start that calls applyClaimStart inside the same mutateItem callback (the release --handoff pattern). It resolves the in-progress status through resolveStartTaskInProgressStatus, refuses a terminal item with terminal_state_conflict even under --force, enforces type workflows via enforceTypeWorkflowTransition, and returns start {from_status,to_status}. runClaimNext forwards start to every candidate in the race-loss walk, so --if-available and filters behave as before. CLI runClaimAction and MCP runMcpClaimAction route start with an explicit id to the unchanged two-step composition, which keeps its typed 'Remove --next and --if-available' refusal; start without an id uses the atomic path or the new requireClaimTarget in src/sdk/lifecycle/claim-target.ts (missing_required_argument / ambiguous_option_combination, attributed to claim through the catalog generator's module map). runStartTask now strips start before its claim step so explicit --start stays claim-then-update. MCP schema: the start+next exclusion is gone and start+ifAvailable is exclusive only with an id. PmClient.claimNext({start:true}) is typed. Verified in a sandbox: claim --next --start → status in_progress, changed_field_count 3, history create→claim (one event)." "2026-10-31T23:02:07.900Z","harness:claude-code","CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi." "2026-11-01T01:17:43.802Z","harness:claude-code","CodeRabbit review round 2 on PR #1645: runMcpClaimAction now reads next only from the normalized runner options, so nested next:false overrides a flat next:true for both dispatch and the target check. The spec asserts {next:true, options:{next:false,start:true}} is refused with missing_required_argument instead of claiming an item." + "2026-11-01T02:03:51.160Z","harness:claude-code","cubic review on PR #1645. Fixed: a quoted empty id (pm claim '' --start) now reaches the typed missing_required_argument refusal on CLI and MCP instead of the explicit composition. resolveStartTaskInProgressStatus never returns a configured in-progress status that the registry no longer defines (pm schema remove-status leaves the workflow reference); it falls back to the alias lookup and open status. The runClaim start option's docstring states it is the single-write variant used by ranked selection, while explicit --start keeps the composition. LIFECYCLE_COMMANDS now names the invalid_argument_value refusal for --start with --next or --if-available. Kept as is: MCP id precedence (flat id first) matches every lifecycle action's shared item-id resolution." files[19]{path,scope}: scripts/release/surface-replication-sets.json,project sdk/public-surface.json,project @@ -50,7 +51,7 @@ files[19]{path,scope}: tests/unit/sdk/lifecycle/claim-start.spec.ts,project tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,project,"harness:claude-code","2026-10-31T21:46:33.577Z",local_mutation,fix/agent-ux-orient-claim-envelopes -test_runs[1]: +test_runs[2]: - run_id: test-local-mvwz4r6h-yrwyti kind: test status: passed @@ -62,6 +63,17 @@ test_runs[1]: skipped: 0 executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-mvx7c8cf-1iiij0 + kind: test + status: passed + started_at: "2026-11-01T02:19:19.242Z" + finished_at: "2026-11-01T02:19:34.335Z" + recorded_at: "2026-11-01T02:19:34.335Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts,schema,schema,source,local_source_ref docs[2]{path,scope}: docs/COMMANDS.md,project docs/LIFECYCLE_COMMANDS.md,project diff --git a/.agents/pm/issues/pm-gh1644.toon b/.agents/pm/issues/pm-gh1644.toon index e122b1ca1b..309854d7a1 100644 --- a/.agents/pm/issues/pm-gh1644.toon +++ b/.agents/pm/issues/pm-gh1644.toon @@ -1,12 +1,12 @@ id: pm-gh1644 -title: "GH-1644: context --for orient silently reduces an explicit --limit to the intent's budget-derived row ceiling, and pm's own quickstart recommends that command" +title: "GH-1644: disclose when a --for intent reduces an explicit --limit and recommend the measured canonical pm context --for orient" description: "https://github.com/unbraind/pm-cli/issues/1644\n\nReproduced on the current source build (2026.10.31) in a fresh 6-task sandbox and on the real tracker: `pm context --limit 10 --for orient` returns filters.limit \"3\" in either flag order, while `pm context --limit 10` returns 10. The intent applier (CONTEXT_INTENT_DEFAULT_APPLIERS in src/sdk/context-intent-contracts.ts) computes a budget-derived ceiling ((3000 - 1200) / 600 = 3 rows) and replaces the caller's --limit with min(requested, derived). The context_intent receipt reports binding_constraint token_budget but neither the requested value nor a warning; filters.limit shows only the effective 3. list --for triage and search --for discover use the same clamp.\n\nMeasured on the real tracker (3173 items): 3 orient rows serialize to ~1,680 JSON tokens, 5 rows ~2,724, 10 rows ~5,937. The 3000-token preset therefore genuinely cannot carry 10 rows, so the preset ceiling is correct; the defects are (1) the silent override, (2) filters reporting only the effective value, and (3) pm's own agent quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE) recommending `pm context --limit 10 --for orient`, a self-contradicting command. The measured canonical in token-budget-gate is `context --for orient` without --limit.\n\nDuplicate search: 'context orient preset limit', 'for orient intent preset', GH-1644; nearest are pm-gy885b (intent discoverability, closed), pm-3crymx (intent returned no context, closed) and pm-ka6d (canonical cold-start docs, closed)." type: Issue status: closed priority: 1 tags[4]: agent-ux,context,github,token-efficiency created_at: "2026-10-31T21:13:36.728Z" -updated_at: "2026-10-31T23:02:09.740Z" +updated_at: "2026-11-01T02:19:44.315Z" closed_at: "2026-10-31T22:31:33.693Z" completed_at: "2026-10-31T22:31:33.693Z" author: "harness:claude-code" @@ -17,14 +17,15 @@ risk: medium confidence: high resolution: "The three row-bounded intent appliers share applyIntentRowLimit, which keeps the budget-derived ceiling and records a reduced request. attachContextIntentReceipt writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning limit_overridden_by_intent:--limit N->M for context, list and search intents, reading either projected or raw host options. The quickstart generator, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and both replayed agent-task transcripts use pm context --for orient. The quickstart no longer calls explicit --start atomic." expected_result: Every row-bounded --for intent either honours an explicit --limit or reports a typed override with the requested and effective values and a retry that keeps the limit; pm's quickstart recommends no silently overridden flag. -actual_result: "Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning, and restore_with pm context --for orient --limit 10 --token-budget 7200. That retry returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100. The agent-task token baseline changed only its transcript digest (identical output). intent-limit-override spec 3/3 passes; 3/3 fail with the fix stashed." +actual_result: "Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning limit_overridden_by_intent:--limit 10->3, and limit_override.restore_with '--token-budget 7200' (a flag fragment added to the original command, so every original option and inline filter is kept). Adding that flag returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100 with restore_with '--token-budget 3200'. Both restore fields are omitted when no safe-integer budget can carry the request. The agent-task token baseline changed only its transcript digest (identical output). The intent-limit-override spec passes and fails with the fix stashed." dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-3crymx,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected pm-gy885b,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected pm-ka6d,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected -comments[2]{created_at,author,text}: +comments[3]{created_at,author,text}: "2026-10-31T21:46:59.575Z","harness:claude-code","Implementation: the three row-bounded intent appliers share applyIntentRowLimit, which keeps the existing min(requested, budget-derived) clamp but stashes a reduced request as intentRequestedLimit. attachContextIntentReceipt calls discloseIntentLimitOverride, which reads that key or the raw host --limit and writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning 'limit_overridden_by_intent:--limit N->M'. token_budget_for_requested = max(current budget, overhead + rowCost*requested) from the shared resolveIntentRowCost table. Measured on this tracker: pm context --for orient --limit 10 now prints the override with restore_with 'pm context --for orient --limit 10 --token-budget 7200'. That retry returns 10 rows at an estimated 4,637 tokens within budget. list --for triage --limit 150 → effective 100, retry with --token-budget 3200 honours 150. The quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, pm-user ECOSYSTEM_PLANNING) and the replayed agent-task transcript now use the measured canonical pm context --for orient." "2026-10-31T23:02:09.739Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: limit_override.restore_with is now the flag fragment '--token-budget N' to add to (or replace in) the original command, following the omission-receipt convention (restore_with '--full'). The full rebuilt command had dropped named options (--status, --sort) and inline search field:value filters. token_budget_for_requested and restore_with are omitted when the sufficient budget is not a safe integer (spec covers Number.MAX_SAFE_INTEGER)." + "2026-11-01T02:03:52.127Z","harness:claude-code","cubic review on PR #1645: the intentRequestedLimit marker option is removed. Every host attaches the receipt with the caller's own options (CLI output context and SDK runtime both pass unprojected options), so the requested limit is read from options.limit and no undeclared option leaks into list/search options or can be spoofed. The conformance doc names limit_override.flag." files[10]{path,scope}: docs/agent-task-token-baseline.json,project docs/agent-task-transcripts.json,project @@ -38,7 +39,7 @@ files[10]{path,scope}: tests/unit/sdk/recovery/recovery-guidance.spec.ts,project tests[1]{command,scope,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts,project,"harness:claude-code","2026-10-31T21:46:34.470Z",local_mutation,fix/agent-ux-orient-claim-envelopes -test_runs[1]: +test_runs[2]: - run_id: test-local-mvwz4yu4-gbikjl kind: test status: passed @@ -50,6 +51,17 @@ test_runs[1]: skipped: 0 executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-mvx7cg1g-jj5ioa + kind: test + status: passed + started_at: "2026-11-01T02:19:34.855Z" + finished_at: "2026-11-01T02:19:44.307Z" + recorded_at: "2026-11-01T02:19:44.307Z" + passed: 1 + failed: 0 + skipped: 0 + executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts,schema,schema,source,local_source_ref docs[6]{path,scope}: .agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md,project AGENTS.md,project diff --git a/.agents/pm/issues/pm-hke9z7.toon b/.agents/pm/issues/pm-hke9z7.toon index 02a98b6ee8..4ca9b22116 100644 --- a/.agents/pm/issues/pm-hke9z7.toon +++ b/.agents/pm/issues/pm-hke9z7.toon @@ -1,12 +1,12 @@ id: pm-hke9z7 -title: "MCP read tools silently ignore declared flat parameters: pm_context reads none, and for/tokenBudget/depth on pm_list, pm_search, pm_next and pm_get never reach the intent layer" +title: "MCP read tools read every declared flat parameter, pm_search honours its keywords alias, and filtered pm context keeps dependency edges" description: "Discovered while verifying pm-gh1644 over MCP (published 2026.10.31 and current source both reproduce).\n\nEvery narrow MCP read tool declares its action parameters flat in its inputSchema (pm_context declares for, limit, depth, section, fields, tokenBudget, ...; pm_list/pm_search/pm_next/pm_get declare for and tokenBudget). The SDK runtime only moves a flat argument into the options record the handlers read when it appears in the hand-maintained HOISTED_ACTION_OPTION_KEYS table in src/sdk/runtime-input.ts. That table has no context entry at all and omits for/tokenBudget (and most filters) from list, search, next and get. A flat argument outside the table is accepted without a warning (it is declared) and then dropped.\n\nMeasured in a fresh sandbox through the MCP tools/call handler, flat vs nested options: pm_context {depth: deep} → depth brief (nested → deep); pm_context {for: orient, limit: 10} → no intent, limit null, token_budget 1600 (nested → orient, limit 3); pm_list {for: triage}, pm_search {query, for: discover}, pm_next {for: execute}, pm_get {id, for: inspect} → no context_intent receipt (nested → applied); pm_next {limit: 1} → 5 rows (nested → 1). The declared intent layer is therefore unreachable for MCP hosts that send the flat arguments the schema advertises. Same defect family as pm-wcaa (pm_files declared-but-ignored params, closed individually) and pm-hno5.\n\nDuplicate search: 'MCP pm_context flat arguments ignored options', 'MCP declared parameter silently ignored', 'mcp for intent not applied' — no open item." type: Issue status: closed priority: 1 tags[4]: agent-ux,context,contracts,mcp created_at: "2026-10-31T21:27:47.547Z" -updated_at: "2026-11-01T01:17:43.180Z" +updated_at: "2026-11-01T02:20:14.430Z" closed_at: "2026-10-31T22:31:35.081Z" completed_at: "2026-10-31T22:31:35.081Z" author: "harness:claude-code" @@ -22,10 +22,11 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-gh1644,discovered_from,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected pm-hno5,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected pm-wcaa,related,"2026-10-31T21:27:47.547Z","harness:claude-code","cli:create:dep",detected -comments[3]{created_at,author,text}: +comments[4]{created_at,author,text}: "2026-10-31T21:47:01.751Z","harness:claude-code","Implementation: runtime-input.ts derives hoisting for context, get, list, next and search from PM_TOOL_ACTION_PARAMETER_CONTRACTS[action].optional (declaredReadActionOptionKeys), after the hand table, so nested options still win. Verified through the MCP tools/call handler: flat and nested calls now agree for pm_context depth/for/limit, pm_list/pm_search/pm_next/pm_get for, pm_get depth and pm_next limit. The regression spec iterates every declared optional key of the five actions, so a new declared parameter can no longer be silently dropped." "2026-10-31T23:02:13.786Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: the docs now state that search's query operand (and its keywords alias) stays top-level like pm_get's id, while every other declared optional parameter is accepted flat or under options." "2026-11-01T01:17:43.180Z","harness:claude-code","CodeRabbit review round 2 on PR #1645. (1) Major, predates this PR (published 2026.10.31 reproduces via pm context --type Task): when a context filter removed a blocker that declares a reverse blocks edge, the blocked item was reported as not blocked, because the dependency corpus was the filtered list. pm_context now hoists flat filters, which made this reachable from MCP too. runContext keeps the filtered list for displayed rows, scale and workspace memory, but resolves blocker ids, containers, blocker sections and the --parent subtree from an edge corpus. That corpus is the same list when no filter applied, or one unfiltered scan when the list echo shows a selection filter. A side effect fixed: --parent combined with --type Task no longer reports the epic as not found. The filtered-context-edges spec fails with the fix stashed. (2) Hoisting treats a nested snake_case spelling (options.token_budget) as already present, so it wins over a flat tokenBudget." + "2026-11-01T02:03:54.628Z","harness:claude-code","cubic review on PR #1645. The filtered-context edge corpus costs one extra list scan only when a selection filter applied. Measured on this 3,177-item tracker (best of 5), pm context --type Task takes 896 ms on published 2026.10.31 and 901 ms on this branch, and unfiltered context is unchanged. The second scan is served by the list index and OS cache, so the list-loader API was not widened. The spec now also asserts the filter still hides the blocker row." files[5]{path,scope}: src/sdk/query/context.ts,project src/sdk/runtime-input.ts,project @@ -35,7 +36,7 @@ files[5]{path,scope}: tests[2]{command,scope,provenance{author,created_at,source_kind,source_ref}}: node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts,project,"harness:claude-code","2026-10-31T21:46:36.177Z",local_mutation,fix/agent-ux-orient-claim-envelopes node scripts/run-tests.mjs test -- tests/unit/sdk/context/filtered-context-edges.spec.ts,project,"harness:claude-code","2026-11-01T01:17:42.357Z",local_mutation,fix/agent-ux-orient-claim-envelopes -test_runs[1]: +test_runs[2]: - run_id: test-local-mvwz5gjx-0ayygj kind: test status: passed @@ -47,6 +48,18 @@ test_runs[1]: skipped: 0 executions[1]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts,schema,schema,source,local_source_ref + - run_id: test-local-mvx7d39y-av0wrn + kind: test + status: passed + started_at: "2026-11-01T02:19:52.407Z" + finished_at: "2026-11-01T02:20:14.422Z" + recorded_at: "2026-11-01T02:20:14.422Z" + passed: 2 + failed: 0 + skipped: 0 + executions[2]{command,requested_pm_context_mode,pm_context_mode,workspace_context_mode,trust_reason}: + node scripts/run-tests.mjs test -- tests/unit/mcp/read-tool-flat-parameters.spec.ts,schema,schema,source,local_source_ref + node scripts/run-tests.mjs test -- tests/unit/sdk/context/filtered-context-edges.spec.ts,schema,schema,source,local_source_ref docs[1]{path,scope}: docs/SDK_ACTION_CONFORMANCE.md,project close_reason: "Fixed: MCP read tools read every declared flat parameter and pm_search honours its keywords alias" diff --git a/CHANGELOG.md b/CHANGELOG.md index 32e5aee62e..5369a95512 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,10 @@ ### Fixed -- MCP read tools silently ignore declared flat parameters: pm_context reads none, and for/tokenBudget/depth on pm_list, pm_search, pm_next and pm_get never reach the intent layer ([pm-hke9z7](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-hke9z7.toon)) -- GH-1642: brief pm list and pm search pages print an envelope as large as their rows; the default sort and search's default omission receipt are no longer compacted ([pm-gh1642](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1642.toon)) -- GH-1644: context --for orient silently reduces an explicit --limit to the intent's budget-derived row ceiling, and pm's own quickstart recommends that command ([pm-gh1644](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1644.toon)) -- GH-1643: pm claim --next --start is refused with 'Specify an item id or pass --next' although --next was passed; select-claim-start needs one atomic write ([pm-gh1643](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1643.toon)) +- MCP read tools read every declared flat parameter, pm_search honours its keywords alias, and filtered pm context keeps dependency edges ([pm-hke9z7](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-hke9z7.toon)) +- GH-1642: compact brief pm list and pm search TOON envelopes while keeping the effective default sort visible ([pm-gh1642](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1642.toon)) +- GH-1644: disclose when a --for intent reduces an explicit --limit and recommend the measured canonical pm context --for orient ([pm-gh1644](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1644.toon)) +- GH-1643: make pm claim --next --start one atomic select-claim-start write and type the claim-target refusals ([pm-gh1643](https://github.com/unbraind/pm-cli/blob/main/.agents/pm/issues/pm-gh1643.toon)) ## 2026.10.31 - 2026-10-31 diff --git a/docs/LIFECYCLE_COMMANDS.md b/docs/LIFECYCLE_COMMANDS.md index b074a559c1..1bcd8c6f56 100644 --- a/docs/LIFECYCLE_COMMANDS.md +++ b/docs/LIFECYCLE_COMMANDS.md @@ -42,9 +42,11 @@ locked mutation and one `claim` history event, so no other agent can observe the claimed item before it starts. Configured type workflows are enforced, a terminal candidate is refused (`terminal_state_conflict`), and the receipt carries `start: { from_status, to_status }`. A claim without an id or `--next` -is refused with `missing_required_argument`, and an id combined with `--next` -with `ambiguous_option_combination`; neither refusal names a flag the caller -already passed. `release --pause` moves work to the configured +is refused with `missing_required_argument`, and `claim --next` with +`ambiguous_option_combination`; their next steps never tell the caller to pass +a flag that is already present. `claim --start --next` (or +`--if-available`) is refused by the explicit-start composition with +`invalid_argument_value`, whose recovery names the flags to remove. `release --pause` moves work to the configured open status, then releases ownership. `close --release-assignment` records the close and its inline evidence, then releases ownership. diff --git a/docs/SDK.md b/docs/SDK.md index f00d8bc37c..6d50437470 100644 --- a/docs/SDK.md +++ b/docs/SDK.md @@ -2143,9 +2143,11 @@ continuation, and leaves partial, custom, or inconsistent envelopes intact. `projectPmBriefSearchEnvelope` does the same for search pages. It drops `truncated` when it repeats `has_more` and a `filters.limit` echo of `applied_limit`, and it replaces the single default `projection_metadata` -omission receipt with `details: "--full"`. The list projection replaces the -default ascending sort's legacy `sort`/`order` echo with one -`effective_keys` comparator line, so the effective default order stays visible. +omission receipt with `details: "--full"`. For flat default lists, the list +projection replaces the default ascending sort's legacy `sort`/`order` echo with +one `effective_keys` comparator line, so the effective default order stays +visible. Tree-mode lists carry no `effective_keys`, because hierarchy traversal +overrides flat ordering; there the default sort echo is simply omitted. The CLI applies these projections only to TOON; ordinary JSON and SDK query results retain their complete contracts. The public `encodePmTableRows` and `decodePmTableRows` primitives distinguish explicit null and empty cells from diff --git a/docs/SDK_ACTION_CONFORMANCE.md b/docs/SDK_ACTION_CONFORMANCE.md index e757926877..671fac138b 100644 --- a/docs/SDK_ACTION_CONFORMANCE.md +++ b/docs/SDK_ACTION_CONFORMANCE.md @@ -92,8 +92,9 @@ ceiling. For row-oriented `list` and `search` intents, pm reports: When the intent budget reduces an explicit `--limit`, as with `pm context --for orient --limit 10` (the 3000-token orient budget carries 3 -rows), the reduction is never silent. `context_intent.limit_override` records -`requested`, `effective`, the `token_budget_for_requested` that would carry the +rows), the reduction is never silent. `context_intent.limit_override` records the +reduced `flag` (`"--limit"`), `requested`, `effective`, the +`token_budget_for_requested` that would carry the request, and `restore_with`: the flag to add to (or replace in) the original command, so every original positional, inline filter, and option is kept. Both fields are omitted when no safe-integer budget can carry the request. `filters.requested_limit` sits beside the diff --git a/sdk/public-surface.json b/sdk/public-surface.json index 38678bf2aa..8e22d8434f 100644 --- a/sdk/public-surface.json +++ b/sdk/public-surface.json @@ -2985,14 +2985,6 @@ ], "package_version": "2026.10.31", "reason": "Optional additions only: ClaimResult.start (claim --next --start receipt), ClaimNextOptions.start, and PmContextIntentReceipt.limit_override (GH-1643/GH-1644); no existing field changed type or became required." - }, - { - "changes": [ - "signature ./sdk:PmContextIntentLimitOverride", - "signature ./sdk/contracts:PmContextIntentLimitOverride" - ], - "package_version": "2026.10.31", - "reason": "PmContextIntentLimitOverride is new in this PR: token_budget_for_requested and restore_with become optional because they are omitted when no safe-integer budget can carry the request, and restore_with is now a flag fragment (review feedback)." } ], "entrypoints": { diff --git a/src/cli/register-operations.ts b/src/cli/register-operations.ts index 1370faa38a..4ac2e8aa0c 100644 --- a/src/cli/register-operations.ts +++ b/src/cli/register-operations.ts @@ -715,7 +715,7 @@ async function runClaimAction( ): Promise { const next = options.next === true; const start = options.start === true; - if (start && id !== undefined) { + if (start && id) { // The explicit-item composition refuses --next/--if-available with its own typed recovery. await runStartTaskAction(id, options, command); return; diff --git a/src/sdk/context-intent-contracts.ts b/src/sdk/context-intent-contracts.ts index 9757d7731a..67229be86b 100644 --- a/src/sdk/context-intent-contracts.ts +++ b/src/sdk/context-intent-contracts.ts @@ -404,10 +404,7 @@ type BuiltInContextIntentCommand = | "next" | "search"; -/** Option key carrying an explicit row limit that the intent budget reduced, for receipt disclosure. */ -const INTENT_REQUESTED_LIMIT_OPTION = "intentRequestedLimit"; - -/** Bound an explicit or absent row limit by the intent's budget-derived ceiling, remembering a reduced request. */ +/** Bound an explicit or absent row limit by the intent's budget-derived ceiling. */ function applyIntentRowLimit( projected: Record, budgetDerivedLimit: number, @@ -421,9 +418,6 @@ function applyIntentRowLimit( if (projected.limit === undefined || requestedLimit !== undefined) { projected.limit = effectiveLimit; } - if (requestedLimit !== undefined && requestedLimit > budgetDerivedLimit) { - projected[INTENT_REQUESTED_LIMIT_OPTION] = String(requestedLimit); - } return effectiveLimit; } @@ -1065,10 +1059,8 @@ function discloseIntentLimitOverride( result: Record, options: Record, ): void { - // Projected options carry the reduced request; raw host options still carry the caller's limit. - const requested = parsePositiveIntentTokenBudget( - options[INTENT_REQUESTED_LIMIT_OPTION] ?? options.limit, - ); + // Hosts attach receipts with the caller's own options, which still carry the requested limit. + const requested = parsePositiveIntentTokenBudget(options.limit); const effective = receipt.budget_derived_limit!; if (requested === undefined || requested <= effective) return; const { overhead, rowCost } = resolveIntentRowCost( diff --git a/src/sdk/lifecycle/claim.ts b/src/sdk/lifecycle/claim.ts index c6de5054cc..5a322578b7 100644 --- a/src/sdk/lifecycle/claim.ts +++ b/src/sdk/lifecycle/claim.ts @@ -162,7 +162,12 @@ export interface ClaimMutationOptions { ifAvailable?: boolean; /** Maximum ranked candidates attempted by `claim --next`. */ maxAttempts?: string | number; - /** Move the claimed item to the configured in-progress status in the same locked mutation. */ + /** + * Move the claimed item to the configured in-progress status in the same locked + * mutation and history event. `runClaimNext` uses this for `claim --next --start`. + * The explicit-item `claim --start` and `runStartTask` keep their documented + * claim-then-update composition and do not pass it. + */ start?: boolean; } diff --git a/src/sdk/lifecycle/mcp-actions.ts b/src/sdk/lifecycle/mcp-actions.ts index 4df4922f4a..40be11e6d9 100644 --- a/src/sdk/lifecycle/mcp-actions.ts +++ b/src/sdk/lifecycle/mcp-actions.ts @@ -36,7 +36,7 @@ export async function runMcpClaimAction(context: LifecycleMutationActionContext) const next = runnerOptions.next === true; const id = context.id ?? readRuntimeString(runnerOptions, "id"); if (selectionOptions.start === true) { - if (id !== undefined) return runMcpTaskCompositionAction(context, "start_task"); + if (id) return runMcpTaskCompositionAction(context, "start_task"); requireClaimTarget(id, next, true); } const result = next diff --git a/src/sdk/output/list-envelope.ts b/src/sdk/output/list-envelope.ts index b7ca717afc..8e3fc02ae9 100644 --- a/src/sdk/output/list-envelope.ts +++ b/src/sdk/output/list-envelope.ts @@ -144,7 +144,7 @@ function isSortKeyPair(value: unknown): value is [string, string] { Array.isArray(value) && value.length === 2 && typeof value[0] === "string" && - typeof value[1] === "string" + (value[1] === "asc" || value[1] === "desc") ); } @@ -207,6 +207,7 @@ export function projectPmBriefSearchEnvelope(value: unknown): unknown { typeof value.query !== "string" || !Array.isArray(value.items) || Object.hasOwn(value, "details") || + Object.hasOwn(value, "projection") || !( hasStandardPagination(value, value.items.length) || hasTerminalSearchPagination(value, value.items.length) diff --git a/src/sdk/start-task-status.ts b/src/sdk/start-task-status.ts index c3cec21645..59a357743f 100644 --- a/src/sdk/start-task-status.ts +++ b/src/sdk/start-task-status.ts @@ -12,7 +12,11 @@ import { export function resolveStartTaskInProgressStatus( statusRegistry: RuntimeStatusRegistry, ): string { - if (statusRegistry.in_progress_status !== statusRegistry.open_status) { + // A workflow may still name a status that `pm schema remove-status` deleted; never start into an unregistered status. + if ( + statusRegistry.in_progress_status !== statusRegistry.open_status && + statusRegistry.by_id.has(statusRegistry.in_progress_status) + ) { return statusRegistry.in_progress_status; } return ( diff --git a/tests/unit/cli/compact-agent-output.spec.ts b/tests/unit/cli/compact-agent-output.spec.ts index 238c2289be..ee43bd0ae6 100644 --- a/tests/unit/cli/compact-agent-output.spec.ts +++ b/tests/unit/cli/compact-agent-output.spec.ts @@ -418,7 +418,7 @@ describe("GH-1642 brief search and list envelopes", () => { }); it("keeps filters that carry information and drops only limit echoes and empty runtime filters", () => { - expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { type: "Task", limit: "2" } })).toMatchObject({ filters: { type: "Task" } }); + expect((projectPmBriefSearchEnvelope({ ...searchPage, filters: { type: "Task", limit: "2" } }) as Record).filters).toEqual({ type: "Task" }); expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: { limit: "5" } })).toMatchObject({ filters: { limit: "5" } }); expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: null })).toMatchObject({ filters: null }); expect(projectPmBriefSearchEnvelope({ ...searchPage, filters: undefined })).not.toHaveProperty("filters"); @@ -450,6 +450,7 @@ describe("GH-1642 brief search and list envelopes", () => { { ...searchPage, query: 1 }, { ...searchPage, items: null }, { ...searchPage, details: "producer" }, + { ...searchPage, projection: { mode: "full", fields: null } }, { ...searchPage, truncated: false }, { ...searchPage, omission_receipt: { ...searchPage.omission_receipt, omitted_field_groups: [{ name: "full_item_fields", restore_with: "--full" }] } }, { ...searchPage, omission_receipt: { ...searchPage.omission_receipt, omitted_field_group_count: 2 } }, @@ -475,6 +476,7 @@ describe("GH-1642 brief search and list envelopes", () => { { ...value.sorting, provider: "keep" }, { ...value.sorting, effective_keys: "priority" }, { ...value.sorting, effective_keys: [["priority"]] }, + { ...value.sorting, effective_keys: [["priority", "sideways"]] }, ]) { expect(projectPmBriefListEnvelope({ ...value, sorting })).toMatchObject({ sorting }); } diff --git a/tests/unit/commands/register-commands.spec.ts b/tests/unit/commands/register-commands.spec.ts index a777ecdcef..bf80b3fce9 100644 --- a/tests/unit/commands/register-commands.spec.ts +++ b/tests/unit/commands/register-commands.spec.ts @@ -959,6 +959,7 @@ describe("operation command actions", () => { expect.objectContaining({ type: "Task" }), ); await expect(runCli("claim", "--start")).rejects.toThrow("pm claim --start needs an item id"); + await expect(runCli("claim", "", "--start")).rejects.toThrow("pm claim --start needs an item id"); }); it("routes claim --next through ranked atomic selection", async () => { @@ -1470,6 +1471,13 @@ describe("operation command actions", () => { } as never); expect(aliased.in_progress_status).toBe("queued"); expect(resolveStartTaskInProgressStatus(aliased)).toBe("building"); + // A workflow still naming a removed status never starts into it. + const dangling = resolveRuntimeStatusRegistry({ + statuses: [{ id: "todo", roles: ["active", "default_open"] }, ...terminal], + workflow: { open_status: "todo", in_progress_status: "working", close_status: "done", canceled_status: "dropped" }, + } as never); + expect(dangling.in_progress_status).toBe("working"); + expect(resolveStartTaskInProgressStatus(dangling)).toBe("todo"); }); it("maps stats, gc, and contracts options", async () => { diff --git a/tests/unit/sdk/context/filtered-context-edges.spec.ts b/tests/unit/sdk/context/filtered-context-edges.spec.ts index 514e7fbe05..bc8279a27e 100644 --- a/tests/unit/sdk/context/filtered-context-edges.spec.ts +++ b/tests/unit/sdk/context/filtered-context-edges.spec.ts @@ -14,8 +14,10 @@ describe("filtered context keeps dependency edges outside the filter", () => { await runUpdate(blocker, { dep: [`id=${task},kind=blocks`] }, global); for (const options of [{}, { type: "Task" }]) { const result = await runContext(options, global); - const row = [...result.high_level, ...result.low_level].find((item) => item.id === task); - expect(row, JSON.stringify(options)).toMatchObject({ blocked: true, blocker_ids: [blocker] }); + const rows = [...result.high_level, ...result.low_level]; + expect(rows.find((item) => item.id === task), JSON.stringify(options)).toMatchObject({ blocked: true, blocker_ids: [blocker] }); + // The filter still excludes the blocker's own row; only its edge is resolved. + expect(rows.some((item) => item.id === blocker)).toBe(Object.keys(options).length === 0); } const scoped = await runContext({ type: "Task", parent: epic }, global); expect([...scoped.high_level, ...scoped.low_level].map((item) => item.id)).toEqual([task]); diff --git a/tests/unit/sdk/context/intent-limit-override.spec.ts b/tests/unit/sdk/context/intent-limit-override.spec.ts index 18d84b530f..45aa6893b1 100644 --- a/tests/unit/sdk/context/intent-limit-override.spec.ts +++ b/tests/unit/sdk/context/intent-limit-override.spec.ts @@ -6,21 +6,18 @@ import { } from "../../../../src/sdk/context-intent-contracts.js"; describe("GH-1644 intent budgets never silently reduce an explicit --limit", () => { - it("remembers a reduced explicit limit only when the budget actually reduced it", () => { - expect(applyContextIntentProjection("context", { for: "orient", limit: "10" })).toMatchObject({ - limit: "3", - activityLimit: "3", - intentRequestedLimit: "10", - }); - expect(applyContextIntentProjection("context", { for: "orient", limit: "2" })).not.toHaveProperty("intentRequestedLimit"); - expect(applyContextIntentProjection("context", { for: "orient" })).not.toHaveProperty("intentRequestedLimit"); - expect(applyContextIntentProjection("list", { for: "triage", limit: "150" })).toMatchObject({ limit: "100", intentRequestedLimit: "150" }); - expect(applyContextIntentProjection("search", { for: "discover", limit: "200" }, ["q"])).toMatchObject({ limit: "82", intentRequestedLimit: "200" }); + it("bounds an explicit limit by the budget without adding undeclared options", () => { + const projected = applyContextIntentProjection("context", { for: "orient", limit: "10" }); + expect(projected).toMatchObject({ limit: "3", activityLimit: "3" }); + expect(Object.keys(projected).sort()).toEqual(["activityLimit", "depth", "for", "limit", "section", "tokenBudget"]); + expect(applyContextIntentProjection("context", { for: "orient", limit: "2" })).toMatchObject({ limit: "2" }); + expect(applyContextIntentProjection("list", { for: "triage", limit: "150" })).toMatchObject({ limit: "100" }); + expect(applyContextIntentProjection("search", { for: "discover", limit: "200" }, ["q"])).toMatchObject({ limit: "82" }); }); it("discloses the reduction in the receipt, warnings and filters with a retry that keeps the limit", () => { - const options = applyContextIntentProjection("context", { for: "orient", limit: "10" }); - const result = attachContextIntentReceipt("context", options, { + // Hosts attach the receipt with the caller's own options, as the CLI output path and SDK runtime do. + const result = attachContextIntentReceipt("context", { for: "orient", limit: "10" }, { filters: { limit: "3" }, high_level: [], low_level: [], @@ -44,7 +41,6 @@ describe("GH-1644 intent budgets never silently reduce an explicit --limit", () }); const retry = applyContextIntentProjection("context", { for: "orient", limit: "10", tokenBudget: "7200" }); expect(retry).toMatchObject({ limit: "10" }); - expect(retry).not.toHaveProperty("intentRequestedLimit"); }); it("reads the caller's limit from raw host options and restores it with a flag the original command keeps", () => { From e464087a8a77f82ab98ab54f8291d472870bb6f8 Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 1 Nov 2026 03:42:20 +0100 Subject: [PATCH 5/6] =?UTF-8?q?docs(agent-ux):=20address=20review=20round?= =?UTF-8?q?=203=20=E2=80=94=20ranking=20precedes=20the=20claim=20lock,=20a?= =?UTF-8?q?ccurate=20GH-1644=20criterion,=20reject=20the=20old=20orient=20?= =?UTF-8?q?command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs now say claim --next --start ranks candidates before any lock, then re-reads each candidate under its item lock for the single claim-and-start write. The pm-gh1644 acceptance criterion describes the shipped receipt: limit_override.requested, the warning without a budget, and restore fields omitted for unsafe budgets. The guidance tests assert that the old 'pm context --limit 10' command is absent. --- .agents/pm/history/pm-gh1643.jsonl | 1 + .agents/pm/history/pm-gh1644.jsonl | 2 ++ .agents/pm/issues/pm-gh1643.toon | 5 +++-- .agents/pm/issues/pm-gh1644.toon | 7 ++++--- docs/COMMANDS.md | 2 +- docs/LIFECYCLE_COMMANDS.md | 12 +++++++----- tests/integration/cli.integration.spec.ts | 3 ++- tests/unit/commands/workspace/init-command.spec.ts | 4 +++- 8 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.agents/pm/history/pm-gh1643.jsonl b/.agents/pm/history/pm-gh1643.jsonl index 829e79d83b..762da83574 100644 --- a/.agents/pm/history/pm-gh1643.jsonl +++ b/.agents/pm/history/pm-gh1643.jsonl @@ -37,3 +37,4 @@ {"hash_algorithm":"sha256","ts":"2026-11-01T02:03:02.437Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:02.437Z"},{"op":"replace","path":"/metadata/title","value":"GH-1643: make pm claim --next --start one atomic select-claim-start write and type the claim-target refusals"}],"before_hash":"f677667536f449c94a11eb7302e541871b6f281974896af8ee39fd4015feca8c","after_hash":"21ec4e3c70d8266e7cd2f03cae34ea103b245919b415613c2e79f9b94e2756b1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"dd0ed5558014412faaf62eda42b01d56829c045dba301cd38eccf63e2f957766"} {"hash_algorithm":"sha256","ts":"2026-11-01T02:03:51.162Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T02:03:51.160Z","author":"harness:claude-code","text":"cubic review on PR #1645. Fixed: a quoted empty id (pm claim '' --start) now reaches the typed missing_required_argument refusal on CLI and MCP instead of the explicit composition. resolveStartTaskInProgressStatus never returns a configured in-progress status that the registry no longer defines (pm schema remove-status leaves the workflow reference); it falls back to the alias lookup and open status. The runClaim start option's docstring states it is the single-write variant used by ranked selection, while explicit --start keeps the composition. LIFECYCLE_COMMANDS now names the invalid_argument_value refusal for --start with --next or --if-available. Kept as is: MCP id precedence (flat id first) matches every lifecycle action's shared item-id resolution."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:51.162Z"}],"before_hash":"21ec4e3c70d8266e7cd2f03cae34ea103b245919b415613c2e79f9b94e2756b1","after_hash":"5003f860a7ab5335a2a7cb60c542b8a2969b6ce0c36f2dccc8ff101ed2f20305","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e62d9997c9a42b98fd92f3f4f2655dd7941d6ca77c4d2fb2e6111f0d2bab3ff0"} {"hash_algorithm":"sha256","ts":"2026-11-01T02:19:34.344Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7c8cf-1iiij0","kind":"test","status":"passed","started_at":"2026-11-01T02:19:19.242Z","finished_at":"2026-11-01T02:19:34.335Z","recorded_at":"2026-11-01T02:19:34.335Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/lifecycle/claim-start.spec.ts tests/integration/lifecycle-composition-sdk.spec.ts tests/integration/lifecycle-receipts.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:19:34.344Z"}],"before_hash":"5003f860a7ab5335a2a7cb60c542b8a2969b6ce0c36f2dccc8ff101ed2f20305","after_hash":"76119df4685f56d3c5b524d2b43b9c675015b139f960618e6d72134d51d18e0d","item_hash_version":3,"message":"Track test run summary (test-local-mvx7c8cf-1iiij0)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a6737bfb5f8555d1695bc8db45c30ffb60c03ebacb7466a8e1e9b2fc1b3b31f3"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:39:19.500Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/4","value":{"created_at":"2026-11-01T02:39:19.500Z","author":"harness:claude-code","text":"CodeRabbit review round 3 on PR #1645: the docs now say ranking happens before any lock, and each candidate is re-read under its item lock before the single claim-and-start write. The selection itself was never atomic."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:39:19.500Z"}],"before_hash":"76119df4685f56d3c5b524d2b43b9c675015b139f960618e6d72134d51d18e0d","after_hash":"78f1f5b54aff095f719c3f9ddb67a91697cbfe42583ea0cbaad2c60e13b5d968","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"c48097ec83be33965058aea81a59b34233db088c816cba7c32d7ab481da6e5ec"} diff --git a/.agents/pm/history/pm-gh1644.jsonl b/.agents/pm/history/pm-gh1644.jsonl index 132312c61d..91ec665e94 100644 --- a/.agents/pm/history/pm-gh1644.jsonl +++ b/.agents/pm/history/pm-gh1644.jsonl @@ -27,3 +27,5 @@ {"hash_algorithm":"sha256","ts":"2026-11-01T02:03:02.992Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/actual_result","value":"Real tracker: pm context --for orient --limit 10 prints filters.requested_limit 10 beside limit 3, the warning limit_overridden_by_intent:--limit 10->3, and limit_override.restore_with '--token-budget 7200' (a flag fragment added to the original command, so every original option and inline filter is kept). Adding that flag returns 10 rows at 4,637 estimated tokens within budget. list --for triage --limit 150 discloses 150->100 with restore_with '--token-budget 3200'. Both restore fields are omitted when no safe-integer budget can carry the request. The agent-task token baseline changed only its transcript digest (identical output). The intent-limit-override spec passes and fails with the fix stashed."},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:02.992Z"},{"op":"replace","path":"/metadata/title","value":"GH-1644: disclose when a --for intent reduces an explicit --limit and recommend the measured canonical pm context --for orient"}],"before_hash":"174e3ee96abb589fe5e2d2ba32fbf8f2c4987bd116f31d50c36cea610ca35e20","after_hash":"f4014d298d15355ae31aec709cbab3d9d9d25653410e3077059e478c5fc21093","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"1b52d2f197a6599cc3c936014adde4a5f96045d7a7eba1ac0011a43dad536e07"} {"hash_algorithm":"sha256","ts":"2026-11-01T02:03:52.127Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-11-01T02:03:52.127Z","author":"harness:claude-code","text":"cubic review on PR #1645: the intentRequestedLimit marker option is removed. Every host attaches the receipt with the caller's own options (CLI output context and SDK runtime both pass unprojected options), so the requested limit is read from options.limit and no undeclared option leaks into list/search options or can be spoofed. The conformance doc names limit_override.flag."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:03:52.127Z"}],"before_hash":"f4014d298d15355ae31aec709cbab3d9d9d25653410e3077059e478c5fc21093","after_hash":"9cbd903eb85dfd87ecf24bcd0860298bec07530d22bace46196fa303a570cbde","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"2e81abde9ce67a4f86cf2eedfa1a1c8d89e172a0fd9c985d5468e3dab6d7fb0e"} {"hash_algorithm":"sha256","ts":"2026-11-01T02:19:44.315Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7cg1g-jj5ioa","kind":"test","status":"passed","started_at":"2026-11-01T02:19:34.855Z","finished_at":"2026-11-01T02:19:44.307Z","recorded_at":"2026-11-01T02:19:44.307Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:19:44.315Z"}],"before_hash":"9cbd903eb85dfd87ecf24bcd0860298bec07530d22bace46196fa303a570cbde","after_hash":"a22ae0c682b70a8fe25c887718c6a26bb1c367758c1172a1ac056b8c0552a3f6","item_hash_version":3,"message":"Track test run summary (test-local-mvx7cg1g-jj5ioa)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"5f767e629c1684aca843a3f0cbe8f925f9d231cf374859e0cf0b8422e15fcca1"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:39:18.430Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"When a context, list or search --for intent reduces an explicit --limit, the result carries the warning limit_overridden_by_intent:--limit ->, filters.requested_limit beside the effective filters.limit, and context_intent.limit_override with flag --limit, requested and effective; limit_override.token_budget_for_requested and restore_with (the flag fragment --token-budget to add to the original command) are present when a safe-integer budget can carry the request and omitted otherwise; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers context, list and search overrides, raw host options and the unsafe-budget case"},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:39:18.430Z"}],"before_hash":"a22ae0c682b70a8fe25c887718c6a26bb1c367758c1172a1ac056b8c0552a3f6","after_hash":"da698a2157c295b2c11caaba9c028107ab1437ec1ef1a72ee0976580c39ea9e1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"38a23fb50ef4c6ca2bf412806eef820118f5ca316b584246b78365ae3d218898"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:39:18.932Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T02:39:18.932Z","author":"harness:claude-code","text":"CodeRabbit review round 3 on PR #1645: the acceptance criterion now describes the shipped contract, so consumers are not sent to fields that do not exist. The warning has no budget, the requested value lives in limit_override.requested, and the restore fields are omitted for unsafe budgets. The guidance tests now also assert the old pm context --limit 10 command is absent."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:39:18.932Z"}],"before_hash":"da698a2157c295b2c11caaba9c028107ab1437ec1ef1a72ee0976580c39ea9e1","after_hash":"8d18d8b4f714640a966eae0a2501704f62ee867b43931680b656c01d28f7bcb3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"eec959c48bd50b735aa05f9c8d34ac19db0d66fa9003ffc76b2cb0d546b2b6be"} diff --git a/.agents/pm/issues/pm-gh1643.toon b/.agents/pm/issues/pm-gh1643.toon index 72bdacf7eb..7aadb4dc91 100644 --- a/.agents/pm/issues/pm-gh1643.toon +++ b/.agents/pm/issues/pm-gh1643.toon @@ -6,7 +6,7 @@ status: closed priority: 1 tags[4]: agent-ux,claim,github,refusal created_at: "2026-10-31T21:13:57.945Z" -updated_at: "2026-11-01T02:19:34.344Z" +updated_at: "2026-11-01T02:39:19.500Z" closed_at: "2026-10-31T22:31:19.294Z" completed_at: "2026-10-31T22:31:19.294Z" author: "harness:claude-code" @@ -24,11 +24,12 @@ dependencies[5]{id,kind,created_at,author,source_kind,author_source}: pm-fjxm,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-gh1369,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected pm-ixi1,related,"2026-10-31T21:13:57.945Z","harness:claude-code","cli:create:dep",detected -comments[4]{created_at,author,text}: +comments[5]{created_at,author,text}: "2026-10-31T21:46:58.955Z","harness:claude-code","Implementation: runClaim gained an optional start that calls applyClaimStart inside the same mutateItem callback (the release --handoff pattern). It resolves the in-progress status through resolveStartTaskInProgressStatus, refuses a terminal item with terminal_state_conflict even under --force, enforces type workflows via enforceTypeWorkflowTransition, and returns start {from_status,to_status}. runClaimNext forwards start to every candidate in the race-loss walk, so --if-available and filters behave as before. CLI runClaimAction and MCP runMcpClaimAction route start with an explicit id to the unchanged two-step composition, which keeps its typed 'Remove --next and --if-available' refusal; start without an id uses the atomic path or the new requireClaimTarget in src/sdk/lifecycle/claim-target.ts (missing_required_argument / ambiguous_option_combination, attributed to claim through the catalog generator's module map). runStartTask now strips start before its claim step so explicit --start stays claim-then-update. MCP schema: the start+next exclusion is gone and start+ifAvailable is exclusive only with an id. PmClient.claimNext({start:true}) is typed. Verified in a sandbox: claim --next --start → status in_progress, changed_field_count 3, history create→claim (one event)." "2026-10-31T23:02:07.900Z","harness:claude-code","CodeRabbit review round 1 on PR #1645. (1) The compact claim receipt now keeps the start {from_status,to_status} object (mutation-projection copies plain-object start), so default CLI JSON and MCP responses show the transition. (2) Major, predates this PR: resolveStartTaskInProgressStatus ignored a configured in_progress_status. A workflow declaring in_progress_status 'working' started items into the open status. It now returns the registry's in-progress status when it differs from the open status, and otherwise keeps the in_progress alias lookup (the 'building' alias workflow still resolves). Registration tests cover both. (3) Semantic attribution after the claim commit is best-effort: a failure returns the warning claim_semantic_attribution_unrecorded instead of an error for persisted ownership (claim-attribution spec). (4) MCP claim {id, next, start} was already refused with invalid_argument_value by the explicit-start composition; a test now pins it. (5) Starting an item with an open blocker is out of scope and matches update semantics; filed as pm-urzubi." "2026-11-01T01:17:43.802Z","harness:claude-code","CodeRabbit review round 2 on PR #1645: runMcpClaimAction now reads next only from the normalized runner options, so nested next:false overrides a flat next:true for both dispatch and the target check. The spec asserts {next:true, options:{next:false,start:true}} is refused with missing_required_argument instead of claiming an item." "2026-11-01T02:03:51.160Z","harness:claude-code","cubic review on PR #1645. Fixed: a quoted empty id (pm claim '' --start) now reaches the typed missing_required_argument refusal on CLI and MCP instead of the explicit composition. resolveStartTaskInProgressStatus never returns a configured in-progress status that the registry no longer defines (pm schema remove-status leaves the workflow reference); it falls back to the alias lookup and open status. The runClaim start option's docstring states it is the single-write variant used by ranked selection, while explicit --start keeps the composition. LIFECYCLE_COMMANDS now names the invalid_argument_value refusal for --start with --next or --if-available. Kept as is: MCP id precedence (flat id first) matches every lifecycle action's shared item-id resolution." + "2026-11-01T02:39:19.500Z","harness:claude-code","CodeRabbit review round 3 on PR #1645: the docs now say ranking happens before any lock, and each candidate is re-read under its item lock before the single claim-and-start write. The selection itself was never atomic." files[19]{path,scope}: scripts/release/surface-replication-sets.json,project sdk/public-surface.json,project diff --git a/.agents/pm/issues/pm-gh1644.toon b/.agents/pm/issues/pm-gh1644.toon index 309854d7a1..b3b7abca59 100644 --- a/.agents/pm/issues/pm-gh1644.toon +++ b/.agents/pm/issues/pm-gh1644.toon @@ -6,12 +6,12 @@ status: closed priority: 1 tags[4]: agent-ux,context,github,token-efficiency created_at: "2026-10-31T21:13:36.728Z" -updated_at: "2026-11-01T02:19:44.315Z" +updated_at: "2026-11-01T02:39:18.932Z" closed_at: "2026-10-31T22:31:33.693Z" completed_at: "2026-10-31T22:31:33.693Z" author: "harness:claude-code" estimated_minutes: 150 -acceptance_criteria: "An explicit row limit above the intent's budget-derived ceiling yields a typed limit_overridden_by_intent warning naming --limit, the requested and effective values, and a --token-budget that would carry the request; filters reports requested_limit beside the effective limit whenever they differ; the context_intent receipt records requested_limit; the same behaviour holds for context, list --for triage and search --for discover; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers every row-bounded intent with an explicit over-ceiling limit" +acceptance_criteria: "When a context, list or search --for intent reduces an explicit --limit, the result carries the warning limit_overridden_by_intent:--limit ->, filters.requested_limit beside the effective filters.limit, and context_intent.limit_override with flag --limit, requested and effective; limit_override.token_budget_for_requested and restore_with (the flag fragment --token-budget to add to the original command) are present when a safe-integer budget can carry the request and omitted otherwise; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers context, list and search overrides, raw host options and the unsafe-budget case" parent: pm-5t33or risk: medium confidence: high @@ -22,10 +22,11 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}: pm-3crymx,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected pm-gy885b,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected pm-ka6d,related,"2026-10-31T21:13:36.728Z","harness:claude-code","cli:create:dep",detected -comments[3]{created_at,author,text}: +comments[4]{created_at,author,text}: "2026-10-31T21:46:59.575Z","harness:claude-code","Implementation: the three row-bounded intent appliers share applyIntentRowLimit, which keeps the existing min(requested, budget-derived) clamp but stashes a reduced request as intentRequestedLimit. attachContextIntentReceipt calls discloseIntentLimitOverride, which reads that key or the raw host --limit and writes context_intent.limit_override {flag, requested, effective, token_budget_for_requested, restore_with}, filters.requested_limit, and the warning 'limit_overridden_by_intent:--limit N->M'. token_budget_for_requested = max(current budget, overhead + rowCost*requested) from the shared resolveIntentRowCost table. Measured on this tracker: pm context --for orient --limit 10 now prints the override with restore_with 'pm context --for orient --limit 10 --token-budget 7200'. That retry returns 10 rows at an estimated 4,637 tokens within budget. list --for triage --limit 150 → effective 100, retry with --token-budget 3200 honours 150. The quickstart (init-agent-guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, pm-user ECOSYSTEM_PLANNING) and the replayed agent-task transcript now use the measured canonical pm context --for orient." "2026-10-31T23:02:09.739Z","harness:claude-code","CodeRabbit review round 1 on PR #1645: limit_override.restore_with is now the flag fragment '--token-budget N' to add to (or replace in) the original command, following the omission-receipt convention (restore_with '--full'). The full rebuilt command had dropped named options (--status, --sort) and inline search field:value filters. token_budget_for_requested and restore_with are omitted when the sufficient budget is not a safe integer (spec covers Number.MAX_SAFE_INTEGER)." "2026-11-01T02:03:52.127Z","harness:claude-code","cubic review on PR #1645: the intentRequestedLimit marker option is removed. Every host attaches the receipt with the caller's own options (CLI output context and SDK runtime both pass unprojected options), so the requested limit is read from options.limit and no undeclared option leaks into list/search options or can be spoofed. The conformance doc names limit_override.flag." + "2026-11-01T02:39:18.932Z","harness:claude-code","CodeRabbit review round 3 on PR #1645: the acceptance criterion now describes the shipped contract, so consumers are not sent to fields that do not exist. The warning has no budget, the requested value lives in limit_override.requested, and the restore fields are omitted for unsafe budgets. The guidance tests now also assert the old pm context --limit 10 command is absent." files[10]{path,scope}: docs/agent-task-token-baseline.json,project docs/agent-task-transcripts.json,project diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index e6b7fdf921..0429b1d2aa 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -919,7 +919,7 @@ Lifecycle composition flags combine claim, status, and close operations: ```bash pm claim --start # claim + move to the configured in-progress status -pm claim --next --start # select, claim and start the next available item in one locked write +pm claim --next --start # select the next available item, then claim and start it in one locked write pm release --pause # move to the configured open status + release claim pm close "" --release-assignment # close with evidence + release assignment ``` diff --git a/docs/LIFECYCLE_COMMANDS.md b/docs/LIFECYCLE_COMMANDS.md index 1bcd8c6f56..0402548b62 100644 --- a/docs/LIFECYCLE_COMMANDS.md +++ b/docs/LIFECYCLE_COMMANDS.md @@ -36,9 +36,11 @@ Plain `claim`, `release`, `update`, and `close` keep their original semantics. `claim --start` rejects `--if-available` before mutation. It claims ownership, then moves the item to the configured in-progress status. `claim --next --start` selects the next caller-available item with the same -filters, `--if-available` behavior, and race-loss walk as `claim --next`. It then -claims the item and moves it to the configured in-progress status in one -locked mutation and one `claim` history event, so no other agent can observe +filters, `--if-available` behavior, and race-loss walk as `claim --next`. +Ranking happens before any lock, so a candidate can change before it is +claimed. Each attempt re-reads the candidate under its item lock and then +claims it and moves it to the configured in-progress status in one locked +mutation and one `claim` history event. No other agent can therefore observe the claimed item before it starts. Configured type workflows are enforced, a terminal candidate is refused (`terminal_state_conflict`), and the receipt carries `start: { from_status, to_status }`. A claim without an id or `--next` @@ -78,7 +80,7 @@ import { PmClient } from "@unbrained/pm-cli/sdk"; const pm = new PmClient({ pmRoot: "/project/.agents/pm" }); const started = await pm.claim("pm-example", { start: true }); -const nextStarted = await pm.claimNext({ start: true }); // one locked select-claim-start write +const nextStarted = await pm.claimNext({ start: true }); // ranked selection, then one locked claim-and-start write const paused = await pm.release("pm-example", { pause: true }); const closed = await pm.close("pm-example", "Acceptance verified", { releaseAssignment: true, @@ -97,7 +99,7 @@ Literal `true` options return typed `StartTaskResult`, `PauseTaskResult`, or MCP actions remain `claim`, `release`, and `close`, using `start`, `pause`, and `releaseAssignment`. MCP `claim` with `next: true` and `start: true` uses the -same single locked write as `claim --next --start`. Action-specific discovery types claim's `start` as boolean; +same ranked selection and single locked claim-and-start write as `claim --next --start`. Action-specific discovery types claim's `start` as boolean; scheduling actions retain string timestamps. For the broad provider tool schema, pass the claim boolean through `options: { start: true }` because its shared flat `start` property retains the scheduling string contract. Bulk action names remain diff --git a/tests/integration/cli.integration.spec.ts b/tests/integration/cli.integration.spec.ts index 6de492fe54..53b1c13e9a 100644 --- a/tests/integration/cli.integration.spec.ts +++ b/tests/integration/cli.integration.spec.ts @@ -3504,7 +3504,8 @@ describe("CLI integration (sandboxed PM_PATH)", () => { const guidancePath = path.join(context.tempRoot, "AGENTS.md"); const guidanceBody = await readFile(guidancePath, "utf8"); expect(guidanceBody).toContain(""); - expect(guidanceBody).toContain("pm context --for orient"); + expect(guidanceBody).toContain("`pm context --for orient`"); + expect(guidanceBody).not.toContain("pm context --limit 10"); expect(guidanceBody).toContain("pm close "); const guidanceStatusAfter = context.runCli(["init", "--agent-guidance", "status", "--json"], { expectJson: true }); diff --git a/tests/unit/commands/workspace/init-command.spec.ts b/tests/unit/commands/workspace/init-command.spec.ts index f79ee9f0f9..1c16fad790 100644 --- a/tests/unit/commands/workspace/init-command.spec.ts +++ b/tests/unit/commands/workspace/init-command.spec.ts @@ -1206,7 +1206,9 @@ describe("runInit", () => { expect(firstGuidance).toContain( "", ); - expect(firstGuidance).toContain("pm context --for orient"); + expect(firstGuidance).toContain("`pm context --for orient`"); + expect(firstGuidance).not.toContain("--for orient --limit"); + expect(firstGuidance).not.toContain("pm context --limit 10"); expect(firstGuidance).toContain("Author identity is automatic"); const readded = await runInit( From 0c27ee653863afbdc77fc7f6b658550e9f21311d Mon Sep 17 00:00:00 2001 From: Stefan Preu Date: Sun, 1 Nov 2026 04:00:00 +0100 Subject: [PATCH 6/6] chore(pm): scope the GH-1644 guidance criterion to the self-contradicting --for orient --limit command Plain 'pm context --limit N' without an intent is honoured (filters.limit N, no warning) and stays documented. The criterion now names the surfaces that recommended the overridden 'pm context --limit 10 --for orient'. --- .agents/pm/history/pm-gh1644.jsonl | 1 + .agents/pm/issues/pm-gh1644.toon | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.agents/pm/history/pm-gh1644.jsonl b/.agents/pm/history/pm-gh1644.jsonl index 91ec665e94..2f1ad8f9e3 100644 --- a/.agents/pm/history/pm-gh1644.jsonl +++ b/.agents/pm/history/pm-gh1644.jsonl @@ -29,3 +29,4 @@ {"hash_algorithm":"sha256","ts":"2026-11-01T02:19:44.315Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"test_run_track","patch":[{"op":"add","path":"/metadata/test_runs/1","value":{"run_id":"test-local-mvx7cg1g-jj5ioa","kind":"test","status":"passed","started_at":"2026-11-01T02:19:34.855Z","finished_at":"2026-11-01T02:19:44.307Z","recorded_at":"2026-11-01T02:19:44.307Z","passed":1,"failed":0,"skipped":0,"executions":[{"command":"node scripts/run-tests.mjs test -- tests/unit/sdk/context/intent-limit-override.spec.ts tests/unit/sdk/context-intent-contracts.spec.ts tests/unit/sdk/recovery/recovery-guidance.spec.ts","requested_pm_context_mode":"schema","pm_context_mode":"schema","workspace_context_mode":"source","trust_reason":"local_source_ref"}]}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:19:44.315Z"}],"before_hash":"9cbd903eb85dfd87ecf24bcd0860298bec07530d22bace46196fa303a570cbde","after_hash":"a22ae0c682b70a8fe25c887718c6a26bb1c367758c1172a1ac056b8c0552a3f6","item_hash_version":3,"message":"Track test run summary (test-local-mvx7cg1g-jj5ioa)","context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"5f767e629c1684aca843a3f0cbe8f925f9d231cf374859e0cf0b8422e15fcca1"} {"hash_algorithm":"sha256","ts":"2026-11-01T02:39:18.430Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"When a context, list or search --for intent reduces an explicit --limit, the result carries the warning limit_overridden_by_intent:--limit ->, filters.requested_limit beside the effective filters.limit, and context_intent.limit_override with flag --limit, requested and effective; limit_override.token_budget_for_requested and restore_with (the flag fragment --token-budget to add to the original command) are present when a safe-integer budget can carry the request and omitted otherwise; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers context, list and search overrides, raw host options and the unsafe-budget case"},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:39:18.430Z"}],"before_hash":"a22ae0c682b70a8fe25c887718c6a26bb1c367758c1172a1ac056b8c0552a3f6","after_hash":"da698a2157c295b2c11caaba9c028107ab1437ec1ef1a72ee0976580c39ea9e1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"38a23fb50ef4c6ca2bf412806eef820118f5ca316b584246b78365ae3d218898"} {"hash_algorithm":"sha256","ts":"2026-11-01T02:39:18.932Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":null,"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/3","value":{"created_at":"2026-11-01T02:39:18.932Z","author":"harness:claude-code","text":"CodeRabbit review round 3 on PR #1645: the acceptance criterion now describes the shipped contract, so consumers are not sent to fields that do not exist. The warning has no budget, the requested value lives in limit_override.requested, and the restore fields are omitted for unsafe budgets. The guidance tests now also assert the old pm context --limit 10 command is absent."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:39:18.932Z"}],"before_hash":"da698a2157c295b2c11caaba9c028107ab1437ec1ef1a72ee0976580c39ea9e1","after_hash":"8d18d8b4f714640a966eae0a2501704f62ee867b43931680b656c01d28f7bcb3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"eec959c48bd50b735aa05f9c8d34ac19db0d66fa9003ffc76b2cb0d546b2b6be"} +{"hash_algorithm":"sha256","ts":"2026-11-01T02:58:11.285Z","author":"harness:claude-code","author_source":"detected","agent_harness":"claude-code","agent_model":"claude-opus-5-5","agent_model_source":"probe","agent_instance":"8a4268fe12c2a5f9fcd4da8d","agent_provenance":{"model":{"value":"claude-opus-5-5","source":"probe"},"effort":{"value":"xhigh","source":"environment"},"role":{"value":"implementer","source":"argv"},"topic":null,"version":{"value":"2.1.314","source":"probe"}},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"When a context, list or search --for intent reduces an explicit --limit, the result carries the warning limit_overridden_by_intent:--limit ->, filters.requested_limit beside the effective filters.limit, and context_intent.limit_override with flag --limit, requested and effective; limit_override.token_budget_for_requested and restore_with (the flag fragment --token-budget to add to the original command) are present when a safe-integer budget can carry the request and omitted otherwise; every guidance surface that recommended the self-contradicting pm context --limit 10 --for orient (generated agent guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and the replayed agent-task transcripts) recommends the measured canonical pm context --for orient, while plain pm context --limit N without an intent is honoured and stays documented; a regression test covers context, list and search overrides, raw host options and the unsafe-budget case"},{"op":"replace","path":"/metadata/updated_at","value":"2026-11-01T02:58:11.285Z"}],"before_hash":"8d18d8b4f714640a966eae0a2501704f62ee867b43931680b656c01d28f7bcb3","after_hash":"85245f838ff7f9b164862480d1365b3d960a657d20c4268288a70e160eeb6308","item_hash_version":3,"context":{"agent_provenance_outcomes":{"model":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"},"version":{"status":"resolved","resolver":"claude_session_file","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9bb85fa9fd169ddd01344d1b10a3520b610530f445e69d40012bbdc8beee1fac"} diff --git a/.agents/pm/issues/pm-gh1644.toon b/.agents/pm/issues/pm-gh1644.toon index b3b7abca59..b435767eaa 100644 --- a/.agents/pm/issues/pm-gh1644.toon +++ b/.agents/pm/issues/pm-gh1644.toon @@ -6,12 +6,12 @@ status: closed priority: 1 tags[4]: agent-ux,context,github,token-efficiency created_at: "2026-10-31T21:13:36.728Z" -updated_at: "2026-11-01T02:39:18.932Z" +updated_at: "2026-11-01T02:58:11.285Z" closed_at: "2026-10-31T22:31:33.693Z" completed_at: "2026-10-31T22:31:33.693Z" author: "harness:claude-code" estimated_minutes: 150 -acceptance_criteria: "When a context, list or search --for intent reduces an explicit --limit, the result carries the warning limit_overridden_by_intent:--limit ->, filters.requested_limit beside the effective filters.limit, and context_intent.limit_override with flag --limit, requested and effective; limit_override.token_budget_for_requested and restore_with (the flag fragment --token-budget to add to the original command) are present when a safe-integer budget can carry the request and omitted otherwise; generated agent guidance, AGENTS.md, README and docs recommend the measured canonical pm context --for orient; a regression test covers context, list and search overrides, raw host options and the unsafe-budget case" +acceptance_criteria: "When a context, list or search --for intent reduces an explicit --limit, the result carries the warning limit_overridden_by_intent:--limit ->, filters.requested_limit beside the effective filters.limit, and context_intent.limit_override with flag --limit, requested and effective; limit_override.token_budget_for_requested and restore_with (the flag fragment --token-budget to add to the original command) are present when a safe-integer budget can carry the request and omitted otherwise; every guidance surface that recommended the self-contradicting pm context --limit 10 --for orient (generated agent guidance, AGENTS.md, README, AGENT_GUIDE, COMMAND_NAMESPACES, the pm-user skill reference and the replayed agent-task transcripts) recommends the measured canonical pm context --for orient, while plain pm context --limit N without an intent is honoured and stays documented; a regression test covers context, list and search overrides, raw host options and the unsafe-budget case" parent: pm-5t33or risk: medium confidence: high