diff --git a/.agents/skills/test-audit/CAMPAIGN.md b/.agents/skills/test-audit/CAMPAIGN.md new file mode 100644 index 00000000000..d761f82ad65 --- /dev/null +++ b/.agents/skills/test-audit/CAMPAIGN.md @@ -0,0 +1,98 @@ +# Coordinating a test-gardening campaign + +Use this guide for a broad sweep or multiple PRs. Apply the decision and +preservation rules in [SKILL.md](SKILL.md) to every batch. + +## 1. Define the audit boundary + +From the request, establish scope, desired benefit, time budget, target branch, +and delivery authority. Pin the base revision and inspect CI routing and cost +data, including setup/import overhead. Choose one mode: + +- **Targeted sweep:** investigate promising owners or expensive repeated work. + Report the examined area and exclusions; do not claim subsystem completeness. +- **Exhaustive audit:** inventory every in-scope test file and scenario, including + colocated, shared-package, browser, and dedicated-lane coverage. Assign each to + one owner and record its baseline as passed, failed, blocked, or unrun. + +For an exhaustive claim, every declaration needs a decision and evidence; +classify table rows separately when their contracts differ. Missing baseline +results or unread tests leave the audit partial. Existing matching-revision CI +can supply results; a targeted sweep does not require a full-repository census. +Reserve time for preservation checks and reporting; an unfinished validation +leaves an unvalidated local patch, not a PR-ready result. + +## 2. Assign owners and record decisions + +Split work by protected production behavior, including tests across directories. +When delegation is authorized, assign each worker an isolated checkout, owner, +prior findings, model if specified, and discovery/edit/publication authority. +Tell workers to preserve others' changes. One coordinator owns shared fixtures, +routing, and integration. Do not edit a checkout with active test runs. + +Keep one ledger: candidate, contract, retain/repair/consolidate/delete/defer, +reason, keeper, baseline, validation, and branch/PR status. Record reviewed areas +with no safe removals too. Discovery stays read-only until ownership is clear; +qualified edits need no additional user-approval round within existing authority. + +## 3. Plan removal of redundant layers + +Before editing a broad batch, review the ledger a second time by contract and +boundary. Look for suites that replay the same behavior through wrappers, +mocked collaborators, or provider-specific copies of a shared implementation. +Individual cases can look reasonable while the entire layer is redundant. + +Produce a compact layer plan: retired suites, retained owner per contract, +unique assertions to transfer, affected routing/commands, validation controls, +and any unused fixtures or production seams. Prefer a boundary that reaches +the responsible code; preserve distinct transport, lifecycle, and permission +risks. If no whole layer qualifies, record that and proceed with justified +case-level changes. Large deletions are not a requirement. + +The coordinator reconciles keeper dependencies across all batches before +cutover. Follow each chain to surviving proof; reject cycles and recheck keepers +another batch modifies or excludes from routing. A batch is ready when every +retired contract has retained proof or retirement evidence. Production-seam +removal still needs separate caller evidence. + +## 4. Cut over and verify preservation + +Move unique assertions first, apply one coherent batch, and update selection +and ownership references. Follow [validation.md](references/validation.md) for +baselines, controls, restoration, and cost claims. Serialize heavy database or +process runs when host capacity is shared; clean up only owned resources. + +Review the combined diff against the layer plan. For broad or consequential +batches, use independent preservation review when delegation is available and +authorized; otherwise disclose that limitation. Resolve each finding with +source evidence or restored coverage and validate repaired assertions with an +intended-failure control. No batch is complete with an unresolved coverage gap. + +An exhaustive audit requires a decision for every declaration, a completed and +classified baseline for every file, and passing preservation proof for each +removal. Diagnosed pre-existing defects may remain as explicit follow-ups; +unresolved setup failures or unread tests leave the audit partial. If time +expires first, report the remaining inventory and validation status. + +## 5. Deliver and integrate + +Prefer a few coherent PRs to one PR per trivial deletion. For a stack, record +each PR's head SHA, base branch/SHA, unique diff, and intended final target. +After a parent is squash-merged, discover its actual merge SHA and replay only +the child's unique change if necessary. Inspect the resulting PR diff for +accidentally reintroduced parent commits or files. + +Resolve integration conflicts by reading new contracts on the target branch. +If a deleted file gained a regression, preserve that regression at an appropriate +owner; do not blindly accept the deletion or restore the entire old suite. + +For authorized merges, use checks and reviews tied to the current PR head, +inspect unresolved findings, and respect the repository's merge policy. A +previous green head does not validate a changed head. + +A PR marked “merged” into an intermediate stack branch is not necessarily +landed in the intended integration branch. Verify the full parent chain and +final diff inclusion before reporting the campaign landed. Preserve or clean +up branches/worktrees according to the user's instructions and retain needed +evidence before cleanup. Report delivery separately from audit completeness; +stop new discovery when the user asks to wrap up. diff --git a/.agents/skills/test-audit/NOTICE.md b/.agents/skills/test-audit/NOTICE.md new file mode 100644 index 00000000000..f38cbe74b94 --- /dev/null +++ b/.agents/skills/test-audit/NOTICE.md @@ -0,0 +1,34 @@ +# Attribution + +Paperclip's test-audit skill is inspired by the organization and audit principles +of OpenClaw's [test-audit skill](https://github.com/openclaw/openclaw/tree/main/.agents/skills/test-audit), +consulted on 2026-10-13. It uses the short entry point, separate campaign guide, +and focused references pattern, with independently written Paperclip procedures +and lessons from our test-gardening work. It has no execution or documentation +dependency on an OpenClaw checkout or another installed skill. + +The upstream license is retained below for the adapted material. + +```text +MIT License + +Copyright (c) 2026 OpenClaw Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/.agents/skills/test-audit/SKILL.md b/.agents/skills/test-audit/SKILL.md new file mode 100644 index 00000000000..36288974712 --- /dev/null +++ b/.agents/skills/test-audit/SKILL.md @@ -0,0 +1,78 @@ +--- +name: test-audit +description: > + Garden the test suite by finding and removing redundant, vacuous, or + implementation-coupled tests while preserving useful regression coverage. + Use for test audits, test pruning, duplicate coverage, or reducing CI test + cost. Supports focused cleanups and coordinated audit campaigns. +--- + +# Test Audit + +Reduce execution and maintenance cost while preserving meaningful failure +detection. Investigate aggressively; delete only with evidence. Retaining an +area after review is a valid result. This skill is self-contained; upstream +attribution is in [NOTICE.md](NOTICE.md). + +## Choose the scope + +- Focused cleanup: follow this entry point and read the + [candidate evidence guide](references/audit.md) before editing. +- Multiple owners, agents, or PRs: also read [CAMPAIGN.md](CAMPAIGN.md). Declare + whether the work is a targeted sweep or an exhaustive subsystem audit. +- Validation and cost claims: read [references/validation.md](references/validation.md). +- Paperclip commands, invariants, and delivery: read + [references/paperclip.md](references/paperclip.md) alongside repository instructions. + +## Decision gate + +For every proposed removal, answer: + +1. Which observable contract and plausible regression do the assertions cover? +2. Which retained file/case detects that regression through the responsible + production code? Otherwise, why is the assertion vacuous or contract retired? +3. Does this candidate uniquely cover a transport, permission, lifecycle, + storage boundary, platform, default, or other independent failure mode? +4. What improves: executions, fixtures, processes, maintenance, or runtime? + +Prefer the smallest test that reaches the code responsible for the contract. +A broader suite is not automatically stronger proof. Similarity, size, slowness, +or suspected AI authorship alone never justify deletion. + +## Prevent new duplication + +Apply this gate to tests added or changed during gardening: + +- Name the regression that existing proof misses. Extend an appropriate owner + or table before adding another suite; another layer needs a distinct risk. +- Exercise the responsible production behavior. Do not add a mock that supplies + the result being asserted or a production hook solely for convenient testing. +- Check observable outcomes that survive behavior-preserving refactors. A + negative case must reach its named guard with otherwise valid inputs. + +## Focused workflow + +1. **Inspect:** pin the base; read candidates, keepers, fixtures, production + callers, history, and CI routing. Preserve unrelated work. +2. **Plan:** mark retain, repair, consolidate, delete, or defer. Name remaining + proof and assertions to move; look for redundant suites as well as cases. +3. **Prove:** establish the affected baseline locally or from matching-revision + CI. Diagnose failures before treating them as cleanup candidates. +4. **Edit:** move unique assertions first, then retire duplicates and unused + support. Production-seam deletion needs its own caller/contract evidence. +5. **Validate:** run keepers and affected siblings, verify selection, and apply + the validation guide's intended-failure controls where needed. +6. **Review:** reconcile deleted contracts with retained proof. Publish or merge + only within the task's authorization. + +## Completion and reporting + +Report removals and keepers, retained/deferred candidates, checks and limitations, +distinct cases versus repeated executions, and test/support versus production +LOC. Count documentation separately. Label cost evidence as measured, estimated, +or historical; a proven simplification does not require a measurable speedup. + +State the actual endpoint: findings, validated local patch, published PR, +intermediate merge, or landed in the intended target. Stop at the requested +scope/time limit or completed delivery, not a deletion quota. For another pass, +refresh the base and prior findings; revisit retained tests only with new evidence. diff --git a/.agents/skills/test-audit/agents/openai.yaml b/.agents/skills/test-audit/agents/openai.yaml new file mode 100644 index 00000000000..9987fcc761e --- /dev/null +++ b/.agents/skills/test-audit/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Test Audit" + short_description: "Prune redundant tests while preserving useful coverage" + default_prompt: "Use $test-audit to garden this test suite and remove well-supported redundancy while preserving meaningful regression coverage." diff --git a/.agents/skills/test-audit/references/audit.md b/.agents/skills/test-audit/references/audit.md new file mode 100644 index 00000000000..e821e2f4ec1 --- /dev/null +++ b/.agents/skills/test-audit/references/audit.md @@ -0,0 +1,72 @@ +# Candidate evidence and retention + +Use this reference before deleting or consolidating tests. + +## Search for waste, then inspect the contract + +| Candidate pattern | What to establish before changing it | +| --- | --- | +| Several tests repeat one assertion through the same production path | A retained case has equivalent inputs, failure sensitivity, and relevant setup. | +| A test file only imports another test file | The real owner is selected in the same required runs; the alias adds no setup, configuration, or separate entry point. Check both CI and documented commands. | +| A large table repeats equivalent rows | Preserve rows that exercise different branches, boundary values, error classes, platforms, or defaults. | +| A bulk fixture creates many interchangeable records | Establish that the count does not cross batching, pagination, concurrency, or transaction thresholds. Retain mixed outcomes and persisted state assertions. | +| Assertions copy lists, exports, strings, or source shapes | Determine whether the list or shape is an independent public, packaging, generated, or architecture contract. | +| Mocks implement the behavior being asserted | Find a test that exercises the responsible production behavior; otherwise repair or relocate this proof before removing it. | +| Fixtures fabricate receipts, ordering, or persisted state | Verify that the responsible production path creates that evidence; fixture-supplied outcomes cannot prove it does. | +| A negative case passes for an unrelated reason | Reach the named guard with otherwise valid inputs. Fix useful coverage rather than discarding it as waste. | +| A test-only export, reset hook, or wrapper becomes unused | Search runtime callers, package exports, generated callers, supported integrations, and history. File location alone does not make it dead production code. | + +Also inspect self-comparisons, assertions with expected values derived from the +subject, probes with no checks, and names that promise more than the body tests. +Explicitly assertion-free smoke tests can still protect module loading or +execution; identify that purpose before deleting them. + +## Reasons to retain apparently similar tests + +Two tests can use the same payload while protecting different failure modes: + +- Authorization and tenant isolation at separate exposed entry points. +- Direct API versus SDK/MCP transport, including serialization and errors. +- Initial execution versus replay, recovery, restart, or concurrent access. +- In-memory return values versus committed database state or rollback. +- Default-off behavior versus explicit opt-in; missing versus invalid input. +- Platform behavior, published package exports, migrations, generated-language + compatibility, or intentional prompt/protocol bytes. +- Browser interaction, reconnect, pagination, lost responses, attachment + persistence, or visible ordering absent from lower-level coverage. + +Keep a static check when it is an independent and appropriate guard of a +contract. Implementation-shaped checks that fail on a harmless identifier +rename deserve scrutiny; this does not make all source inspection disposable. +Likewise, a slow integration test can be the only meaningful proof of a +transaction or lifecycle. Cost helps rank investigation, not determine truth. + +## Evidence before editing + +Read the candidate's full body and parameter rows, setup/teardown, production +owner, real callers, proposed keeper, configuration, and relevant history. +Look for why the test was introduced and regressions added since. Inspect the +dependency's actual contract when a claim depends on library behavior. + +Use a compact table or PR notes: + +| Candidate | Decision and reason | Contract/regression | Keeper or retirement evidence | Validation and benefit | +| --- | --- | --- | --- | --- | +| File and case/rows | Retain / repair / consolidate / delete / defer | Observable failure detected | Exact file/case, or evidence the contract no longer exists | Commands/control, cost or maintenance reduction, limitations | + +For consolidation, list unique assertions to move. The keeper must actually +exercise them with valid fixtures; adding an assertion that cannot fail is +not coverage preservation. If setup or the selected test project differs, +explain why it does not change the protected contract. + +## Two useful distinctions + +- An import-only alias and its directly selected owner may duplicate execution. + An identical-looking alias importing a package suite absent from the default + run may be the only entry point and must stay until routing is repaired. +- Repeated successful records in a bulk route fixture may be reducible while + retaining mixed failures and persisted outcomes. The same reduction would + invalidate a test whose record count exercises a batch or pagination limit. + +If evidence is missing, defer that candidate and keep working on independent +ones. Audit-only findings do not become permission to delete tests elsewhere. diff --git a/.agents/skills/test-audit/references/paperclip.md b/.agents/skills/test-audit/references/paperclip.md new file mode 100644 index 00000000000..56d916fe26f --- /dev/null +++ b/.agents/skills/test-audit/references/paperclip.md @@ -0,0 +1,71 @@ +# Paperclip integration + +This reference adapts the standalone workflow to this repository. Read root +and scoped `AGENTS.md` and their required product/development documents. Resolve +paths below from the repository root and inspect current contents; counts and +shard layouts evolve. + +## Discovery and test selection + +- `package.json`, `vitest.config.ts`, and package-local Vitest configurations + describe the test projects and commands. +- `scripts/run-vitest-stable.mjs` selects and runs the default suites. Inspect + its supported dry-run/mode/shard flags before constructing routing checks. +- `.github/workflows/pr.yml` identifies the PR workflow; follow its reusable + workflow references to the actual lanes and shard matrix. +- `scripts/general-server-shard.mjs` and the general-server, serialized, and + E2E duration JSON files under `scripts/` provide routing and historical cost + clues. Read each manifest's provenance; a file's weight is not per-case time. +- Search tests colocated with production modules as well as `__tests__/`. + Some acceptance files import other suites. Verify both direct selection and + aliases, including package suites outside the default project list. + +Example focused command, replacing the example path with the actual owner: + +```sh +pnpm --filter @paperclipai/server exec vitest run src/services/OWNER.test.ts +``` + +`pnpm test` is the normal Vitest entry point. Browser and release-smoke suites +are opt-in; run them when affected or explicitly verifying those flows. +Follow repository requirements for a PR-ready handoff, including the full +typecheck/test/build checks when required; identify local versus CI execution +and report anything unrun. Do not rerun the entire repository on every audit +heartbeat. Test removals do not excuse required checks. + +## Contracts deserving special care + +Preserve company isolation, board/agent authorization, atomic checkout, +single-assignee rules, approval gates, budget hard stops, and mutation activity +logging. Persistence, idempotency, cancellation, concurrency, recovery, and +ordering often have separate owners even when test names look similar. + +Distinguish first-party Telemetry, opt-in OpenTelemetry observability, and the +local run log by their paths as defined in `AGENTS.md`. A pruning task is not +permission to remove privacy, endpoint gating, or event-contract coverage. + +## Environment and delivery + +Database-backed tests may start real embedded Postgres even though development +can use PGlite. Check the actual fixture. An isolated install with scripts +disabled may need documented dependency setup before its binaries work. +Resource exhaustion must not be interpreted as a tested product failure or a +passing control. Coordinate heavy runs and clean up only owned resources. + +Follow existing branch/worktree conventions and protect unrelated working-tree +edits. New repository audit plans/receipts belong in +`doc/plans/YYYY-MM-DD--test-audit.md`. Prefer one concise receipt per +coherent batch or campaign. For Paperclip issue work, follow the issue's artifact +and work-product requirements rather than relying only on local paths. + +When publishing is authorized, fill every section of +`.github/PULL_REQUEST_TEMPLATE.md`, including exact model information that is +actually known; do not fabricate context-window details. Use an accurate PR +title consistent with current repository policy. A deletion-only cleanup may +be a refactor and does not need a token added test to satisfy a title heuristic. + +When merging is authorized, verify current-head CI and fresh repository review +results, inspect review threads and edited bot summaries, and resolve genuine +findings. After a stack merge, verify inclusion in the intended final branch +as described in [CAMPAIGN.md](../CAMPAIGN.md). No fixed check count or old +review score substitutes for inspecting the current requirements. diff --git a/.agents/skills/test-audit/references/validation.md b/.agents/skills/test-audit/references/validation.md new file mode 100644 index 00000000000..8a86e6452ea --- /dev/null +++ b/.agents/skills/test-audit/references/validation.md @@ -0,0 +1,87 @@ +# Preservation, cost, and evidence + +## Prove the retained behavior + +Pin baseline and candidate revisions and record the actual command, project, +filters, environment differences, and pass/fail/skip counts. Prefer focused +owner and affected sibling runs. A zero-test run, setup error, or fully skipped +suite is not a successful baseline. Use recent CI only when its revision and +test selection support the claim being made. + +For straightforward exact duplicates, compare all assertions, fixtures, +configuration, and selection. For assertion migration, weaker-to-stronger +coverage substitution, security/persistence boundaries, or uncertain failure +sensitivity, use a deliberate control: + +1. Run the keeper normally and observe it passing. +2. In an isolated, idle checkout, temporarily introduce the specific plausible + production regression the keeper is meant to detect, or revert the relevant + fix. Keep unrelated guards and fixtures valid. +3. Verify the expected case fails at the intended assertion. A timeout, missing + dependency, unrelated rejection, or initialization failure is not proof. +4. Restore the exact pre-control bytes in a guaranteed cleanup step. Check the + diff/hash and run the same selected case successfully again. +5. Run the affected candidate owners without that narrow name filter before + calling the complete owner suite green. + +Never mutate a checkout with active tests, commit a temporary mutation, or +overwrite someone else's source changes during restoration. Do not add a +permanent replacement test solely to justify removing an obvious duplicate. + +## Verify what actually runs + +Inspect the real runner entry point, include/exclude rules, package projects, +shard routing, dedicated CI lanes, and documented focused commands. A test +existing on disk does not guarantee that it executes in the required run. +The required invocations are the affected CI lanes and supported developer +commands for the audit's scope. Redirecting a documented alias command to its +owner is valid when the configuration and protected contracts remain equivalent; +preservation does not require retaining an obsolete filename indefinitely. + +When removing test-import aliases, prove that the retained owners remain +selected with the intended multiplicity in each required invocation. Update +explicit commands and ownership inventories. Use dry-run routing to inspect +selection and an actual focused run to confirm execution where import caching +or collector behavior makes listing ambiguous. Intentional runs on different +platforms/configurations are separate contracts, not duplicate registrations. + +## Measure the benefit honestly + +Keep these quantities separate: + +| Quantity | Interpretation | +| --- | --- | +| Deleted source declarations / expanded table cases | Change in test code or case inventory. | +| Avoided registrations / suite invocations | Repeated execution eliminated; may remove no unique contract. | +| Test/support LOC | Maintenance reduction, not a runtime estimate. Report additions and net change. | +| Production LOC | Runtime code changed, with its own justification; do not combine it with test LOC. | +| Historical manifest weight | Scheduling estimate at its recorded revision/date, often including setup/import cost. | +| Case time / owner suite time / process time | Different scopes; compare like with like. | +| Shard time / workflow critical path | Scheduling and parallelism effects; summing suite savings does not predict elapsed CI savings. | + +Use bounded matched baseline/candidate runs when a quantified runtime claim is +important. Keep environment and configuration comparable, alternate order when +practical, and disclose sample count and variation. Inspect neighboring suites +for host-wide drift. A faster unrelated shard is not deletion evidence; a slower +whole suite does not establish that removing a duplicate caused a regression. + +Logs that only print slow cases provide censored observations: an absent case +is below the reporting threshold, not zero milliseconds. A passing retry is +validation evidence, not a fair speed comparison with the failed attempt. +Report uncertain speed effects as inconclusive and keep structural benefits +separate. Do not demand significance testing for every safe maintenance edit. + +## Failures and durable reporting + +Classify failures before acting: product assertion, test defect, setup/dependency, +host capacity, or CI infrastructure. Fix relevant defects or report the blocker; +do not weaken assertions, increase budgets, skip suites, or erase a failing +baseline to obtain a green audit. Retry a demonstrated transient problem within +a stated bound; a recurring failure needs investigation. + +Use PR notes for small changes. For a campaign, keep a compact dated receipt +with revisions, decisions, keepers, commands, results, controls, measurements, +and limitations. Link accessible CI runs/artifacts when they support claims. +Temporary paths and hashes alone do not make evidence durably inspectable. +Do not generate large evidence dossiers for trivial deletions or store raw +logs containing secrets. State precisely which broad checks remain unrun. diff --git a/doc/DATABASE.md b/doc/DATABASE.md index 959658e603a..a580717f0b8 100644 --- a/doc/DATABASE.md +++ b/doc/DATABASE.md @@ -716,7 +716,7 @@ not revoke grants used by other bots. See [Slack setup](connections/slack-setup. ## Transaction-aware delivery work signals -Five existing delivery queues use process-local work signals to avoid empty +Durable delivery queues, including plugin resource lifecycle events, use process-local work signals to avoid empty polling. Register work **before** writing a queue row, on that row's transaction: ```ts diff --git a/doc/DEVELOPING.md b/doc/DEVELOPING.md index 838dc0a88ca..2a35c21259e 100644 --- a/doc/DEVELOPING.md +++ b/doc/DEVELOPING.md @@ -1020,6 +1020,13 @@ When an additional repository has a configured local checkout, Paperclip seeds t Sandbox staging, including Daytona, transfers each repository's Git history and working files. Restore merges files and commits back into each local task checkout independently. Durable sandbox recovery keeps the same repository snapshots. Normal ignore and workspace exclusion rules still apply. A clone failure stops task preparation with an error so the agent does not start with only part of the project. +An interrupted legacy run can retain a file recovery hold after its automatic +replay decision is resolved. The task's recovery notice remains authoritative. +After recovering and verifying files from the original workspace, a board member +with runtime access can record the evidence in that notice. This operation checks +the exact source run and stop receipts, leaves the task status unchanged, and does +not start another run. A chat reply cannot clear the file recovery hold. + Staging preserves relative symlink targets in secondary repositories, including skill links such as `.claude/skills/demo -> ../../skills/demo`. It does not rewrite them to host temporary paths or copy their target contents in place of the link. Daytona still rejects outbound archives with absolute or escaping link targets before extraction. If a repository is detached or its source configuration changes, its previous task copy is retained under `.paperclip-runtime/detached-repositories/` and excluded from future sandbox transfers. Referenced projects continue to use the separate read-only multi-project workspace behavior. diff --git a/doc/plans/2026-10-06-default-agent-runner.md b/doc/plans/2026-10-06-default-agent-runner.md index e1cc1fff162..9c4b407391c 100644 --- a/doc/plans/2026-10-06-default-agent-runner.md +++ b/doc/plans/2026-10-06-default-agent-runner.md @@ -35,9 +35,16 @@ passes69 setup controls and12 unchanged lower controls, server no-emit and independent source review. Private copies retire only after confirmed cleanup; an uncertain close retains owned state and remains a failure. -Next action: integrate exact master -`393d1c410862aa93561548e0a39feec3b89df6ff` with its migration0340 and plugin lifecycle -changes, preserve the Storybook catalog, and freeze one candidate. Require fresh +The reviewed fix commit is `12046f522bca29ded24425be71b856993b4997f7`. +The normal merge retains exact master `393d1c410862aa93561548e0a39feec3b89df6ff`, +including migration0340, shared/plugin lifecycle contracts and file recovery. +Independent source inspection confirms all four reviewed fix hashes and the +incoming executable changes. Storybook reconciliation retains263 suites and2503 +authored IDs, both review histories and all four incoming recovery states. Its +27 existing controls, maintained catalog check and35-area/175-ID map check pass. +Only JavaScript dependencies are prepared locally; native/image builds stay remote. + +Next action: finish focused combined-source checks and freeze one candidate. Require fresh CI/review, affected installed-package and managed first-task proof before ready or merge. Historical receipts keep their literal sources. The following checkpoints are retained history, not the current merge state. diff --git a/doc/plugins/PLUGIN_AUTHORING_GUIDE.md b/doc/plugins/PLUGIN_AUTHORING_GUIDE.md index cae51f0cdc7..f4b4c06a8d3 100644 --- a/doc/plugins/PLUGIN_AUTHORING_GUIDE.md +++ b/doc/plugins/PLUGIN_AUTHORING_GUIDE.md @@ -114,48 +114,71 @@ agents. The durable event inbox below is separate: acknowledging an event does not complete required lifecycle work. See [Agent lifecycle](../AGENT-LIFECYCLE.md) for phase ordering, recovery, and existing-installation limits. -## Durable resource lifecycle inbox +## Durable resource lifecycle delivery -Plugins with `events.subscribe` can read durable, company-scoped resource hooks -through `ctx.events.listLifecycle(companyId, limit?, afterId?)` and acknowledge successful -work with `ctx.events.acknowledgeLifecycle(companyId, eventId)`. Use an existing -plugin job to poll each configured company; the host checks invocation scope and whether -the plugin is ready and enabled for that company. These methods are separate -from the fire-and-forget `ctx.events.on()` bus. -Proactive jobs and timers may read only companies authorized by the plugin's -company configuration. Calls inside a host-issued invocation must match its company. +Declare `resourceLifecycle: ["project"]` (or `["agent", "project"]`) in the +manifest, with `events.subscribe`, and implement `onResourceLifecycle(event)`: -An event has `id`, `companyId`, `resourceType`, `resourceId`, `action`, and +```ts +async onResourceLifecycle(event) { + const config = await ctx.config.get(event.companyId); + if (!config.provider) return { eventId: event.id, status: "deferred" }; + const finished = await reconcileCurrentResource(event); + return { eventId: event.id, status: finished ? "acknowledged" : "retry" }; +} +``` + +Core delivers events to ready plugins with a configuration row for that company, +unless the plugin is disabled there. Each invocation has the event's company +scope. The dispatcher wakes after the mutation's outer transaction settles, +on host startup, plugin activation/worker start, and company configuration or +enablement changes. Successful worker `onConfigChanged` completion wakes delivery +again, so a callback that deferred while configuration was still applying is +revisited. Failed scans retain pending work through idle drain. Empty and deferred +queues have no periodic scans. `retry`, +exceptions, invalid results, and unavailable workers leave the event pending and +schedule another attempt after 30 seconds. Other acknowledgments or mutations +do not bypass that event's delay; a host restart may replay it immediately. +`deferred` retains the event without +a retry deadline, useful while waiting for operator configuration. A later wake +revisits deferred events. Acknowledgment immediately exposes subsequent work. + +Events contain `id`, `companyId`, `resourceType`, `resourceId`, `action`, and `createdAt`. Agent actions are `create`, `pause`, `resume`, and `terminate`; -project actions are `create`, `update`, and `archive`. Pending hires produce creation -after approval. Project updates include repository/workspace mutations and restoring -an archived project. Provider cleanup and retention policy belong to the plugin. +project actions are `create`, `update`, `archive`, and `delete`. Pending hires +produce creation after approval. Project updates include repository/workspace +mutations and restoring an archived project. Deletion is recorded in the same +transaction as removal and survives the project row. Earlier deletions cannot +be reconstructed. Provider cleanup and retention policy belong to the plugin. -Reads return at most one pending event per resource (default 50, maximum 100). Creation is delivered before other events for that resource, even when its -backfilled ID is newer. Remaining events follow ID order. After acknowledging -an event, a later read exposes its successor. A failed -resource remains pending without blocking other resources; process the rest of -the batch independently. Progress is stored per plugin, survives worker restarts, -and is not a global sequence cursor. The delivery migration seeds existing hired -agents and all projects once, including archived projects. Pending hires require -approval; paused and terminated agents retain their current status intents. -This baseline represents current desired state, not reconstructed history. -The baseline includes archive for existing archived projects and update for restored projects whose -journal still ends at archive. Archiving does not authorize provider cleanup. -After that migration, capture stays forward-only; no later journal backfill runs. +backfilled ID is newer. Remaining events follow ID order. Failed or deferred +resources do not block independent resources. Acknowledgments are stored per +plugin, survive worker/host restarts, and are not a global sequence cursor. +The initial journal migration seeds existing hired agents and projects, +including archived projects; this is a desired-state baseline, not history. +Agent lifecycle participation (`onAgentLifecycle`) remains a separate contract: +acknowledging a resource event does not advance an agent lifecycle phase. -Delivery is at least once: concurrent reads or a crash after a provider operation -can repeat an event. Serialize polling and use stable company/event idempotency -keys, then acknowledge only after successful completion. Load current authorized -agent/project/workspace data before acting; the journal contains no configuration -snapshots, repository credentials, or deletion authority. For offline plugin tests, -seed `lifecycleEvents` with `createTestHarness().seed()`. +Delivery is at least once. Re-read current authorized state and make provider +operations idempotent; a worker can finish an external effect and exit before +Core commits its acknowledgment. Content-free events contain no repository +credentials or configuration snapshots. Deleted projects may return `null`. +Use stable resource identifiers and fence delayed provider requests when removing +resources. Neither the callback nor an acknowledgment proves mounts are unused. -Lifecycle polls can page past failed resources using the last returned event id as -`afterId`. Reset `afterId` at the start of every polling sweep: it is a page -cursor, never a persisted high-water mark. This retries failures and includes -transactions that commit later with lower ids. +Delivery uses the same process-local transaction signals as Core's other durable +queues. Startup replays committed work. Independent database writers and separate +active server processes need an explicit ownership/wake protocol; see +[database work signals](../DATABASE.md#transaction-aware-delivery-work-signals). + +The existing `ctx.events.listLifecycle(companyId, limit?, afterId?)` and +`acknowledgeLifecycle(companyId, eventId)` remain available for manual consumers. +Do not mix automatic and manual acknowledgment for the same resources. Reads +return one pending event per resource (default 50, maximum 100). `afterId` is a +page cursor, never a durable high-water mark: restart pagination to revisit +failures and transactions that commit later with lower IDs. Offline SDK tests +can seed `lifecycleEvents` with `createTestHarness().seed()`. ## External object reference providers diff --git a/maps/agent-access-map/recovery.md b/maps/agent-access-map/recovery.md index c7cc042f969..81b52a87279 100644 --- a/maps/agent-access-map/recovery.md +++ b/maps/agent-access-map/recovery.md @@ -79,6 +79,17 @@ retry and some operator controls require board. Completion or cancellation of a task does not clear its exact unresolved native workspace owner hold. Physical owner release still requires the [operator stop-verification procedure](../../doc/native-workspace-finalization-recovery.md). +For a resolved legacy action that retains a file hold, the same resolution API +supports source-only file repair. Board runtime management and recovery-resolution +authority are required. The request identifies the exact source action/run and +records recovered-file evidence; the server verifies the original stop receipt. +It preserves the current task status and does not wake or replay the agent. The +task form fails closed without permission or source bindings. Agent Chat retains +the file diagnosis while this hold exists. The +[repair route regressions](../../server/src/__tests__/workspace-restore-repair-route.test.ts) +and [file form](../../ui/src/components/WorkspaceFileRecovery.test.tsx) cover the +local contract; they do not independently recover files or qualify a live provider. + Evidence: `source-only` — [implementation](../../server/src/routes/issues.ts#L9478) at the reviewed commit above. Exact owner hold retention in the [recovery state helper](../../server/src/services/native-runtime/native-workspace-finalization-recovery-state.ts) diff --git a/maps/feature-map/recovery.md b/maps/feature-map/recovery.md index 7d5a5945d6d..7e7c1ab34f8 100644 --- a/maps/feature-map/recovery.md +++ b/maps/feature-map/recovery.md @@ -121,6 +121,18 @@ The recovery-card suite covers workspace divergence, reissue, reconcile, and confirmation. [Workspace export](../../ui/src/components/WorkspaceExportRecovery.test.tsx) covers quiet restoration, legacy/operator assistance and resolved states. +For a retained legacy file hold, open the source task after its recovery action +has resolved but its files still need recovery. An authorized board operator can +record the verified recovered files and backup link without changing the current +task status or starting another run. The server must independently validate the +original stopped-execution receipt. Agent Chat must show the file hold rather than +suggesting another message. The [file recovery form](../../ui/src/components/WorkspaceFileRecovery.test.tsx), +[notice](../../ui/src/components/ExecutionBlockerNotice.test.tsx), and +[repair route](../../server/src/__tests__/workspace-restore-repair-route.test.ts) +cover evidence/confirmation, read-only access, stale or missing stop proof, +source-only repair, and preserved task status. These tests use fixtures; separately +verify original files and their attached artifact before recording a real repair. + For an abandoned native copyback owner, the harness verifies the exact stopped deployment and the reconciler restores saved work automatically. Task users see no process details or shutdown form. The documented operator API handles older diff --git a/maps/storybook-map/INDEX.md b/maps/storybook-map/INDEX.md index 6efcd072ddb..e59fed61840 100644 --- a/maps/storybook-map/INDEX.md +++ b/maps/storybook-map/INDEX.md @@ -4,7 +4,7 @@ [Contract and maintenance](README.md) · [Review findings](REVIEW.md) -263 story files; 2499 indexed stories. Counts describe inventory, not passed tests or complete feature coverage. +263 story files; 2503 indexed stories. Counts describe inventory, not passed tests or complete feature coverage. Status describes product alignment; implementation describes the rendered subject; boundary describes simulated data. Sources and limitations are in catalog.json. Related capabilities cross-list one canonical story file. @@ -18,10 +18,10 @@ Instance setup, company wizard, first agent and task. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Getting started and access/Onboarding and first work/Onboarding/Agent arc](../../ui/storybook/stories/onboarding/onboarding-agent-arc.stories.tsx) | `onboarding/adapter`, `onboarding/first-task` | unreviewed / production / fixtures | 31 / 11 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Four managed Codex cases use scoped immutable-image discovery and a stateful saved-probe fixture: native default, measured Legacy, permission refusal, and unconfirmed cleanup. Native isolation recipe added; these fixture setup responses do not establish server-resolved transport isolation, real sandbox cleanup, or local/SSH enforcement. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. | -| [Getting started and access/Onboarding and first work/Onboarding/Character](../../ui/storybook/stories/onboarding/onboarding-character.stories.tsx) | `onboarding/company` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The added native isolation recipe is outside this character preview’s demonstrated scope. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. | -| [Getting started and access/Onboarding and first work/Onboarding/Connect a model](../../ui/storybook/stories/onboarding/onboarding-connect-model.stories.tsx) | `onboarding/adapter`, `onboarding/first-task` | historical / prototype / fixtures | 5 / 0 | Older tile/mode preview; onboarding-agent-arc mounts the production wizard. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The expanded native isolation recipe is not exercised by this retained historical tile preview. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. | -| [Getting started and access/Onboarding and first work/Resume a saved draft](../../ui/storybook/stories/onboarding/resume-draft.stories.tsx) | `onboarding/resume` | current / production / fixtures | 4 / 3 | OnboardingWizard restores a seeded draft, malformed/unavailable-company handling and draft retention across a fixture remount. The reopen button belongs to the fixture host; real reload, account switch and storage failure remain separate checks. The merged wizard includes Codex harness/runner draft restoration and immutable-environment setup qualification. These isolated draft states do not establish the new native setup, task/follow-up, account or cleanup journey. | +| [Getting started and access/Onboarding and first work/Onboarding/Agent arc](../../ui/storybook/stories/onboarding/onboarding-agent-arc.stories.tsx) | `onboarding/adapter`, `onboarding/first-task` | unreviewed / production / fixtures | 31 / 11 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Four managed Codex cases use scoped immutable-image discovery and a stateful saved-probe fixture: native default, measured Legacy, permission refusal, and unconfirmed cleanup. Native isolation recipe added; these fixture setup responses do not establish server-resolved transport isolation, real sandbox cleanup, or local/SSH enforcement. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task. | +| [Getting started and access/Onboarding and first work/Onboarding/Character](../../ui/storybook/stories/onboarding/onboarding-character.stories.tsx) | `onboarding/company` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The added native isolation recipe is outside this character preview’s demonstrated scope. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task. | +| [Getting started and access/Onboarding and first work/Onboarding/Connect a model](../../ui/storybook/stories/onboarding/onboarding-connect-model.stories.tsx) | `onboarding/adapter`, `onboarding/first-task` | historical / prototype / fixtures | 5 / 0 | Older tile/mode preview; onboarding-agent-arc mounts the production wizard. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The expanded native isolation recipe is not exercised by this retained historical tile preview. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task. | +| [Getting started and access/Onboarding and first work/Resume a saved draft](../../ui/storybook/stories/onboarding/resume-draft.stories.tsx) | `onboarding/resume` | current / production / fixtures | 4 / 3 | OnboardingWizard restores a seeded draft, malformed/unavailable-company handling and draft retention across a fixture remount. The reopen button belongs to the fixture host; real reload, account switch and storage failure remain separate checks. The merged wizard includes Codex harness/runner draft restoration and immutable-environment setup qualification. These isolated draft states do not establish the new native setup, task/follow-up, account or cleanup journey. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task. | ### Login, invitations, and access @@ -34,13 +34,13 @@ Sessions, bootstrap, membership, roles, and CLI authorization. | [Getting started and access/Login, invitations, and access/CLI authorization](../../ui/storybook/stories/access/cli-authorization.stories.tsx) | `access/cli-auth` | current / production / fixtures | 9 / 5 | Real CliAuth route with pending/expired/unavailable/preflight denial, approval/cancel and write pending/error. Fictional challenge; no CLI identity or real token is authorized. | | [Getting started and access/Login, invitations, and access/Organization membership](../../ui/storybook/stories/access/membership.stories.tsx) | `access/membership` | current / production / fixtures | 7 / 2 | CompanyAccess member list and role change with loading/empty/failure/403 and failed mutation toast. Backend actor authority, invite lifecycle and durable membership are not verified. | | [Getting started and access/Login, invitations, and access/Private tasks/00 Review guide](../../ui/storybook/stories/access/private-tasks/00-review-guide.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Getting started and access/Login, invitations, and access/Private tasks/01 Creation](../../ui/storybook/stories/access/private-tasks/01-creation.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 22 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/01 Creation](../../ui/storybook/stories/access/private-tasks/01-creation.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 22 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. | | [Getting started and access/Login, invitations, and access/Private tasks/02 Sharing](../../ui/storybook/stories/access/private-tasks/02-sharing.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 26 / 10 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Getting started and access/Login, invitations, and access/Private tasks/03 Task actions](../../ui/storybook/stories/access/private-tasks/03-task-actions.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 11 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Getting started and access/Login, invitations, and access/Private tasks/04 References and blockers](../../ui/storybook/stories/access/private-tasks/04-references-and-blockers.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 12 / 5 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Getting started and access/Login, invitations, and access/Private tasks/05 Projects](../../ui/storybook/stories/access/private-tasks/05-projects.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 19 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Getting started and access/Login, invitations, and access/Private tasks/06 Full product pages](../../ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 21 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Getting started and access/Login, invitations, and access/Private tasks/07 User stories](../../ui/storybook/stories/access/private-tasks/07-user-stories.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 13 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Getting started and access/Login, invitations, and access/Private tasks/06 Full product pages](../../ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx) | `access/resources`, `tasks/properties`, `projects/lifecycle` | unreviewed / production / fixtures | 21 / 12 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. | +| [Getting started and access/Login, invitations, and access/Private tasks/07 User stories](../../ui/storybook/stories/access/private-tasks/07-user-stories.stories.tsx) | `access/resources`, `tasks/properties` | unreviewed / production / fixtures | 13 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. | | [Getting started and access/Login, invitations, and access/Sign in](../../ui/storybook/stories/access/sign-in.stories.tsx) | `access/login` | current / production / fixtures | 6 / 3 | AuthPage email form, session read loading/error, submission pending/failure and navigation to Companies after a fixture session. Real authentication, bootstrap claim and sign-out remain separate. | ### Companies and portability @@ -69,24 +69,24 @@ Creation, assignment, lists, properties, comments, and completion. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Tasks and conversations/Task creation and lifecycle/Paperclip/Assigned Backlog Safeguards](../../ui/storybook/stories/tasks/assigned-backlog-safeguards.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Tasks and conversations/Task creation and lifecycle/Product/Chat & Comments](../../ui/storybook/stories/tasks/chat-comments.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Completed activity preview](../../ui/storybook/stories/tasks/completed-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Tasks and conversations/Task creation and lifecycle/Paperclip/Assigned Backlog Safeguards](../../ui/storybook/stories/tasks/assigned-backlog-safeguards.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Tasks and conversations/Task creation and lifecycle/Product/Chat & Comments](../../ui/storybook/stories/tasks/chat-comments.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Completed activity preview](../../ui/storybook/stories/tasks/completed-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | | [Tasks and conversations/Task creation and lifecycle/Composer/Add menu](../../ui/storybook/stories/tasks/composer-add-menu.stories.tsx) | `tasks/create` | unreviewed / production / fixtures | 14 / 14 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Tasks and conversations/Task creation and lifecycle/Composer/Mobile assignee picker](../../ui/storybook/stories/tasks/composer-mobile-assignee.stories.tsx) | `tasks/create`, `runs-adapters/availability` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Tasks and conversations/Task creation and lifecycle/Composer/Model and effort picker](../../ui/storybook/stories/tasks/composer-model-picker.stories.tsx) | `tasks/create`, `runs-adapters/availability` | mixed / mixed / fixtures | 57 / 30 | Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Tasks and conversations/Task creation and lifecycle/Composer/Mobile assignee picker](../../ui/storybook/stories/tasks/composer-mobile-assignee.stories.tsx) | `tasks/create`, `runs-adapters/availability` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Tasks and conversations/Task creation and lifecycle/Composer/Model and effort picker](../../ui/storybook/stories/tasks/composer-model-picker.stories.tsx) | `tasks/create`, `runs-adapters/availability` | mixed / mixed / fixtures | 57 / 30 | Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | | [Tasks and conversations/Task creation and lifecycle/Product/Control Plane Surfaces](../../ui/storybook/stories/tasks/control-plane-surfaces.stories.tsx) | `tasks/organize`, `questions-and-approvals/governance`, `budgets-costs/budgets`, `activity/activity` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Product/Dialogs & Modals](../../ui/storybook/stories/tasks/dialogs-modals.stories.tsx) | `tasks/create`, `agents/hire`, `projects/lifecycle`, `goals/hierarchy` | unreviewed / production / fixtures | 17 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Tasks and conversations/Task creation and lifecycle/External Objects](../../ui/storybook/stories/tasks/external-objects.stories.tsx) | `tasks/properties` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Product/Issue Management](../../ui/storybook/stories/tasks/issue-management.stories.tsx) | `tasks/properties`, `tasks/organize` | unreviewed / production / fixtures | 11 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Focused creation-visibility stories mount the production collection with simulated hierarchy reads and assert hidden roots, opt-in inclusion, and the unchecked filter control. Creation visibility examples and fixture setup now live in a support module so the surface matrix stays below 1000 lines; exported story IDs are unchanged. Creation visibility now shares the subscribed preference across modern and legacy controls; hierarchy child reads share a bounded rendering budget. Source alignment inspected; server behavior is tested separately. Hierarchy fetch and rendering budgets are separate; loaded context remains during paging. The large-tree UI regression exercises delayed reads in both list interfaces. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Components/Entity pickers/Mobile](../../ui/storybook/stories/tasks/mobile-entity-pickers.stories.tsx) | `tasks/create` | unreviewed / production / fixtures | 3 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Composer/New task](../../ui/storybook/stories/tasks/new-task-composer.stories.tsx) | `tasks/create`, `workspaces/binding` | current / production / fixtures | 40 / 25 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. WorktreePicker, ReuseWorktree, CreateWithReusedWorktree and NewWorktreeAfterReuse exercise workspace selection and submitted fixture request values. Actual admission/binding and execution remain outside this story. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Runner activity preview](../../ui/storybook/stories/tasks/runner-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Tasks and conversations/Task creation and lifecycle/Paperclip/Successful Run Handoff](../../ui/storybook/stories/tasks/successful-run-handoff.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Task chat review fixes](../../ui/storybook/stories/tasks/task-chat-chain-of-thought.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Runner activity preview](../../ui/storybook/stories/tasks/runner-activity.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | current / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Tasks and conversations/Task creation and lifecycle/Paperclip/Successful Run Handoff](../../ui/storybook/stories/tasks/successful-run-handoff.stories.tsx) | `tasks/completion`, `recovery/diagnosis` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Task chat review fixes](../../ui/storybook/stories/tasks/task-chat-chain-of-thought.stories.tsx) | `tasks/discussion`, `runs-adapters/runs` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | | [Tasks and conversations/Task creation and lifecycle/Task chat/Mobile streaming follow](../../ui/storybook/stories/tasks/task-chat-mobile-follow.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Product/Task chat/System notice](../../ui/storybook/stories/tasks/task-chat-system-notice.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | -| [Tasks and conversations/Task creation and lifecycle/Tasks/Context notices](../../ui/storybook/stories/tasks/task-context-notices.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Task creation and lifecycle/Tasks/Context notices](../../ui/storybook/stories/tasks/task-context-notices.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 1 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. | | [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/00 Review guide](../../ui/storybook/stories/tasks/thread-human-identity/00-evidence.stories.tsx) | `tasks/discussion` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/01 Components](../../ui/storybook/stories/tasks/thread-human-identity/01-components.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Task creation and lifecycle/Chat & Comments/Human identity/02 Mixed-author journey](../../ui/storybook/stories/tasks/thread-human-identity/02-task-journey.stories.tsx) | `tasks/discussion` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | @@ -101,7 +101,7 @@ Child work, prerequisites, reviewers, and task-tree controls. | --- | --- | --- | --- | --- | | [Tasks and conversations/Delegation, dependencies, and signoff/Product/Decisions/Stalled review actions](../../ui/storybook/stories/task-coordination/issue-review-panel.stories.tsx) | `task-coordination/signoff` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Tasks and conversations/Delegation, dependencies, and signoff/Product/Issue Monitor surfaces](../../ui/storybook/stories/task-coordination/monitor-surfaces.stories.tsx) | `task-coordination/tree-control` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Tasks Created From a Task](../../ui/storybook/stories/task-coordination/originating-tasks.stories.tsx) | `task-coordination/delegation`, `projects/task-context` | unreviewed / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. | +| [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Tasks Created From a Task](../../ui/storybook/stories/task-coordination/originating-tasks.stories.tsx) | `task-coordination/delegation`, `projects/task-context` | unreviewed / production / fixtures | 14 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. | | [Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Sub-issues Workflow Checklist](../../ui/storybook/stories/task-coordination/sub-issues-workflow.stories.tsx) | `task-coordination/dependencies`, `task-coordination/delegation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Creation visibility now shares the subscribed preference across modern and legacy controls; hierarchy child reads share a bounded rendering budget. Source alignment inspected; server behavior is tested separately. Hierarchy fetch and rendering budgets are separate; loaded context remains during paging. The large-tree UI regression exercises delayed reads in both list interfaces. | | [Tasks and conversations/Delegation, dependencies, and signoff/Product/Task Watchdog surfaces](../../ui/storybook/stories/task-coordination/task-watchdog-surfaces.stories.tsx) | `task-coordination/tree-control` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | @@ -113,7 +113,7 @@ Personal triage, decision queues, unread/blocked views, search, and personal tas | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab](../../ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx) | `inbox-search/triage`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | +| [Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab](../../ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx) | `inbox-search/triage`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | | [Tasks and conversations/Inbox, decisions, and search/Decisions/DecisionCard](../../ui/storybook/stories/inbox-search/decision-card.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 9 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Inbox, decisions, and search/Pages/Decisions Desk](../../ui/storybook/stories/inbox-search/decisions-desk.stories.tsx) | `inbox-search/decisions` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. | | [Tasks and conversations/Inbox, decisions, and search/Personal task views](../../ui/storybook/stories/inbox-search/inbox-views.stories.tsx) | `inbox-search/views` | current / production / fixtures | 8 / 0 | Shipped Inbox host with Mine/Recent/Unread/Blocked under combined-inbox flags and list states. Approvals/join requests/run lists are isolated alongside tasks. Failed/denied list queries currently fall back to Inbox zero; personal participation/read metadata and server counts remain production verification. Mine/recent fixture tasks are human-created; the upstream agent-created-task filter changes are separate visibility scenarios. | @@ -129,10 +129,10 @@ Persistent conversations, discovery, handoff, and gated board chat. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Pages](../../ui/storybook/stories/agent-chat/agent-chat-sidebar.stories.tsx) | `agent-chat/discovery`, `agent-chat/history` | unreviewed / production / fixtures | 9 / 1 | Wrapper mounts production Layout and AgentChat; conversation selection and sends use in-memory routes. Directory name is not prototype evidence. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat](../../ui/storybook/stories/agent-chat/agent-chat.stories.tsx) | `agent-chat/discovery`, `agent-chat/history`, `agent-chat/handoff` | mixed / mixed / fixtures | 17 / 0 | Production conversation/task routes with a local ChatEntryLanding. Default-agent behavior and fallback API responses remain outside current-product parity. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. ProjectCreated, ExistingProject, MultipleRepositories, ProjectWithoutRepository and ProjectCreationFailed show linked handoff results/navigation through shipped chat/task hosts. Tool execution and persistence are simulated. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Pages](../../ui/storybook/stories/agent-chat/agent-chat-sidebar.stories.tsx) | `agent-chat/discovery`, `agent-chat/history` | unreviewed / production / fixtures | 9 / 1 | Wrapper mounts production Layout and AgentChat; conversation selection and sends use in-memory routes. Directory name is not prototype evidence. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements. | +| [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat](../../ui/storybook/stories/agent-chat/agent-chat.stories.tsx) | `agent-chat/discovery`, `agent-chat/history`, `agent-chat/handoff` | mixed / mixed / fixtures | 17 / 0 | Production conversation/task routes with a local ChatEntryLanding. Default-agent behavior and fallback API responses remain outside current-product parity. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. ProjectCreated, ExistingProject, MultipleRepositories, ProjectWithoutRepository and ProjectCreationFailed show linked handoff results/navigation through shipped chat/task hosts. Tool execution and persistence are simulated. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements. | | [Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Components](../../ui/storybook/stories/agent-chat/agent-conversation-sidebar.stories.tsx) | `agent-chat/discovery`, `agent-chat/history` | current / production / fixtures | 10 / 1 | Support module re-exports production AgentConversationSidebar; search/selection use fixture callbacks. | -| [Tasks and conversations/Agent conversations and project handoff/Design explorations/Chat entry](../../ui/storybook/stories/agent-chat/chat-entry.stories.tsx) | `agent-chat/discovery`, `agent-chat/history` | historical / mixed / fixtures | 9 / 3 | Retained first-created-agent sidebar/landing design predates personal primary-agent selection. Shared production conversation components remain useful. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Tasks and conversations/Agent conversations and project handoff/Design explorations/Chat entry](../../ui/storybook/stories/agent-chat/chat-entry.stories.tsx) | `agent-chat/discovery`, `agent-chat/history` | historical / mixed / fixtures | 9 / 3 | Retained first-created-agent sidebar/landing design predates personal primary-agent selection. Shared production conversation components remain useful. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements. | | [Tasks and conversations/Agent conversations and project handoff/Conference Room](../../ui/storybook/stories/agent-chat/conference-room.stories.tsx) | `agent-chat/conference-room` | current / production / fixtures | 5 / 4 | BoardChat empty, reply, send pending/error/403 through fixture SSE and persisted comment reads. 403 uses the shipped generic unavailable message; route gates and real Claude execution remain separate. | ### Questions and approvals @@ -162,7 +162,7 @@ Follow-ups, queue edits, interruption, and pause/resume. | --- | --- | --- | --- | --- | | [Tasks and conversations/Steering and queued messages/Draft recovery](../../ui/storybook/stories/steering/composer-draft-recovery.stories.tsx) | `steering/drafts` | current / production / fixtures | 6 / 3 | TaskChatComposer uses owned task-keyed browser drafts, uncertain submission fence, switch/restore and failed/accepted send callbacks. API receipt authority and real submission durability remain separate. Storybook-owned draft keys are reset using the matching retained submission ID on setup and cleanup; same-browser uncertain-to-empty/send switching is covered separately. | | [Tasks and conversations/Steering and queued messages/Composer/Steering and queued messages](../../ui/storybook/stories/steering/composer-queued-messages.stories.tsx) | `steering/queue`, `steering/edit-order-discard`, `steering/steer`, `steering/interrupt` | current / production / fixtures | 6 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. | -| [Tasks and conversations/Steering and queued messages/Accepted response delivery](../../ui/storybook/stories/steering/durable-response-queue.stories.tsx) | `steering/approval-queue`, `recovery/user-continuation` | current / production / fixtures | 7 / 6 | Accepted immutable response, cleanup wait, unavailable admission, empty queue and interrupt delivery states. Local callbacks remove delivered entries; durable queue/coalescing and fresh-session authority need production tests. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. The shipped pending-delivery path removes rows optimistically and shows no separate progress indicator; the Delivering story asserts that actual presentation. | +| [Tasks and conversations/Steering and queued messages/Accepted response delivery](../../ui/storybook/stories/steering/durable-response-queue.stories.tsx) | `steering/approval-queue`, `recovery/user-continuation` | current / production / fixtures | 7 / 6 | Accepted immutable response, cleanup wait, unavailable admission, empty queue and interrupt delivery states. Local callbacks remove delivered entries; durable queue/coalescing and fresh-session authority need production tests. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. The shipped pending-delivery path removes rows optimistically and shows no separate progress indicator; the Delivering story asserts that actual presentation. Retained legacy files use a separate recovery form with verified stop evidence; these exhaustion and response-queue examples do not establish file repair. | | [Tasks and conversations/Steering and queued messages/Surfaces/Interrupt Handoff](../../ui/storybook/stories/steering/interrupt-handoff.stories.tsx) | `steering/interrupt` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Tasks and conversations/Steering and queued messages/Composer/Paused task takeover](../../ui/storybook/stories/steering/paused-composer.stories.tsx) | `steering/stop-and-pause` | historical / prototype / fixtures | 8 / 0 | Retained local paused takeover; task-execution-controls exercises the shipped pause/stop controls. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. | | [Tasks and conversations/Steering and queued messages/Composer/Execution controls](../../ui/storybook/stories/steering/task-execution-controls.stories.tsx) | `steering/stop-and-pause` | current / production / fixtures | 22 / 6 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. | @@ -211,11 +211,11 @@ Agent identity, instructions, reporting lines, lifecycle, and built-ins. | [Agents and reusable capabilities/Hiring, configuration, and organization/Pages/Agent Detail](../../ui/storybook/stories/agents/agent-detail.stories.tsx) | `agents/lifecycle`, `agents/instructions` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Persistent files](../../ui/storybook/stories/agents/agent-files.stories.tsx) | `agents/instructions` | unreviewed / production / fixtures | 9 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Product/Agent Management](../../ui/storybook/stories/agents/agent-management.stories.tsx) | `agents/lifecycle`, `agents/instructions`, `execution-environments/assignment` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. ForcedKubernetesExecution and ForcedKubernetesMissingEnvironment mount AgentConfigForm with a forced execution target or missing-target warning. Read-only assignment presentation; no saved target or remote execution proof. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review of the Environment override and Codex lifecycle/ACP selectors confirms reuse of the harness SelectPopover with the existing option and save semantics; render/play and live execution of these updated controls remain unverified. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Personas/Full pages](../../ui/storybook/stories/agents/agent-persona-pages.stories.tsx) | `agents/organization`, `navigation-preferences/profile` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Personas/Full pages](../../ui/storybook/stories/agents/agent-persona-pages.stories.tsx) | `agents/organization`, `navigation-preferences/profile` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Personas](../../ui/storybook/stories/agents/agent-personas.stories.tsx) | `agents/organization`, `navigation-preferences/profile` | unreviewed / production / fixtures | 19 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Configuration refresh](../../ui/storybook/stories/agents/agent-settings-redesign.stories.tsx) | `agents/lifecycle`, `agents/instructions` | mixed / mixed / fixtures | 20 / 1 | Production configuration panels in a locally composed settings shell; full AgentDetail route parity remains unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Existing Legacy/native Codex editing, light/mobile and Advanced states retained unchanged. Source review of the Environment override and Codex lifecycle/ACP selectors confirms reuse of the harness SelectPopover with the existing option and save semantics; render/play and live execution of these updated controls remain unverified. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Product/Built-in Agents](../../ui/storybook/stories/agents/built-in-agents.stories.tsx) | `agents/built-in` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Codex runner/Creation](../../ui/storybook/stories/agents/codex-runner/creation.stories.tsx) | `agents/hire`, `runs-adapters/validation` | current / production / fixtures | 9 / 7 | Production NewAgent and AgentBasicsDialog with isolated API fixtures. Automatic and explicit Legacy choices, qualified/unqualified discovery, old native links, setup failure and returned hire identity are asserted in source. Fixture runtime selection follows the requested runner and simulated target qualification; render/play, production persistence and provider execution are not verified by this source pass. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. | +| [Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Codex runner/Creation](../../ui/storybook/stories/agents/codex-runner/creation.stories.tsx) | `agents/hire`, `runs-adapters/validation` | current / production / fixtures | 9 / 7 | Production NewAgent and AgentBasicsDialog with isolated API fixtures. Automatic and explicit Legacy choices, qualified/unqualified discovery, old native links, setup failure and returned hire identity are asserted in source. Fixture runtime selection follows the requested runner and simulated target qualification; render/play, production persistence and provider execution are not verified by this source pass. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Onboarding/External agent invitation/Components](../../ui/storybook/stories/agents/external-agent-invite/components.stories.tsx) | `agents/hire`, `access/invitations` | unreviewed / production / fixtures | 8 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Onboarding/External agent invitation/Journeys](../../ui/storybook/stories/agents/external-agent-invite/journeys.stories.tsx) | `agents/hire`, `access/invitations` | unreviewed / production / fixtures | 20 / 4 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Agents and reusable capabilities/Hiring, configuration, and organization/Onboarding/New agent](../../ui/storybook/stories/agents/new-agent-onboarding.stories.tsx) | `agents/hire` | historical / prototype / fixtures | 37 / 1 | Retained local wizard predates production ui/src/components/new-agent flow; shared fields do not make the wizard current. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. | @@ -237,21 +237,21 @@ Adapter/model setup, account validation, transcripts, and run history. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Onboarding/Saved connections](../../ui/storybook/stories/runs-adapters/agent-connection-reuse.stories.tsx) | `runs-adapters/validation`, `agents/hire` | current / production / fixtures | 7 / 6 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Usage limits](../../ui/storybook/stories/runs-adapters/ai-connection-usage.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 8 / 8 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Review](../../ui/storybook/stories/runs-adapters/ai-connections.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 46 / 15 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Connectors/Pool host](../../ui/storybook/stories/runs-adapters/connection-pool-connector.stories.tsx) | `runs-adapters/validation`, `connection-setup/catalog` | unreviewed / production / fixtures | 5 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The recipe adds bounded Telegram subscription maintenance regressions; this story does not establish real provider startup recovery. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Connection pools](../../ui/storybook/stories/runs-adapters/connection-pools.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 16 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Assistant connections/Dot Runner](../../ui/storybook/stories/runs-adapters/dot-runner-connection.stories.tsx) | `runs-adapters/validation`, `gateways-profiles/client-connect` | unreviewed / production / fixtures | 4 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/07 Subscription sign-in](../../ui/storybook/stories/runs-adapters/local-ai-subscription.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 4 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/00 Overview](../../ui/storybook/stories/runs-adapters/provider-routing-00-overview.stories.tsx) | `runs-adapters/validation` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/01 Onboarding](../../ui/storybook/stories/runs-adapters/provider-routing-01-onboarding.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 6 / 3 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/02 Connect](../../ui/storybook/stories/runs-adapters/provider-routing-02-connect.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 14 / 3 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/03 Agent](../../ui/storybook/stories/runs-adapters/provider-routing-03-agent.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 34 / 7 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/04 Manage](../../ui/storybook/stories/runs-adapters/provider-routing-04-manage.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 11 / 5 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/05 Recovery](../../ui/storybook/stories/runs-adapters/provider-routing-05-recovery.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 7 / 2 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/06 Production components](../../ui/storybook/stories/runs-adapters/provider-routing-06-production.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 5 / 5 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. | -| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Composer/Session goals](../../ui/storybook/stories/runs-adapters/session-goals.stories.tsx) | `runs-adapters/session` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Onboarding/Saved connections](../../ui/storybook/stories/runs-adapters/agent-connection-reuse.stories.tsx) | `runs-adapters/validation`, `agents/hire` | current / production / fixtures | 7 / 6 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Usage limits](../../ui/storybook/stories/runs-adapters/ai-connection-usage.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 8 / 8 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Review](../../ui/storybook/stories/runs-adapters/ai-connections.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 46 / 15 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Connectors/Pool host](../../ui/storybook/stories/runs-adapters/connection-pool-connector.stories.tsx) | `runs-adapters/validation`, `connection-setup/catalog` | unreviewed / production / fixtures | 5 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The recipe adds bounded Telegram subscription maintenance regressions; this story does not establish real provider startup recovery. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Connection pools](../../ui/storybook/stories/runs-adapters/connection-pools.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 16 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Assistant connections/Dot Runner](../../ui/storybook/stories/runs-adapters/dot-runner-connection.stories.tsx) | `runs-adapters/validation`, `gateways-profiles/client-connect` | unreviewed / production / fixtures | 4 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/07 Subscription sign-in](../../ui/storybook/stories/runs-adapters/local-ai-subscription.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 4 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/00 Overview](../../ui/storybook/stories/runs-adapters/provider-routing-00-overview.stories.tsx) | `runs-adapters/validation` | unreviewed / guide / static | 1 / 0 | Review guide retained; explanatory content does not establish runtime or production coverage. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/01 Onboarding](../../ui/storybook/stories/runs-adapters/provider-routing-01-onboarding.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 6 / 3 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/02 Connect](../../ui/storybook/stories/runs-adapters/provider-routing-02-connect.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 14 / 3 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/03 Agent](../../ui/storybook/stories/runs-adapters/provider-routing-03-agent.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 34 / 7 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/04 Manage](../../ui/storybook/stories/runs-adapters/provider-routing-04-manage.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 11 / 5 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/05 Recovery](../../ui/storybook/stories/runs-adapters/provider-routing-05-recovery.stories.tsx) | `runs-adapters/validation` | mixed / mixed / fixtures | 7 / 2 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/06 Production components](../../ui/storybook/stories/runs-adapters/provider-routing-06-production.stories.tsx) | `runs-adapters/validation` | unreviewed / production / fixtures | 5 / 5 | Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | +| [Agents and reusable capabilities/Runs, harnesses, and model accounts/Composer/Session goals](../../ui/storybook/stories/runs-adapters/session-goals.stories.tsx) | `runs-adapters/session` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup. | ### Skills and Skill Studio @@ -346,20 +346,20 @@ Stopped work, workspace repair, bounded continuation, and reconnect. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Projects and execution/Recovery/App/Connection recovery](../../ui/storybook/stories/recovery/cloud-access-recovery.stories.tsx) | `recovery/reconnect` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Task chat/Recovery notice](../../ui/storybook/stories/recovery/execution-blocker-notice.stories.tsx) | `recovery/diagnosis` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Tasks/Execution recovery](../../ui/storybook/stories/recovery/execution-recovery.stories.tsx) | `recovery/diagnosis` | unreviewed / production / fixtures | 18 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Product/Issue/Blocked notice](../../ui/storybook/stories/recovery/issue-blocked-notice.stories.tsx) | `recovery/diagnosis`, `task-coordination/dependencies` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Tasks/Model capacity retry](../../ui/storybook/stories/recovery/model-capacity-retry.stories.tsx) | `recovery/bounded-continuation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Exhaustion and user decisions](../../ui/storybook/stories/recovery/recovery-gap-states.stories.tsx) | `recovery/exhaustion`, `recovery/unknown-outcome` | current / production / fixtures | 7 / 7 | Exhaustion notice, blocked authority, retry pending/failure/accepted and inactive notice; unknown-outcome action card is rendered separately. Retry callbacks and disposition context are simulated; no real continuation proof. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. | -| [Projects and execution/Recovery/Design previews/Recovery notice](../../ui/storybook/stories/recovery/recovery-notice-preview.stories.tsx) | `recovery/diagnosis`, `recovery/source-action` | current / production / fixtures | 9 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Product/Issue Scheduled retry surfaces](../../ui/storybook/stories/recovery/scheduled-retry.stories.tsx) | `recovery/bounded-continuation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Paperclip/Source Issue Recovery](../../ui/storybook/stories/recovery/source-issue-recovery.stories.tsx) | `recovery/diagnosis`, `recovery/source-action` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Source review confirms ordinary recovery fixtures remain applicable after the native-owner resolution gate; native-owner stop verification has its own suite. | -| [Projects and execution/Recovery/Paperclip/Source-resolved Fold](../../ui/storybook/stories/recovery/source-resolved-fold.stories.tsx) | `recovery/diagnosis`, `recovery/source-action` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Workspace branch recovery/01 Repair card](../../ui/storybook/stories/recovery/workspace-branch-recovery/01-component.stories.tsx) | `recovery/workspace` | unreviewed / production / fixtures | 8 / 4 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Projects and execution/Recovery/Workspace branch recovery/02 Task journey](../../ui/storybook/stories/recovery/workspace-branch-recovery/02-task-journey.stories.tsx) | `recovery/workspace` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. | -| [Projects and execution/Recovery/Workspace finalization recovery/01 Notice](../../ui/storybook/stories/recovery/workspace-finalization-recovery/01-notice.stories.tsx) | `recovery/workspace`, `recovery/source-action` | current / production / fixtures | 6 / 2 | Production quiet status; automatic restoration, legacy operator fallback, queued and resolved states. No task repair form, process details or API simulation. Provider proof is verified by separate server and harness tests. | -| [Projects and execution/Recovery/Workspace finalization recovery/02 Task journey](../../ui/storybook/stories/recovery/workspace-finalization-recovery/02-task-journey.stories.tsx) | `recovery/workspace`, `recovery/source-action` | unreviewed / mixed / fixtures | 3 / 0 | Production shell, status, bubbles and composer in a local conversation composition; shows restoration and saved work waiting for confirmation. Fixture messages; not the shipped task page or live provider acceptance. | +| [Projects and execution/Recovery/App/Connection recovery](../../ui/storybook/stories/recovery/cloud-access-recovery.stories.tsx) | `recovery/reconnect` | unreviewed / production / fixtures | 3 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Task chat/Recovery notice](../../ui/storybook/stories/recovery/execution-blocker-notice.stories.tsx) | `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Tasks/Execution recovery](../../ui/storybook/stories/recovery/execution-recovery.stories.tsx) | `recovery/diagnosis` | unreviewed / production / fixtures | 18 / 2 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Product/Issue/Blocked notice](../../ui/storybook/stories/recovery/issue-blocked-notice.stories.tsx) | `recovery/diagnosis`, `task-coordination/dependencies` | unreviewed / production / fixtures | 7 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Tasks/Model capacity retry](../../ui/storybook/stories/recovery/model-capacity-retry.stories.tsx) | `recovery/bounded-continuation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Exhaustion and user decisions](../../ui/storybook/stories/recovery/recovery-gap-states.stories.tsx) | `recovery/exhaustion`, `recovery/unknown-outcome` | current / production / fixtures | 7 / 7 | Exhaustion notice, blocked authority, retry pending/failure/accepted and inactive notice; unknown-outcome action card is rendered separately. Retry callbacks and disposition context are simulated; no real continuation proof. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. Retained legacy files use a separate recovery form with verified stop evidence; these exhaustion and response-queue examples do not establish file repair. | +| [Projects and execution/Recovery/Design previews/Recovery notice](../../ui/storybook/stories/recovery/recovery-notice-preview.stories.tsx) | `recovery/diagnosis`, `recovery/source-action` | current / production / fixtures | 9 / 3 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Product/Issue Scheduled retry surfaces](../../ui/storybook/stories/recovery/scheduled-retry.stories.tsx) | `recovery/bounded-continuation` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Paperclip/Source Issue Recovery](../../ui/storybook/stories/recovery/source-issue-recovery.stories.tsx) | `recovery/diagnosis`, `recovery/source-action` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Source review confirms ordinary recovery fixtures remain applicable after the native-owner resolution gate; native-owner stop verification has its own suite. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Paperclip/Source-resolved Fold](../../ui/storybook/stories/recovery/source-resolved-fold.stories.tsx) | `recovery/diagnosis`, `recovery/source-action` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Workspace branch recovery/01 Repair card](../../ui/storybook/stories/recovery/workspace-branch-recovery/01-component.stories.tsx) | `recovery/workspace` | unreviewed / production / fixtures | 8 / 4 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Workspace branch recovery/02 Task journey](../../ui/storybook/stories/recovery/workspace-branch-recovery/02-task-journey.stories.tsx) | `recovery/workspace` | unreviewed / production / fixtures | 2 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Workspace finalization recovery/01 Notice](../../ui/storybook/stories/recovery/workspace-finalization-recovery/01-notice.stories.tsx) | `recovery/workspace`, `recovery/source-action` | current / production / fixtures | 6 / 2 | Production quiet status; automatic restoration, legacy operator fallback, queued and resolved states. No task repair form, process details or API simulation. Provider proof is verified by separate server and harness tests. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Projects and execution/Recovery/Workspace finalization recovery/02 Task journey](../../ui/storybook/stories/recovery/workspace-finalization-recovery/02-task-journey.stories.tsx) | `recovery/workspace`, `recovery/source-action` | unreviewed / mixed / fixtures | 3 / 0 | Production shell, status, bubbles and composer in a local conversation composition; shows restoration and saved work waiting for confirmation. Fixture messages; not the shipped task page or live provider acceptance. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | ## Apps, channels, and extensions @@ -419,7 +419,7 @@ Provider-specific identity, threads, files, delivery, and endpoint upkeep. | [Apps, channels, and extensions/External chat and email/Task-linked files](../../ui/storybook/stories/chat-channels/channel-attachments.stories.tsx) | `chat-channels/attachments` | current / production / fixtures | 5 / 2 | TaskChatBubble with received/outbound saved attachment receipts, no/multiple files and missing filename. File links are fictional; no download, provider transfer or authorization is proved. | | [Apps, channels, and extensions/External chat and email/Connections/Chat connection actions](../../ui/storybook/stories/chat-channels/chat-connection-actions.stories.tsx) | `chat-channels/providers` | unreviewed / production / fixtures | 1 / 1 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the added connection/destination-default recipe; these unchanged setup, identity or conversation examples do not establish default selection or new-task inheritance. | | [Apps, channels, and extensions/External chat and email/Connections/Chat management](../../ui/storybook/stories/chat-channels/chat-management/chat-management.stories.tsx) | `chat-channels/lifecycle`, `chat-channels/identity`, `chat-channels/conversation` | unreviewed / production / fixtures | 15 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation: the mounted endpoint adds connection and destination editors. Local endpoint PATCH/resource PUT preserve defaults; workspace-choice requests now return only active/idle, unclosed fixture workspaces. Browser save/error/reload and new-conversation inheritance remain unverified. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Inspected channel task-default controls, optional API fields, avatar rendering and matching fixture updates; live defaults inheritance and permission checks remain outside these stories. Access, InvitePeople, ConnectAndTest and Conversations render the shipped endpoint detail with simulated identity linking and conversation data. External delivery and identity authorization are not live. | -| [Apps, channels, and extensions/External chat and email/Connectors/GitHub bot management](../../ui/storybook/stories/chat-channels/github-bot-management/management.stories.tsx) | `chat-channels/providers`, `chat-channels/conversation` | unreviewed / production / fixtures | 33 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation: mounted repository and endpoint editors use simulated defaults. Endpoint PATCH now persists omitted/null/concrete values across GET and resets on remount; repository PUT already preserves them. Choice caches intentionally contain no projects/workspaces. Browser editor interactions, access denials and new-task inheritance remain unverified. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Inspected channel task-default controls, optional API fields, avatar rendering and matching fixture updates; live defaults inheritance and permission checks remain outside these stories. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Apps, channels, and extensions/External chat and email/Connectors/GitHub bot management](../../ui/storybook/stories/chat-channels/github-bot-management/management.stories.tsx) | `chat-channels/providers`, `chat-channels/conversation` | unreviewed / production / fixtures | 33 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation: mounted repository and endpoint editors use simulated defaults. Endpoint PATCH now persists omitted/null/concrete values across GET and resets on remount; repository PUT already preserves them. Choice caches intentionally contain no projects/workspaces. Browser editor interactions, access denials and new-task inheritance remain unverified. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Inspected channel task-default controls, optional API fields, avatar rendering and matching fixture updates; live defaults inheritance and permission checks remain outside these stories. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task. | | [Apps, channels, and extensions/External chat and email/Historical/GitHub chat prototypes](../../ui/storybook/stories/chat-channels/github-chat.stories.tsx) | `chat-channels/providers`, `chat-channels/conversation` | historical / prototype / fixtures | 48 / 9 | Historical wizard retained for original setup/access/review design; current GitHub setup is ui/src/pages/apps/chat/GitHubChatSetup.tsx. Task-workspace reconciliation reviewed the added connection/destination-default recipe; these unchanged setup, identity or conversation examples do not establish default selection or new-task inheritance. | | [Apps, channels, and extensions/External chat and email/Connections/GitHub and Code Review Bot](../../ui/storybook/stories/chat-channels/github-connections.stories.tsx) | `chat-channels/providers`, `connection-setup/channel-setup` | unreviewed / production / fixtures | 7 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the added connection/destination-default recipe; these unchanged setup, identity or conversation examples do not establish default selection or new-task inheritance. The recipe adds bounded Telegram subscription maintenance regressions; this story does not establish real provider startup recovery. | | [Apps, channels, and extensions/External chat and email/Connections/iMessage Photon](../../ui/storybook/stories/chat-channels/imessage-photon.stories.tsx) | `chat-channels/conversation`, `chat-channels/providers`, `chat-channels/identity`, `chat-channels/lifecycle`, `connection-setup/channel-setup`, `connection-setup/failure` | unreviewed / production / fixtures | 13 / 7 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation reviewed the endpoint editor addition; this suite selects setup/access and locally simulated conversation states. Endpoint PATCH retains fields, but settings workspace-choice requests and default selection are not covered by these scenarios. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Inspected channel task-default controls, optional API fields, avatar rendering and matching fixture updates; live defaults inheritance and permission checks remain outside these stories. Catalog, ChooseAgent, Credentials, Access, Reconnect and ProviderOutageRecovery render shipped setup/detail states with simulated provider data. No live iMessage connection, delivery or recovery claim. The recipe adds bounded Telegram subscription maintenance regressions; this story does not establish real provider startup recovery. | @@ -454,8 +454,8 @@ Company/user credentials, grants, proposals, and vault import. | Story suite | Capabilities | Product / implementation / data | Stories / play functions | Review note | | --- | --- | --- | --- | --- | -| [Apps, channels, and extensions/Secrets and proposals/Task credential access/01 Notice](../../ui/storybook/stories/secrets/credential-access/01-notice.stories.tsx) | `secrets/runtime-delivery`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. | -| [Apps, channels, and extensions/Secrets and proposals/Task credential access/02 Task page](../../ui/storybook/stories/secrets/credential-access/02-task-page.stories.tsx) | `secrets/runtime-delivery`, `recovery/diagnosis` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. | +| [Apps, channels, and extensions/Secrets and proposals/Task credential access/01 Notice](../../ui/storybook/stories/secrets/credential-access/01-notice.stories.tsx) | `secrets/runtime-delivery`, `recovery/diagnosis` | unreviewed / production / fixtures | 5 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. | +| [Apps, channels, and extensions/Secrets and proposals/Task credential access/02 Task page](../../ui/storybook/stories/secrets/credential-access/02-task-page.stories.tsx) | `secrets/runtime-delivery`, `recovery/diagnosis` | unreviewed / production / fixtures | 6 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements. | | [Apps, channels, and extensions/Secrets and proposals/Product/Environment Variables Editor](../../ui/storybook/stories/secrets/environment-variables-editor.stories.tsx) | `secrets/runtime-delivery` | unreviewed / production / fixtures | 11 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. | | [Apps, channels, and extensions/Secrets and proposals/Proposal review](../../ui/storybook/stories/secrets/proposal-review.stories.tsx) | `secrets/proposals` | current / production / fixtures | 7 / 3 | Pending proposal, authority preflight block, list states and rejected proposal/rejection error. ProposalsTab and its dialogs are shipped; secret creation, values and grant authority are not exercised. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. | | [Apps, channels, and extensions/Secrets and proposals/Product/Secrets](../../ui/storybook/stories/secrets/secrets.stories.tsx) | `secrets/company-secrets`, `secrets/vault-import` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. | @@ -548,7 +548,7 @@ Company health, live work, activity, runs, routines, and timeline. | --- | --- | --- | --- | --- | | [Oversight and operation/Dashboards and audit trails/Tools/Activity (audit feed)](../../ui/storybook/stories/activity/activity-feed.stories.tsx) | `activity/activity`, `gateways-profiles/activity` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | | [Oversight and operation/Dashboards and audit trails/Apps/App detail · Activity (lifecycle)](../../ui/storybook/stories/activity/app-activity-lifecycle.stories.tsx) | `activity/activity`, `gateways-profiles/activity` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | -| [Oversight and operation/Dashboards and audit trails/Product/Data Visualization & Misc](../../ui/storybook/stories/activity/data-viz-misc.stories.tsx) | `activity/overview` | unreviewed / production / fixtures | 21 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | +| [Oversight and operation/Dashboards and audit trails/Product/Data Visualization & Misc](../../ui/storybook/stories/activity/data-viz-misc.stories.tsx) | `activity/overview` | unreviewed / production / fixtures | 21 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task. | | [Oversight and operation/Dashboards and audit trails/Execution history](../../ui/storybook/stories/activity/run-audit.stories.tsx) | `activity/runs` | current / production / fixtures | 6 / 4 | AuditRuns list, completed receipt, empty/loading/failed/denied and no-match filter. Run records are fixture snapshots; no real execution or scoped audit proof. | | [Oversight and operation/Dashboards and audit trails/Pages/Work Timeline](../../ui/storybook/stories/activity/work-timeline.stories.tsx) | `activity/timeline` | unreviewed / production / fixtures | 4 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. | @@ -609,7 +609,7 @@ Shared components and contributor guidance outside the product feature index. | [Foundations/Primitive Matrix](../../ui/storybook/stories/foundations/foundations.stories.tsx) | `developer-labs/design` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Related design reference: demonstrates shared production controls and visual states in an isolated contributor fixture; retains its Foundations home. No product-journey or baseline comparison claim. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. | | [Foundations/Primitive Coverage](../../ui/storybook/stories/foundations/primitives-coverage.stories.tsx) | `developer-labs/design` | unreviewed / production / fixtures | 11 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Related design reference: demonstrates shared production controls and visual states in an isolated contributor fixture; retains its Foundations home. No product-journey or baseline comparison claim. | | [Foundations/Status Language](../../ui/storybook/stories/foundations/status-language.stories.tsx) | `developer-labs/design` | unreviewed / production / fixtures | 1 / 0 | Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Related design reference: demonstrates shared production controls and visual states in an isolated contributor fixture; retains its Foundations home. No product-journey or baseline comparison claim. | -| [Overview/Storybook Guide](../../ui/storybook/stories/overview/overview.stories.tsx) | Shared foundations or contributor guide; no single business capability owns this suite. | unreviewed / guide / static | 1 / 0 | Matches the upstream feature index and order, including features with no cataloged examples. Related suite links and classifications do not imply execution coverage. The generated upstream feature index was reconciled; example scope and classifications are retained. | +| [Overview/Storybook Guide](../../ui/storybook/stories/overview/overview.stories.tsx) | Shared foundations or contributor guide; no single business capability owns this suite. | unreviewed / guide / static | 1 / 0 | Matches the upstream feature index and order, including features with no cataloged examples. Related suite links and classifications do not imply execution coverage. The generated upstream feature index was reconciled; example scope and classifications are retained. Reviewed the regenerated liveness capability description against the feature recipe; the guide remains a static index and does not prove live readiness. | ## Capability lookup diff --git a/maps/storybook-map/REVIEW.md b/maps/storybook-map/REVIEW.md index 95aa427f4d6..60bc0e986b4 100644 --- a/maps/storybook-map/REVIEW.md +++ b/maps/storybook-map/REVIEW.md @@ -829,3 +829,29 @@ The existing catalog generator/checker, all 27 catalog/generator/map controls an the 35-area/175-capability map check pass. This is source inventory and structural verification only: no additional rendering, play, visual baseline, paid provider call or live production acceptance was performed. + +## Draft reload and retained-file recovery reconciliation — 2026-10-13 + +Reconciled candidate `12046f522bca29ded24425be71b856993b4997f7` with master +`393d1c410862aa93561548e0a39feec3b89df6ff`. Retained every authored suite, stable +ID, classification, capability association and both review histories. The catalog +contains 263 files and 2,503 authored story IDs, including the four incoming +retained-file recovery states. Combined five notes that changed on both branches +so neither the Codex nor recovery limitations were discarded. + +Reviewed the fixed OnboardingWizard opening/draft logic and the onboarding and +managed-credential verification recipes before refreshing 33 affected source +fingerprints. The wizard component controls cover authorized reload, delayed +company context, explicit opening and foreign-company isolation; they do not +close the recorded managed first-task reload failure. The merged IssueDetail +passes the current task status and board runtime/recovery authority to the +retained-file form. Its fixture stories do not verify real recovered files, +provider shutdown or saved-account cleanup. Shared setup is unchanged. + +This reconciliation records source inventory only. Existing rendering, play and +production results retain their literal tested revisions; no new browser run, +visual baseline, provider call or live acceptance is claimed. + +The maintained catalog/index checker, all 27 catalog/generator/map controls and +the 35-area/175-capability structural map check pass. No unowned generated +artifacts or authored story files changed during this reconciliation. diff --git a/maps/storybook-map/VERIFICATION.md b/maps/storybook-map/VERIFICATION.md index 8a18ec2479c..6ad0b1157ed 100644 --- a/maps/storybook-map/VERIFICATION.md +++ b/maps/storybook-map/VERIFICATION.md @@ -1,8 +1,8 @@ # Verification routes for Storybook capabilities -Updated 2026-10-13. Production source is reconciled with master `252d9b5077`; step-3 story and fixture changes are recorded in the catalog fingerprints. +Updated 2026-10-13. Source inventory is reconciled from candidate `12046f522b` and master `393d1c4108`; the authorized draft reload fix, retained-file recovery states and inspected recipe changes are recorded in the catalog fingerprints. -The catalog has 262 authored suites, 2,481 authored story IDs and 175 feature-map capabilities. The 37 new production-subject suites add 257 scenarios and associate all 49 UI capabilities identified in step 2. **11 capabilities remain without authored examples: 8 CLI/operator operations and 3 real-browser concerns.** Generated Overview pages and guides do not count as examples. Existing suites and story IDs are retained. +The catalog has 263 authored suites, 2,503 authored story IDs and 175 feature-map capabilities. The 37 new production-subject suites add 257 scenarios and associate all 49 UI capabilities identified in step 2. **11 capabilities remain without authored examples: 8 CLI/operator operations and 3 real-browser concerns.** Generated Overview pages and guides do not count as examples. Existing suites and story IDs are retained. The four retained-file recovery states from master `393d1c4108` are preserved alongside the Codex suite and examples previously merged from master `c8efdbfba43`; this inventory update does not claim an additional production verification run. These associations provide initial isolated UI coverage, not complete parity with every feature recipe. The original scenario inventory below stays as a maintenance checklist; an association does not mark every listed variant complete. In particular, import examples cover preview, organization archival examples cover the list/restoration side, and custom-image examples cover metadata/configuration rather than a real setup terminal. Existing unreviewed suites remain unreviewed. diff --git a/maps/storybook-map/catalog.json b/maps/storybook-map/catalog.json index 6106206d2b5..a3f7208257a 100644 --- a/maps/storybook-map/catalog.json +++ b/maps/storybook-map/catalog.json @@ -284,7 +284,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Wrapper mounts production Layout and AgentChat; conversation selection and sends use in-memory routes. Directory name is not prototype evidence. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Wrapper mounts production Layout and AgentChat; conversation selection and sends use in-memory routes. Directory name is not prototype evidence. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -306,7 +306,7 @@ "ui/storybook/stories/agent-chat/agent-chat-sidebar.stories.tsx", "ui/src/adapters/adapter-display-registry.ts" ], - "fingerprint": "2973cbd7b859db2b2535a0e3f80402c3755801627d4efa3ff44de380050a2a55", + "fingerprint": "597dcadf01d44933b36074bbb7cee1f1b7a51e3282a5aa1f502fa5d65f7bb60c", "stableId": "design-explorations-agent-chat-sidebar-pages", "sidebarTitle": "Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat sidebar/Pages" }, @@ -345,7 +345,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Production conversation/task routes with a local ChatEntryLanding. Default-agent behavior and fallback API responses remain outside current-product parity. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. ProjectCreated, ExistingProject, MultipleRepositories, ProjectWithoutRepository and ProjectCreationFailed show linked handoff results/navigation through shipped chat/task hosts. Tool execution and persistence are simulated. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Production conversation/task routes with a local ChatEntryLanding. Default-agent behavior and fallback API responses remain outside current-product parity. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. ProjectCreated, ExistingProject, MultipleRepositories, ProjectWithoutRepository and ProjectCreationFailed show linked handoff results/navigation through shipped chat/task hosts. Tool execution and persistence are simulated. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -367,7 +367,7 @@ "ui/storybook/stories/agent-chat/agent-chat.stories.tsx", "ui/src/adapters/adapter-display-registry.ts" ], - "fingerprint": "052bff02117a14b972e48ee4b773f8258f05bbc1dd481d36b535d3240b8b992f", + "fingerprint": "b27f2b222b355a385524824a0cd6aa2d52997ee45301f71262055f1bce425af5", "stableId": "design-explorations-agent-chat", "sidebarTitle": "Tasks and conversations/Agent conversations and project handoff/Design explorations/Agent chat" }, @@ -381,7 +381,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agents.md", @@ -390,7 +390,7 @@ "ui/storybook/fixtures/onboardingEnvironment.ts", "ui/storybook/stories/runs-adapters/agent-connection-reuse.stories.tsx" ], - "fingerprint": "b6cc8fdc833f2216e861e044a92c554eafa48c6b858c9b865d0be1ab4b020534", + "fingerprint": "bbce05148164ab25761a964a86194d7933bd6280e70b6ed8d279bfd2a1dbcded", "stableId": "onboarding-saved-connections", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/Onboarding/Saved connections" }, @@ -507,7 +507,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agents.md", @@ -523,7 +523,7 @@ "ui/storybook/stories/agents/agent-persona-pages.stories.tsx", "ui/src/adapters/adapter-display-registry.ts" ], - "fingerprint": "11e416bdddb44ece283d934eaf87f30eb85b04e5270c06d7bbba815c89484a76", + "fingerprint": "9b8b3116572467ccb0c44e851cb37f4a9b54a483f5a92f4382712dca170e0c9b", "stableId": "agents-personas-full-pages", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Personas/Full pages" }, @@ -760,7 +760,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -768,7 +768,7 @@ "ui/storybook/prototypes/AiConnectionUsageReview.tsx", "ui/storybook/stories/runs-adapters/ai-connection-usage.stories.tsx" ], - "fingerprint": "ff1a30b9b6589b853dd43195d8873d9b26e18669e0d1731bb188ec70c7421275", + "fingerprint": "15b5f99cdafc51c9dc9ee8ffdb838f7aef659eb6d46594b98de354e3283b01ee", "stableId": "ai-connections-usage-limits", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Usage limits" }, @@ -781,7 +781,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -805,7 +805,7 @@ "ui/storybook/prototypes/AiTaskConnectionReview.tsx", "ui/storybook/stories/runs-adapters/ai-connections.stories.tsx" ], - "fingerprint": "37d6bba75d71ce8749b216f6c3fe2968ee7e45fcc153f8ffd9905671082e7a02", + "fingerprint": "964f5e903c699b242f202dd6fd06fc0d3c2113514403c9329b1bf0b10f2a36aa", "stableId": "ai-connections-review", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Review" }, @@ -1199,7 +1199,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -1210,7 +1210,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/assigned-backlog-safeguards.stories.tsx" ], - "fingerprint": "d6e735a2c56092274f139709aba0c1c555f3f3cb0fbf9820d50ee02874907b79", + "fingerprint": "634be5456a6554553c9c19852941533fd657b48ea9aaf97ea2774a5b52fed8d0", "stableId": "paperclip-assigned-backlog-safeguards", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Paperclip/Assigned Backlog Safeguards" }, @@ -1224,7 +1224,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/inbox-search.md", @@ -1234,7 +1234,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/inbox-search/blocked-inbox.stories.tsx" ], - "fingerprint": "70b54f5ce7c083b8e64c28501d66247d4753adabd732b377f7ad18309dc90ffe", + "fingerprint": "1ef985b27fe351ee34b584ca878c66819fd46986eb09dbce59c408c66b35a495", "stableId": "product-inbox-blocked-tab", "sidebarTitle": "Tasks and conversations/Inbox, decisions, and search/Product/Inbox/Blocked tab" }, @@ -1403,7 +1403,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1417,7 +1417,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/chat-comments.stories.tsx" ], - "fingerprint": "fcf806c76d34c706e80e8ae0a7b66a39dd9bdd556dc37c16ae9c4ebd20b8de67", + "fingerprint": "9736a5fcb89328b5d0c41c06d6d836d5e069efc983f9be891f7ad4df21316745", "stableId": "product-chat-comments", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Product/Chat & Comments" }, @@ -1451,7 +1451,7 @@ "status": "historical", "implementation": "mixed", "boundary": "fixtures", - "notes": "Retained first-created-agent sidebar/landing design predates personal primary-agent selection. Shared production conversation components remain useful. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Retained first-created-agent sidebar/landing design predates personal primary-agent selection. Shared production conversation components remain useful. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agent-chat.md", @@ -1473,7 +1473,7 @@ "ui/storybook/stories/agent-chat/chat-entry.stories.tsx", "ui/src/adapters/adapter-display-registry.ts" ], - "fingerprint": "1063df44a6ff6e8972e15c4fa4718ea12d1edbe1ae5816055ab12a9964f007a8", + "fingerprint": "92a29205dfb9afa59f73098136c1becff2c95558596ed659a025fa88cbf0c57f", "replacement": "ui/storybook/stories/agents/primary-agent/05-chat.stories.tsx", "stableId": "design-explorations-chat-entry", "sidebarTitle": "Tasks and conversations/Agent conversations and project handoff/Design explorations/Chat entry" @@ -1515,14 +1515,14 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", "ui/src/components/CloudAccessGate.tsx", "ui/storybook/stories/recovery/cloud-access-recovery.stories.tsx" ], - "fingerprint": "64b2422bdabe2b07b563354481000171455841e49ccbf6a72270066d4e27e9e0", + "fingerprint": "65051e49291a3917a3b62604fa76747d2df1825f2601f9abbf2e4ab45b5add9d", "stableId": "app-connection-recovery", "sidebarTitle": "Projects and execution/Recovery/App/Connection recovery" }, @@ -1536,7 +1536,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1546,7 +1546,7 @@ "ui/storybook/prototypes/completed-activity/CompletedActivityPreview.tsx", "ui/storybook/stories/tasks/completed-activity.stories.tsx" ], - "fingerprint": "51e7312502a71dcaaf53b4727071e4249a8451e5bd54c88803a0094492da1f67", + "fingerprint": "888e749d4aef07ab30de3f35065572b010bd69a40fc4db88e99d400138b7e12b", "stableId": "tasks-completed-activity-preview", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Completed activity preview" }, @@ -1583,7 +1583,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1596,7 +1596,7 @@ "ui/storybook/prototypes/composer-model-picker/fixtures.ts", "ui/storybook/stories/tasks/composer-mobile-assignee.stories.tsx" ], - "fingerprint": "9c73dbc008698bec18b16cdf28143c023234e3614459712aac0704a0f164a084", + "fingerprint": "34a636e15ee140a1ad4aff98f4704bb4a453756a7ed4d9ebed2a7130d278d1e6", "stableId": "composer-mobile-assignee-picker", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Composer/Mobile assignee picker" }, @@ -1610,7 +1610,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Most examples use a local picker implementation; live picker examples use ComposerRunSettingsLiveStory. Do not count the whole suite as production parity. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1624,7 +1624,7 @@ "ui/storybook/prototypes/composer-model-picker/fixtures.ts", "ui/storybook/stories/tasks/composer-model-picker.stories.tsx" ], - "fingerprint": "7c05be2d6fb40c37df21930bed17634dd52b8bee1839ff69e9d137ad5eb7fca3", + "fingerprint": "e479f7f11feac2ce5f78ba24cb276407e62d0679b9aee7dd4c71f1b4b1d6340d", "stableId": "composer-model-and-effort-picker", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Composer/Model and effort picker" }, @@ -1712,7 +1712,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The recipe adds bounded Telegram subscription maintenance regressions; this story does not establish real provider startup recovery.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The recipe adds bounded Telegram subscription maintenance regressions; this story does not establish real provider startup recovery. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/connection-setup.md", @@ -1725,7 +1725,7 @@ "ui/storybook/prototypes/ConnectionPoolConnectorHost.tsx", "ui/storybook/stories/runs-adapters/connection-pool-connector.stories.tsx" ], - "fingerprint": "314b4fa2687a6fc836780b14265cb0ecf245b5d3b71c38be2444630774232fe5", + "fingerprint": "048b24b11a23f8d6beab12d23766c6ca2c25defcfc976edf6d16c715945b3e85", "stableId": "connectors-pool-host", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/Connectors/Pool host" }, @@ -1738,7 +1738,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -1751,7 +1751,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/runs-adapters/connection-pools.stories.tsx" ], - "fingerprint": "64e20fbe133b335a417184f1eed4df4c5ff6f827e3ae96b1e5463d0c8849a411", + "fingerprint": "cdc847e486e7ad16aa349f405535c789e52c372aef25232ee71fb47e6c247cbe", "stableId": "ai-connections-connection-pools", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Connection pools" }, @@ -1845,7 +1845,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -1853,7 +1853,7 @@ "ui/src/components/task-chat/TaskChatMarker.tsx", "ui/storybook/stories/secrets/credential-access/01-notice.stories.tsx" ], - "fingerprint": "505e42e0a40afea13cf02b65bad4488650e2c56a5a4a720b4b9a2c193f4d2e70", + "fingerprint": "e47db04211055da618b8759ca77f1c725fd6370a660619fa621e7df53c6e140a", "stableId": "task-credential-access-01-notice", "sidebarTitle": "Apps, channels, and extensions/Secrets and proposals/Task credential access/01 Notice" }, @@ -1867,7 +1867,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Agent-owned instance secrets remain in trusted server runtime and are excluded from ordinary agent delivery; these UI examples do not verify that lifecycle contract. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery. The merged task page passes task status and runtime/recovery authority to the retained-file form; its stopped-execution and file evidence remain separate live requirements.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -1880,7 +1880,7 @@ "ui/storybook/support/credential-access/CredentialTaskPage.tsx", "ui/src/adapters/adapter-display-registry.ts" ], - "fingerprint": "9314a73161f51137c1dee179818b953c8e58f96c03666c9bd3845f20d2dfdc07", + "fingerprint": "0707a64258e35c1c1cd941304482c17a89d00f4ed02c7b11ec16c060ebdb2c6c", "stableId": "task-credential-access-02-task-page", "sidebarTitle": "Apps, channels, and extensions/Secrets and proposals/Task credential access/02 Task page" }, @@ -1914,7 +1914,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/activity.md", @@ -1935,7 +1935,7 @@ "ui/src/components/CodexRunnerSelect.tsx", "ui/src/components/ui/select.tsx" ], - "fingerprint": "bd49f3a69a7e387bef6679af653180efff259d7a4e58d5401ebd5ece7f8efc86", + "fingerprint": "9e31b126875ae54e0a8249dbc743170a2cc1f7076d5ccb33ab0595122d69db61", "stableId": "product-data-visualization-misc", "sidebarTitle": "Oversight and operation/Dashboards and audit trails/Product/Data Visualization & Misc" }, @@ -2165,7 +2165,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/gateways-profiles.md", @@ -2173,7 +2173,7 @@ "ui/src/components/DotRunnerConnection.tsx", "ui/storybook/stories/runs-adapters/dot-runner-connection.stories.tsx" ], - "fingerprint": "9750e085d11a6c3c9f2edafe906ca6d3a20e22e147524097b2963c3e3e33751f", + "fingerprint": "5a4634b48c66f7bcccbc5cad2f96411e4dc2295dd6b5d6ffc08af78514109432", "stableId": "assistant-connections-dot-runner", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/Assistant connections/Dot Runner" }, @@ -2229,14 +2229,16 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", "ui/src/components/ExecutionBlockerNotice.tsx", - "ui/storybook/stories/recovery/execution-blocker-notice.stories.tsx" + "ui/src/components/WorkspaceFileRecovery.tsx", + "ui/storybook/stories/recovery/execution-blocker-notice.stories.tsx", + "ui/storybook/support/workspace-file-recovery/fixture.tsx" ], - "fingerprint": "018d8d687921ea109cb98f587abd59b94722f1f148940852326417ce8a457b04", + "fingerprint": "cb2da1b3f76b1b644f7b11fb6f55c27950ae51ce3e590ead878d3703606588d7", "stableId": "task-chat-recovery-notice", "sidebarTitle": "Projects and execution/Recovery/Task chat/Recovery notice" }, @@ -2249,7 +2251,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -2260,7 +2262,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/recovery/execution-recovery.stories.tsx" ], - "fingerprint": "82ddff0cf6d98e558f9038d59960758347ad39f3c7a9aaf18f29214a8ad81756", + "fingerprint": "4cfa6fd775c9cf04e0586ed5a7218df3e8bb51dcaadf80b9a26a979aabf61e98", "stableId": "tasks-execution-recovery", "sidebarTitle": "Projects and execution/Recovery/Tasks/Execution recovery" }, @@ -2588,7 +2590,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation: mounted repository and endpoint editors use simulated defaults. Endpoint PATCH now persists omitted/null/concrete values across GET and resets on remount; repository PUT already preserves them. Choice caches intentionally contain no projects/workspaces. Browser editor interactions, access denials and new-task inheritance remain unverified. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Inspected channel task-default controls, optional API fields, avatar rendering and matching fixture updates; live defaults inheritance and permission checks remain outside these stories. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Task-workspace reconciliation: mounted repository and endpoint editors use simulated defaults. Endpoint PATCH now persists omitted/null/concrete values across GET and resets on remount; repository PUT already preserves them. Choice caches intentionally contain no projects/workspaces. Browser editor interactions, access denials and new-task inheritance remain unverified. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Inspected channel task-default controls, optional API fields, avatar rendering and matching fixture updates; live defaults inheritance and permission checks remain outside these stories. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/chat-channels.md", @@ -2613,7 +2615,7 @@ "ui/src/components/CodexRunnerSelect.tsx", "ui/src/components/ui/select.tsx" ], - "fingerprint": "92dc681d9297a73dffa3c2221ae6be4ee324714ae11c9476b7b37e59570295e0", + "fingerprint": "5ded897d9a7da2c801294111417080b771b4d8e225ce311fbd7c0511934d8aad", "stableId": "connectors-github-bot-management", "sidebarTitle": "Apps, channels, and extensions/External chat and email/Connectors/GitHub bot management" }, @@ -2883,7 +2885,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -2892,7 +2894,7 @@ "ui/src/components/task-chat/TaskChatBlockerLinks.tsx", "ui/storybook/stories/recovery/issue-blocked-notice.stories.tsx" ], - "fingerprint": "52f861389de245bda4d9c582581b49ce0b9442d60ee7eff950adc6801aaca8a4", + "fingerprint": "90f1eae150c50bd1a428ce322a8d65ceec8b24b6addc8b3b4ea5e90231bc1a2c", "stableId": "product-issue-blocked-notice", "sidebarTitle": "Projects and execution/Recovery/Product/Issue/Blocked notice" }, @@ -3010,14 +3012,14 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", "ui/src/components/AdapterLoginChrome.tsx", "ui/storybook/stories/runs-adapters/local-ai-subscription.stories.tsx" ], - "fingerprint": "36ec94d6277f06a1b8a9e48a81e18b068ae11e3e3694679f0168181bf4ce8d94", + "fingerprint": "c39048edc842ea22269d40bcd8f1bd02c9a5b4904e2d6fe14a426bce952689db", "stableId": "ai-connections-provider-routing-07-subscription-sign-in", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/07 Subscription sign-in" }, @@ -3115,14 +3117,14 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", "ui/src/components/IssueScheduledRetryCard.tsx", "ui/storybook/stories/recovery/model-capacity-retry.stories.tsx" ], - "fingerprint": "1db367a2b77b8c747ab5c81c1fe3317da02bf294308e9e09af827bcd12ee0b9f", + "fingerprint": "cc935eea1a2ffc27882364655db860c441c4dc059be3d4886a4bd7d3839752bf", "stableId": "tasks-model-capacity-retry", "sidebarTitle": "Projects and execution/Recovery/Tasks/Model capacity retry" }, @@ -3302,7 +3304,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Four managed Codex cases use scoped immutable-image discovery and a stateful saved-probe fixture: native default, measured Legacy, permission refusal, and unconfirmed cleanup. Native isolation recipe added; these fixture setup responses do not establish server-resolved transport isolation, real sandbox cleanup, or local/SSH enforcement. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. Four managed Codex cases use scoped immutable-image discovery and a stateful saved-probe fixture: native default, measured Legacy, permission refusal, and unconfirmed cleanup. Native isolation recipe added; these fixture setup responses do not establish server-resolved transport isolation, real sandbox cleanup, or local/SSH enforcement. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/onboarding.md", @@ -3316,7 +3318,7 @@ "ui/src/components/ui/select.tsx", "ui/storybook/fixtures/paperclipData.ts" ], - "fingerprint": "0fc350b2b574ef98fd015fefe303547be4121d66e090daf4ca56350c2027a243", + "fingerprint": "de0ffbd830a1142d1fd410b6c2ea27b4c3c117b00b867a3ccd1d2e804dfcd7db", "stableId": "onboarding-agent-arc", "sidebarTitle": "Getting started and access/Onboarding and first work/Onboarding/Agent arc" }, @@ -3329,7 +3331,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The added native isolation recipe is outside this character preview’s demonstrated scope. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The added native isolation recipe is outside this character preview’s demonstrated scope. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/onboarding.md", @@ -3337,7 +3339,7 @@ "ui/src/components/ui/button.tsx", "ui/storybook/stories/onboarding/onboarding-character.stories.tsx" ], - "fingerprint": "85e2c1a2d3fff33710ba73a4d416c54e31ced53e158d0d5efd51c5778dd91b23", + "fingerprint": "24a7c5b43d4d43d65908ac75816a0dd42f94d4c57639e21a192ff03d9279be4e", "stableId": "onboarding-character", "sidebarTitle": "Getting started and access/Onboarding and first work/Onboarding/Character" }, @@ -3351,7 +3353,7 @@ "status": "historical", "implementation": "prototype", "boundary": "fixtures", - "notes": "Older tile/mode preview; onboarding-agent-arc mounts the production wizard. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The expanded native isolation recipe is not exercised by this retained historical tile preview. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds.", + "notes": "Older tile/mode preview; onboarding-agent-arc mounts the production wizard. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The expanded native isolation recipe is not exercised by this retained historical tile preview. The operator QA timeout/concurrency recipe was inspected; this preview does not verify the first-task fixture’s pre-admission runtime bounds. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/onboarding.md", @@ -3359,7 +3361,7 @@ "ui/storybook/stories/onboarding/onboarding-connect-model.stories.tsx" ], "replacement": "ui/storybook/stories/onboarding/onboarding-agent-arc.stories.tsx", - "fingerprint": "93801b6f02adb113aeb3ba549d46a7e285c70968e0fd26dd3b6464eb1244ce30", + "fingerprint": "427e876b5873f1643fab2439fe7450abcc51e55192cf9bda867daa33dcbb1ab7", "stableId": "onboarding-connect-a-model", "sidebarTitle": "Getting started and access/Onboarding and first work/Onboarding/Connect a model" }, @@ -3373,7 +3375,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Creation visibility now defaults to hiding automated independent tasks, with a Show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/projects.md", @@ -3386,7 +3388,7 @@ "ui/storybook/prototypes/originating-tasks/OriginatingTasks.tsx", "ui/storybook/stories/task-coordination/originating-tasks.stories.tsx" ], - "fingerprint": "32c9981ce024550b26636b97d7337f7b34a1207e8bcdd5759a11d9a7ddb204fe", + "fingerprint": "16042f0e39379552a00b618f7dda6eea0e5227e4a90e21fbea8fe35c1ecb820f", "stableId": "ux-labs-tasks-created-from-a-task", "sidebarTitle": "Tasks and conversations/Delegation, dependencies, and signoff/UX Labs/Tasks Created From a Task" }, @@ -3397,7 +3399,7 @@ "status": "unreviewed", "implementation": "guide", "boundary": "static", - "notes": "Matches the upstream feature index and order, including features with no cataloged examples. Related suite links and classifications do not imply execution coverage. The generated upstream feature index was reconciled; example scope and classifications are retained.", + "notes": "Matches the upstream feature index and order, including features with no cataloged examples. Related suite links and classifications do not imply execution coverage. The generated upstream feature index was reconciled; example scope and classifications are retained. Reviewed the regenerated liveness capability description against the feature recipe; the guide remains a static index and does not prove live readiness.", "reviewedAt": "2026-10-13", "sources": [ "ui/src/components/ui/badge.tsx", @@ -3810,7 +3812,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3828,7 +3830,7 @@ "ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "ebd9132a1f0228f775b1f382fc30dcae68e65284c8118c7b8a2eee3219c741ed", + "fingerprint": "ea2ddeb4a186e49cdfb7241751e0b25b6565ddee24202c0e48169966ae3e2d96", "stableId": "private-tasks-01-creation", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/01 Creation" }, @@ -3956,7 +3958,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -3974,7 +3976,7 @@ "ui/storybook/stories/access/private-tasks/06-full-product-pages.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "6069116fffc3a661df1fb10245f3f6d8045086efd71c886a8064a27eed09ac42", + "fingerprint": "a394e48a19ffce759f893bee7c28a1eceea2325ee9050cf41d42059afd5460d1", "stableId": "private-tasks-06-full-product-pages", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/06 Full product pages" }, @@ -3988,7 +3990,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation visibility now defaults to hiding automated independent tasks, with a show agent-created tasks control and inbox participation exceptions. Source alignment reviewed; these fixture stories do not establish live server pagination or authorization. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/access.md", @@ -4021,7 +4023,7 @@ "ui/storybook/stories/access/private-tasks/07-user-stories.stories.tsx", "ui/storybook/support/private-tasks/PrivacyStory.tsx" ], - "fingerprint": "483a8bfae265844bfa10ebd8529572f43c23c8474d414b45ba0b663efc280dbd", + "fingerprint": "ce4def84f6ceeb96cd91a285053edf1913baceb6628f70ac98612969bd46cf46", "stableId": "private-tasks-07-user-stories", "sidebarTitle": "Getting started and access/Login, invitations, and access/Private tasks/07 User stories" }, @@ -4117,7 +4119,7 @@ "status": "unreviewed", "implementation": "guide", "boundary": "static", - "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Review guide retained; explanatory content does not establish runtime or production coverage. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4126,7 +4128,7 @@ "ui/storybook/prototypes/provider-routing/story-support.ts", "ui/storybook/stories/runs-adapters/provider-routing-00-overview.stories.tsx" ], - "fingerprint": "b450e64d691795ab38737f27ee62747c21f255b217da6f906d2106e85543b06c", + "fingerprint": "e2ef473158ed16f52c6b23c92dd6d9d8b4995f5e6db09e6f1f6fecf6e02a36aa", "stableId": "ai-connections-provider-routing-00-overview", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/00 Overview" }, @@ -4139,7 +4141,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof.", + "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4179,7 +4181,7 @@ "ui/src/components/CodexRunnerSelect.tsx", "ui/src/components/ui/select.tsx" ], - "fingerprint": "412a440c2ac7a72cde3384c387a64aeec24a4ca22307f2fd13bbca827c5890e0", + "fingerprint": "969788de19b984a2fbd36809cb92273b13aa153489b3e65c026e45ce36511eaf", "stableId": "ai-connections-provider-routing-01-onboarding", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/01 Onboarding" }, @@ -4192,7 +4194,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4210,7 +4212,7 @@ "ui/storybook/prototypes/provider-routing/story-support.ts", "ui/storybook/stories/runs-adapters/provider-routing-02-connect.stories.tsx" ], - "fingerprint": "2b524e6330a3427df4139fedaad7867eed742752508087e6d4c8b9b408c19ff3", + "fingerprint": "7555efd817b31847604a7c4e9d0990cea26357d861fb2b2d70f0826475efe621", "stableId": "ai-connections-provider-routing-02-connect", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/02 Connect" }, @@ -4223,7 +4225,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof.", + "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4263,7 +4265,7 @@ "ui/src/components/CodexRunnerSelect.tsx", "ui/src/components/ui/select.tsx" ], - "fingerprint": "64bf50840c715749b66280c72901a8eecac5500831f00df96ff568579d64c856", + "fingerprint": "489342d16df9bfc96eb1da4c6b6c219ca61472d04f69290a137590b348299608", "stableId": "ai-connections-provider-routing-03-agent", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/03 Agent" }, @@ -4276,7 +4278,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4296,7 +4298,7 @@ "ui/storybook/prototypes/provider-routing/story-support.ts", "ui/storybook/stories/runs-adapters/provider-routing-04-manage.stories.tsx" ], - "fingerprint": "03a7c24f6a7a406a1ba02d56fd9d8a7ca667e89b5d92dfa9c8f8b2f19e64c4ad", + "fingerprint": "dde1c978c8c5314881d622becfecbf2b86c8f2b39790c2268b2ca9941de175d3", "stableId": "ai-connections-provider-routing-04-manage", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/04 Manage" }, @@ -4309,7 +4311,7 @@ "status": "mixed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4321,7 +4323,7 @@ "ui/storybook/prototypes/provider-routing/story-support.ts", "ui/storybook/stories/runs-adapters/provider-routing-05-recovery.stories.tsx" ], - "fingerprint": "627fc9c41cc8a7a8b1f13d5b9c6bfcbb77f8ee427a479e5b88da25bce0860d3b", + "fingerprint": "e7dad46f486fa286e3441b5e5f227f89e011c013551a23f721692616b577aa61", "stableId": "ai-connections-provider-routing-05-recovery", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/05 Recovery" }, @@ -4334,7 +4336,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof.", + "notes": "Design-review route uses simulated account/provider state; audit the rendered subject before treating it as the live connection flow. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4354,7 +4356,7 @@ "ui/src/components/CodexRunnerSelect.tsx", "ui/src/components/ui/select.tsx" ], - "fingerprint": "0adf7fa67da78ff0b655c8054b637aa6ae7ffd376299da905b2d426c5b2500cc", + "fingerprint": "43a234b650ba05d0d7300fed65cecd7e7bbc9f16a572ee227cfd0214bff7fc91", "stableId": "ai-connections-provider-routing-06-production-components", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/AI Connections/Provider routing/06 Production components" }, @@ -4479,7 +4481,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -4489,7 +4491,7 @@ "ui/storybook/prototypes/recovery-notice/RecoveryNoticePreview.tsx", "ui/storybook/stories/recovery/recovery-notice-preview.stories.tsx" ], - "fingerprint": "e83bcaf099227bfcd17d571f680121b5b51d5f605d76479165000a575229a884", + "fingerprint": "44d25bc1cf087902bc991cc7fd1dc2bc19880c18189960dae47c68a9284bc0f6", "stableId": "design-previews-recovery-notice", "sidebarTitle": "Projects and execution/Recovery/Design previews/Recovery notice" }, @@ -4711,7 +4713,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -4726,7 +4728,7 @@ "ui/storybook/prototypes/runner-activity/RunnerActivityPreview.tsx", "ui/storybook/stories/tasks/runner-activity.stories.tsx" ], - "fingerprint": "53553d0b17d423130ba8e0d9d19e33eb9f7da99442ce93485e55ed9160e94682", + "fingerprint": "98c073f2f9f587494dd07e9c90dbc38afe4ca6b5661abd8f648abeef6007466a", "stableId": "tasks-runner-activity-preview", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Runner activity preview" }, @@ -4739,7 +4741,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -4748,7 +4750,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/recovery/scheduled-retry.stories.tsx" ], - "fingerprint": "f360f4207ef2b1cef18eee2307c50a7bce3a5f5bbf8966b24cd86b3ec50d21b4", + "fingerprint": "94225de83234003fe6c5bdc47767e50da8562f661dbf2c5678d9fb41490a86a5", "stableId": "product-issue-scheduled-retry-surfaces", "sidebarTitle": "Projects and execution/Recovery/Product/Issue Scheduled retry surfaces" }, @@ -4832,14 +4834,14 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", "ui/src/components/task-chat/RunnerGoalWidget.tsx", "ui/storybook/stories/runs-adapters/session-goals.stories.tsx" ], - "fingerprint": "a9de8790902b984dc8eba0e9aa7769de66e74b55c364c67d09059fa6789af536", + "fingerprint": "3012a2ac98013c7f3db6f0bf91b2fefbc0772b29c497efacbe1920bf6c5fedf5", "stableId": "composer-session-goals", "sidebarTitle": "Agents and reusable capabilities/Runs, harnesses, and model accounts/Composer/Session goals" }, @@ -5052,7 +5054,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Source review confirms ordinary recovery fixtures remain applicable after the native-owner resolution gate; native-owner stop verification has its own suite.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Source review confirms ordinary recovery fixtures remain applicable after the native-owner resolution gate; native-owner stop verification has its own suite. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5062,7 +5064,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/recovery/source-issue-recovery.stories.tsx" ], - "fingerprint": "949814263788ae0e3501b67bbd26118f150db219fc04e5cf653f18dd345ec8d5", + "fingerprint": "e83979b6d2737fef8666a36860817af9272d4d4df649a0784c04e4bc32b63028", "stableId": "paperclip-source-issue-recovery", "sidebarTitle": "Projects and execution/Recovery/Paperclip/Source Issue Recovery" }, @@ -5076,7 +5078,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5084,7 +5086,7 @@ "ui/src/components/SourceResolvedFoldCallout.tsx", "ui/storybook/stories/recovery/source-resolved-fold.stories.tsx" ], - "fingerprint": "70cab38574df19267e35ab13c10c4a3eea444bb3b6e72c81f739f21cfc519df0", + "fingerprint": "6b42874521d91e0aa6323fb4d6744a6abd7931fda54e6591bdac9131fa2abaa4", "stableId": "paperclip-source-resolved-fold", "sidebarTitle": "Projects and execution/Recovery/Paperclip/Source-resolved Fold" }, @@ -5153,7 +5155,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5165,7 +5167,7 @@ "ui/storybook/fixtures/paperclipData.ts", "ui/storybook/stories/tasks/successful-run-handoff.stories.tsx" ], - "fingerprint": "27ad7c3c2b09acf5ac07163d44026511bd0c2b806387b591fef5fca343b0c63b", + "fingerprint": "58d58a28f56037e392ab0f52d971f4c21f8745f84337552bc45bbeeffd0c6655", "stableId": "paperclip-successful-run-handoff", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Paperclip/Successful Run Handoff" }, @@ -5224,7 +5226,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Codex selection/branding and upstream recipe source changes inspected; prior parity limitations remain, with no new rendered or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/runs-adapters.md", @@ -5236,7 +5238,7 @@ "ui/src/components/task-chat/task-chat-model.ts", "ui/storybook/stories/tasks/task-chat-chain-of-thought.stories.tsx" ], - "fingerprint": "62828b973160bd74da0f314414f77f749247f61e892d0ad055ef1b6f918087ee", + "fingerprint": "cef7fee6be9970cfaf2e7a9c4b2026b3b60adddc179082c8f646e1936139fc67", "stableId": "tasks-task-chat-review-fixes", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Task chat review fixes" }, @@ -5347,7 +5349,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. Creation-visibility verification recipes were updated; this story scenario is unchanged and does not prove that journey. The filter uses one lowercase label without helper text. All chat-channel tasks count as bot-created; the focused visibility stories cover human-initiated channel work. Source review covers the retained-file recovery form, source-bound evidence and unchanged task status; fixture APIs do not prove live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/tasks.md", @@ -5356,7 +5358,7 @@ "ui/src/components/chat/ExternallyConnectedTaskBanner.tsx", "ui/storybook/stories/tasks/task-context-notices.stories.tsx" ], - "fingerprint": "10f395d8ecba84925abf8d843deb52af64356008bb96a03668f29870ac430bc8", + "fingerprint": "794484576911fdab4a9b16db119890b75403316a8a4bbcce4041aee241c95193", "stableId": "tasks-context-notices", "sidebarTitle": "Tasks and conversations/Task creation and lifecycle/Tasks/Context notices" }, @@ -5617,7 +5619,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5625,7 +5627,7 @@ "ui/storybook/stories/recovery/workspace-branch-recovery/01-component.stories.tsx", "ui/storybook/support/workspace-branch-recovery/fixtures.ts" ], - "fingerprint": "eeaa7044a1d5a2b5355a8ac2375820e3611c71920e88c04d87e5d7ce64ed929a", + "fingerprint": "449d4ae2d427627089327567690e88219a14dfaeac3265bb3014cf1555e0da5d", "stableId": "workspace-branch-recovery-01-repair-card", "sidebarTitle": "Projects and execution/Recovery/Workspace branch recovery/01 Repair card" }, @@ -5638,7 +5640,7 @@ "status": "unreviewed", "implementation": "production", "boundary": "fixtures", - "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts.", + "notes": "Shipped subjects identified through imports and local wrappers; full state, fixture-contract and route parity remain unreviewed. Master reconciliation inspected assignment-history recipient suggestions and manual-selection retention; existing stories do not establish the new assignment-history behavior. Expanded upstream verification recipes were inspected; the existing fixture scenarios do not prove the added backend handoff, workspace, recovery or authorization contracts. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5650,7 +5652,7 @@ "ui/storybook/stories/recovery/workspace-branch-recovery/02-task-journey.stories.tsx", "ui/storybook/support/workspace-branch-recovery/fixtures.ts" ], - "fingerprint": "f8a090cfdbadfe77d8ee064b99fc11b8f5e097d4e72173df2496280ef52152d1", + "fingerprint": "c30653d1cd266832f7bdd401bc302a579d4170b7ea9b2523feccbdc66a58e997", "stableId": "workspace-branch-recovery-02-task-journey", "sidebarTitle": "Projects and execution/Recovery/Workspace branch recovery/02 Task journey" }, @@ -5725,7 +5727,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Production quiet status; automatic restoration, legacy operator fallback, queued and resolved states. No task repair form, process details or API simulation. Provider proof is verified by separate server and harness tests.", + "notes": "Production quiet status; automatic restoration, legacy operator fallback, queued and resolved states. No task repair form, process details or API simulation. Provider proof is verified by separate server and harness tests. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5733,7 +5735,7 @@ "ui/storybook/stories/recovery/workspace-finalization-recovery/01-notice.stories.tsx", "ui/storybook/support/workspace-finalization-recovery/fixtures.tsx" ], - "fingerprint": "45bf8c793c91362634b7ce32ed0ec2007861b7fefbfd0e2da692c3dc804a53c9", + "fingerprint": "5026bd7133f40a9c61371e22ed6976efc67013fd3047bf85056026de86bf6cec", "stableId": "workspace-finalization-recovery-01-notice", "sidebarTitle": "Projects and execution/Recovery/Workspace finalization recovery/01 Notice" }, @@ -5747,7 +5749,7 @@ "status": "unreviewed", "implementation": "mixed", "boundary": "fixtures", - "notes": "Production shell, status, bubbles and composer in a local conversation composition; shows restoration and saved work waiting for confirmation. Fixture messages; not the shipped task page or live provider acceptance.", + "notes": "Production shell, status, bubbles and composer in a local conversation composition; shows restoration and saved work waiting for confirmation. Fixture messages; not the shipped task page or live provider acceptance. The added verification recipe documents source-only retained-file repair; existing fixtures do not establish live provider recovery.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5758,7 +5760,7 @@ "ui/storybook/stories/recovery/workspace-finalization-recovery/02-task-journey.stories.tsx", "ui/storybook/support/workspace-finalization-recovery/fixtures.tsx" ], - "fingerprint": "aa76cd28dde8d50abd208b34283249170f3a85435ba82fff169a1c33433dfca1", + "fingerprint": "934a8f1882d8b5f5381aba9d0f27026849870a1fa417178fb6bc4170c54b15ba", "stableId": "workspace-finalization-recovery-02-task-journey", "sidebarTitle": "Projects and execution/Recovery/Workspace finalization recovery/02 Task journey" }, @@ -5772,7 +5774,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Production NewAgent and AgentBasicsDialog with isolated API fixtures. Automatic and explicit Legacy choices, qualified/unqualified discovery, old native links, setup failure and returned hire identity are asserted in source. Fixture runtime selection follows the requested runner and simulated target qualification; render/play, production persistence and provider execution are not verified by this source pass. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof.", + "notes": "Production NewAgent and AgentBasicsDialog with isolated API fixtures. Automatic and explicit Legacy choices, qualified/unqualified discovery, old native links, setup failure and returned hire identity are asserted in source. Fixture runtime selection follows the requested runner and simulated target qualification; render/play, production persistence and provider execution are not verified by this source pass. AgentConfigForm shared-module dropdown changes inspected; this suite demonstrates imported picker fields or substitutes local runtime content, not the updated environment and Codex lifecycle/ACP controls. No new render/play or live proof. The managed-link credential and confirmed probe-retirement recipe was inspected; fixture responses do not verify a real account or cleanup.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/agents.md", @@ -5788,7 +5790,7 @@ "ui/storybook/fixtures/onboardingEnvironment.ts", "ui/storybook/stories/agents/codex-runner/creation.stories.tsx" ], - "fingerprint": "5055ac064b22cda06d70b9f41049f0c8fb22cb94f35398cb0690bc13980b22d9", + "fingerprint": "ef8333ecc416c0098cb887128a59a7fd77af91be89f9dec07b28d4f0e2c505f2", "stableId": "agents-codex-runner-creation", "sidebarTitle": "Agents and reusable capabilities/Hiring, configuration, and organization/Agents/Codex runner/Creation" }, @@ -5825,7 +5827,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Exhaustion notice, blocked authority, retry pending/failure/accepted and inactive notice; unknown-outcome action card is rendered separately. Retry callbacks and disposition context are simulated; no real continuation proof. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path.", + "notes": "Exhaustion notice, blocked authority, retry pending/failure/accepted and inactive notice; unknown-outcome action card is rendered separately. Retry callbacks and disposition context are simulated; no real continuation proof. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. Retained legacy files use a separate recovery form with verified stop evidence; these exhaustion and response-queue examples do not establish file repair.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5838,7 +5840,7 @@ ], "stableId": "feature-recovery-gap-states", "sidebarTitle": "Projects and execution/Recovery/Exhaustion and user decisions", - "fingerprint": "b040d5167216510cd7f4384294fb27eb0a06168436b9cdd0a1ea9ac1b0e997b0" + "fingerprint": "8eb7fd4ef6387f3b39c3c83ac6fe945761999a0c2c6a9eb124e2c4550f8718a1" }, { "file": "ui/storybook/stories/steering/durable-response-queue.stories.tsx", @@ -5850,7 +5852,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "Accepted immutable response, cleanup wait, unavailable admission, empty queue and interrupt delivery states. Local callbacks remove delivered entries; durable queue/coalescing and fresh-session authority need production tests. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. The shipped pending-delivery path removes rows optimistically and shows no separate progress indicator; the Delivering story asserts that actual presentation.", + "notes": "Accepted immutable response, cleanup wait, unavailable admission, empty queue and interrupt delivery states. Local callbacks remove delivered entries; durable queue/coalescing and fresh-session authority need production tests. The expanded native copyback recovery recipe requires harness stop proof; these generic exhaustion/queued-response fixtures do not establish that path. The shipped pending-delivery path removes rows optimistically and shows no separate progress indicator; the Delivering story asserts that actual presentation. Retained legacy files use a separate recovery form with verified stop evidence; these exhaustion and response-queue examples do not establish file repair.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/recovery.md", @@ -5862,7 +5864,7 @@ ], "stableId": "feature-durable-response-queue", "sidebarTitle": "Tasks and conversations/Steering and queued messages/Accepted response delivery", - "fingerprint": "0071986d1378afd66fcb683d479d9569b05af97a4f98b2bc7ced1f67e8aaf61b" + "fingerprint": "9dce4d11180c4e178e8e2c23d85175f97256d8137727076c6fbc979ea3fd1734" }, { "file": "ui/storybook/stories/workspaces/workspace-close.stories.tsx", @@ -6338,7 +6340,7 @@ "status": "current", "implementation": "production", "boundary": "fixtures", - "notes": "OnboardingWizard restores a seeded draft, malformed/unavailable-company handling and draft retention across a fixture remount. The reopen button belongs to the fixture host; real reload, account switch and storage failure remain separate checks. The merged wizard includes Codex harness/runner draft restoration and immutable-environment setup qualification. These isolated draft states do not establish the new native setup, task/follow-up, account or cleanup journey.", + "notes": "OnboardingWizard restores a seeded draft, malformed/unavailable-company handling and draft retention across a fixture remount. The reopen button belongs to the fixture host; real reload, account switch and storage failure remain separate checks. The merged wizard includes Codex harness/runner draft restoration and immutable-environment setup qualification. These isolated draft states do not establish the new native setup, task/follow-up, account or cleanup journey. The authorized draft reload and explicit company-opening boundaries were reviewed at the fixed wizard source; component controls do not establish a successful managed reload or first task.", "reviewedAt": "2026-10-13", "sources": [ "maps/feature-map/onboarding.md", @@ -6353,7 +6355,7 @@ ], "stableId": "feature-onboarding-resume", "sidebarTitle": "Getting started and access/Onboarding and first work/Resume a saved draft", - "fingerprint": "99d51dd447392e61a014351e956d3b93e36663ba7a795a591b694b05fe49e9ad" + "fingerprint": "5dcf40da0836bb9ea5625095b839a3c9b4c2636bcaf6ebde41055e2b6983ba99" }, { "file": "ui/storybook/stories/routines/run-now.stories.tsx", diff --git a/packages/db/src/migrations/0340_open_warlock.sql b/packages/db/src/migrations/0340_open_warlock.sql new file mode 100644 index 00000000000..81041a3a4de --- /dev/null +++ b/packages/db/src/migrations/0340_open_warlock.sql @@ -0,0 +1,2 @@ +ALTER TABLE "resource_lifecycle_events" DROP CONSTRAINT "resource_lifecycle_events_action_check";--> statement-breakpoint +ALTER TABLE "resource_lifecycle_events" ADD CONSTRAINT "resource_lifecycle_events_action_check" CHECK ("resource_lifecycle_events"."action" = 'create' OR ("resource_lifecycle_events"."resource_type" = 'project' AND "resource_lifecycle_events"."action" IN ('update', 'archive', 'delete')) OR ("resource_lifecycle_events"."resource_type" = 'agent' AND "resource_lifecycle_events"."action" IN ('pause', 'resume', 'terminate'))); \ No newline at end of file diff --git a/packages/db/src/migrations/meta/0335_snapshot.json b/packages/db/src/migrations/meta/0340_snapshot.json similarity index 98% rename from packages/db/src/migrations/meta/0335_snapshot.json rename to packages/db/src/migrations/meta/0340_snapshot.json index b348334bbd3..188aeb107d1 100644 --- a/packages/db/src/migrations/meta/0335_snapshot.json +++ b/packages/db/src/migrations/meta/0340_snapshot.json @@ -1,6 +1,6 @@ { - "id": "18c156f2-8344-4282-98b7-f702f0035c81", - "prevId": "39d82f2c-8fd5-4606-84df-f51880dbd6a7", + "id": "b32fa655-79c7-4793-a021-22258b6c369c", + "prevId": "045fe4f7-1636-4e1b-b304-11b6128fefcf", "version": "7", "dialect": "postgresql", "tables": { @@ -9858,6 +9858,12 @@ "notNull": true, "default": false }, + "execution_defaults": { + "name": "execution_defaults", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, "metadata": { "name": "metadata", "type": "jsonb", @@ -10098,6 +10104,12 @@ "notNull": true, "default": "''" }, + "execution_defaults": { + "name": "execution_defaults", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, "allow_direct_messages": { "name": "allow_direct_messages", "type": "boolean", @@ -16312,6 +16324,18 @@ "primaryKey": false, "notNull": false }, + "owner_agent_id": { + "name": "owner_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "owner_plugin_id": { + "name": "owner_plugin_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, "user_secret_definition_id": { "name": "user_secret_definition_id", "type": "uuid", @@ -16540,6 +16564,40 @@ "method": "btree", "with": {} }, + "company_secrets_instance_owner_key_uq": { + "name": "company_secrets_instance_owner_key_uq", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_agent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "owner_plugin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "\"company_secrets\".\"scope\" = 'instance' and \"company_secrets\".\"deleted_at\" is null", + "concurrently": false, + "method": "btree", + "with": {} + }, "company_secrets_provider_config_idx": { "name": "company_secrets_provider_config_idx", "columns": [ @@ -16642,6 +16700,19 @@ "onDelete": "no action", "onUpdate": "no action" }, + "company_secrets_owner_plugin_id_plugins_id_fk": { + "name": "company_secrets_owner_plugin_id_plugins_id_fk", + "tableFrom": "company_secrets", + "tableTo": "plugins", + "columnsFrom": [ + "owner_plugin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, "company_secrets_user_secret_definition_id_user_secret_definitions_id_fk": { "name": "company_secrets_user_secret_definition_id_user_secret_definitions_id_fk", "tableFrom": "company_secrets", @@ -16680,6 +16751,21 @@ ], "onDelete": "set null", "onUpdate": "no action" + }, + "company_secrets_company_id_owner_agent_id_agents_company_id_id_fk": { + "name": "company_secrets_company_id_owner_agent_id_agents_company_id_id_fk", + "tableFrom": "company_secrets", + "tableTo": "agents", + "columnsFrom": [ + "company_id", + "owner_agent_id" + ], + "columnsTo": [ + "company_id", + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -16688,7 +16774,7 @@ "checkConstraints": { "company_secrets_scope_shape_check": { "name": "company_secrets_scope_shape_check", - "value": "(\n \"company_secrets\".\"scope\" = 'company'\n and \"company_secrets\".\"owner_user_id\" is null\n and \"company_secrets\".\"user_secret_definition_id\" is null\n ) or (\n \"company_secrets\".\"scope\" = 'user'\n and \"company_secrets\".\"owner_user_id\" is not null\n and \"company_secrets\".\"user_secret_definition_id\" is not null\n )" + "value": "(\n \"company_secrets\".\"scope\" = 'company'\n and \"company_secrets\".\"owner_user_id\" is null\n and \"company_secrets\".\"user_secret_definition_id\" is null\n and \"company_secrets\".\"owner_agent_id\" is null and \"company_secrets\".\"owner_plugin_id\" is null\n ) or (\n \"company_secrets\".\"scope\" = 'user'\n and \"company_secrets\".\"owner_user_id\" is not null\n and \"company_secrets\".\"user_secret_definition_id\" is not null\n and \"company_secrets\".\"owner_agent_id\" is null and \"company_secrets\".\"owner_plugin_id\" is null\n ) or (\n \"company_secrets\".\"scope\" = 'instance'\n and \"company_secrets\".\"owner_user_id\" is null and \"company_secrets\".\"user_secret_definition_id\" is null\n and \"company_secrets\".\"owner_agent_id\" is not null and \"company_secrets\".\"owner_plugin_id\" is not null\n )" } }, "isRLSEnabled": false @@ -26581,6 +26667,476 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.execution_workspace_repositories": { + "name": "execution_workspace_repositories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "execution_workspace_id": { + "name": "execution_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "requested_by_issue_id": { + "name": "requested_by_issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "repository_identity": { + "name": "repository_identity", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_repository_id": { + "name": "catalog_repository_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "repo_url": { + "name": "repo_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "relative_path": { + "name": "relative_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "requested_ref": { + "name": "requested_ref", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'HEAD'" + }, + "pinned_commit": { + "name": "pinned_commit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch_name": { + "name": "branch_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "request_key": { + "name": "request_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "request_keys": { + "name": "request_keys", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "failure_code": { + "name": "failure_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_workspace_repositories_identity_idx": { + "name": "execution_workspace_repositories_identity_idx", + "columns": [ + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "repository_identity", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_repositories_path_idx": { + "name": "execution_workspace_repositories_path_idx", + "columns": [ + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "relative_path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_repositories_request_idx": { + "name": "execution_workspace_repositories_request_idx", + "columns": [ + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "execution_workspace_repositories_company_workspace_idx": { + "name": "execution_workspace_repositories_company_workspace_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "execution_workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_workspace_repositories_company_id_companies_id_fk": { + "name": "execution_workspace_repositories_company_id_companies_id_fk", + "tableFrom": "execution_workspace_repositories", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_workspace_repositories_execution_workspace_id_execution_workspaces_id_fk": { + "name": "execution_workspace_repositories_execution_workspace_id_execution_workspaces_id_fk", + "tableFrom": "execution_workspace_repositories", + "tableTo": "execution_workspaces", + "columnsFrom": [ + "execution_workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "execution_workspace_repositories_requested_by_issue_id_issues_id_fk": { + "name": "execution_workspace_repositories_requested_by_issue_id_issues_id_fk", + "tableFrom": "execution_workspace_repositories", + "tableTo": "issues", + "columnsFrom": [ + "requested_by_issue_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_workspace_repository_access": { + "name": "execution_workspace_repository_access", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "root_key": { + "name": "root_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canonical_cwd": { + "name": "canonical_cwd", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "issue_id": { + "name": "issue_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "agent_id": { + "name": "agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "responsible_user_id": { + "name": "responsible_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_workspace_repository_access_scope_idx": { + "name": "execution_workspace_repository_access_scope_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_workspace_repository_access_company_id_companies_id_fk": { + "name": "execution_workspace_repository_access_company_id_companies_id_fk", + "tableFrom": "execution_workspace_repository_access", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.execution_workspace_repository_roots": { + "name": "execution_workspace_repository_roots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "company_id": { + "name": "company_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "canonical_cwd": { + "name": "canonical_cwd", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "root_key": { + "name": "root_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "execution_workspace_repository_roots_company_root_idx": { + "name": "execution_workspace_repository_roots_company_root_idx", + "columns": [ + { + "expression": "company_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "root_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "execution_workspace_repository_roots_company_id_companies_id_fk": { + "name": "execution_workspace_repository_roots_company_id_companies_id_fk", + "tableFrom": "execution_workspace_repository_roots", + "tableTo": "companies", + "columnsFrom": [ + "company_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.execution_workspace_runtime_leases": { "name": "execution_workspace_runtime_leases", "schema": "", @@ -26836,7 +27392,7 @@ "name": "project_id", "type": "uuid", "primaryKey": false, - "notNull": true + "notNull": false }, "project_workspace_id": { "name": "project_workspace_id", @@ -27114,7 +27670,7 @@ "columnsTo": [ "id" ], - "onDelete": "cascade", + "onDelete": "restrict", "onUpdate": "no action" }, "execution_workspaces_project_workspace_id_project_workspaces_id_fk": { @@ -37902,6 +38458,25 @@ "primaryKey": false, "notNull": false }, + "workspace_binding_revision": { + "name": "workspace_binding_revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "workspace_selection": { + "name": "workspace_selection", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "workspace_pending_selection": { + "name": "workspace_pending_selection", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, "execution_workspace_preference": { "name": "execution_workspace_preference", "type": "text", @@ -45625,7 +46200,7 @@ }, "resource_lifecycle_events_action_check": { "name": "resource_lifecycle_events_action_check", - "value": "\"resource_lifecycle_events\".\"action\" = 'create' OR (\"resource_lifecycle_events\".\"resource_type\" = 'project' AND \"resource_lifecycle_events\".\"action\" IN ('update', 'archive')) OR (\"resource_lifecycle_events\".\"resource_type\" = 'agent' AND \"resource_lifecycle_events\".\"action\" IN ('pause', 'resume', 'terminate'))" + "value": "\"resource_lifecycle_events\".\"action\" = 'create' OR (\"resource_lifecycle_events\".\"resource_type\" = 'project' AND \"resource_lifecycle_events\".\"action\" IN ('update', 'archive', 'delete')) OR (\"resource_lifecycle_events\".\"resource_type\" = 'agent' AND \"resource_lifecycle_events\".\"action\" IN ('pause', 'resume', 'terminate'))" } }, "isRLSEnabled": false diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index 4e82cc85923..248bad8e5b5 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -2367,6 +2367,13 @@ "when": 1791903526575, "tag": "0339_luxuriant_iron_monger", "breakpoints": true + }, + { + "idx": 340, + "version": "7", + "when": 1791911743153, + "tag": "0340_open_warlock", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/resource_lifecycle_events.ts b/packages/db/src/schema/resource_lifecycle_events.ts index cc0747373a3..d107f7f1f7f 100644 --- a/packages/db/src/schema/resource_lifecycle_events.ts +++ b/packages/db/src/schema/resource_lifecycle_events.ts @@ -8,12 +8,12 @@ export const resourceLifecycleEvents = pgTable("resource_lifecycle_events", { companyId: uuid("company_id").notNull().references(() => companies.id, { onDelete: "cascade" }), resourceType: text("resource_type").$type<"agent" | "project">().notNull(), resourceId: uuid("resource_id").notNull(), - action: text("action").$type<"create" | "update" | "archive" | "pause" | "resume" | "terminate">().notNull(), + action: text("action").$type<"create" | "update" | "archive" | "delete" | "pause" | "resume" | "terminate">().notNull(), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ resourceIdx: uniqueIndex("resource_lifecycle_events_creation_idx").on(table.companyId, table.resourceType, table.resourceId).where(sql`${table.action} = 'create'`), companyIdx: index("resource_lifecycle_events_company_idx").on(table.companyId, table.id), resourceOrderIdx: index("resource_lifecycle_events_resource_order_idx").on(table.companyId, table.resourceType, table.resourceId, table.id), resourceTypeCheck: check("resource_lifecycle_events_resource_type_check", sql`${table.resourceType} IN ('agent', 'project')`), - actionCheck: check("resource_lifecycle_events_action_check", sql`${table.action} = 'create' OR (${table.resourceType} = 'project' AND ${table.action} IN ('update', 'archive')) OR (${table.resourceType} = 'agent' AND ${table.action} IN ('pause', 'resume', 'terminate'))`), + actionCheck: check("resource_lifecycle_events_action_check", sql`${table.action} = 'create' OR (${table.resourceType} = 'project' AND ${table.action} IN ('update', 'archive', 'delete')) OR (${table.resourceType} = 'agent' AND ${table.action} IN ('pause', 'resume', 'terminate'))`), })); diff --git a/packages/plugins/sdk/src/define-plugin.ts b/packages/plugins/sdk/src/define-plugin.ts index f6f20214707..f206f8c8399 100644 --- a/packages/plugins/sdk/src/define-plugin.ts +++ b/packages/plugins/sdk/src/define-plugin.ts @@ -51,7 +51,7 @@ import type { AiConnectionRouterRequest, AiConnectionRouterResult } from "@paper * ``` */ -import type { PluginContext } from "./types.js"; +import type { PluginContext, ResourceLifecycleEvent, ResourceLifecycleResult } from "./types.js"; import type { PluginEnvironmentAcquireLeaseParams, PluginEnvironmentDestroyLeaseParams, @@ -360,6 +360,14 @@ export interface PluginDefinition { */ onAgentLifecycle?(params: AgentLifecycleRequest): Promise; + /** + * Reconcile a committed resource event. Requires manifest resourceLifecycle and events.subscribe. + * The host acknowledges successful results and replays interrupted/unfinished work. + * Calls are at least once, in order per resource. Re-read current state and use + * idempotent external operations; deletion events remain available after the row is gone. + */ + onResourceLifecycle?(event: ResourceLifecycleEvent): Promise; + /** Propose a member from host-authorized candidates. Requires ai.connections.route. */ onRouteAiConnection?(params: AiConnectionRouterRequest): Promise; diff --git a/packages/plugins/sdk/src/index.ts b/packages/plugins/sdk/src/index.ts index 98fa405f850..d6ab601b7a7 100644 --- a/packages/plugins/sdk/src/index.ts +++ b/packages/plugins/sdk/src/index.ts @@ -239,6 +239,7 @@ export type { PluginLocalFoldersClient, PluginEventsClient, ResourceLifecycleEvent, + ResourceLifecycleResult, PluginJobsClient, PluginLaunchersClient, PluginHttpClient, diff --git a/packages/plugins/sdk/src/protocol.ts b/packages/plugins/sdk/src/protocol.ts index a851294d7ad..cc5d11d154d 100644 --- a/packages/plugins/sdk/src/protocol.ts +++ b/packages/plugins/sdk/src/protocol.ts @@ -56,6 +56,7 @@ export type { PluginLauncherRenderContextSnapshot } from "@paperclipai/shared"; import type { PluginEvent, ResourceLifecycleEvent, + ResourceLifecycleResult, PluginIssueCheckoutOwnership, PluginIssueOrchestrationSummary, PluginIssueRelationSummary, @@ -1351,6 +1352,7 @@ export interface HostToWorkerMethods { handleApiRequest: [params: PluginApiRequestInput, result: PluginApiResponse]; /** @see PLUGIN_SPEC.md §13.8 */ agentLifecycle: [params: AgentLifecycleRequest, result: AgentLifecycleResult]; + resourceLifecycle: [params: ResourceLifecycleEvent, result: ResourceLifecycleResult]; getData: [params: GetDataParams, result: unknown]; /** @see PLUGIN_SPEC.md §13.9 */ performAction: [params: PerformActionParams, result: unknown]; @@ -1488,6 +1490,7 @@ export const HOST_TO_WORKER_OPTIONAL_METHODS: readonly HostToWorkerMethodName[] "handleWebhook", "handleApiRequest", "agentLifecycle", + "resourceLifecycle", "getData", "performAction", "executeTool", diff --git a/packages/plugins/sdk/src/types.ts b/packages/plugins/sdk/src/types.ts index b1e2980315b..d553744bbb6 100644 --- a/packages/plugins/sdk/src/types.ts +++ b/packages/plugins/sdk/src/types.ts @@ -547,7 +547,8 @@ export interface PluginLocalFoldersClient { export interface PluginEventsClient { /** Read durable resource hooks. Requires events.subscribe and a company scope. * Creation is delivered first, then the remaining events in id order. - * Page using afterId; reset it each polling sweep to retry failures and late commits. + * Prefer manifest resourceLifecycle + onResourceLifecycle for automatic delivery. + * Manual consumers page using afterId and restart pagination to revisit unacknowledged events. * Events repeat until acknowledged; use a company-scoped provider idempotency key. */ listLifecycle(companyId: string, limit?: number, afterId?: string): Promise; @@ -597,9 +598,18 @@ export type ResourceLifecycleEvent = { createdAt: string; } & ( | { resourceType: "agent"; action: "create" | "pause" | "resume" | "terminate" } - | { resourceType: "project"; action: "create" | "update" | "archive" } + | { resourceType: "project"; action: "create" | "update" | "archive" | "delete" } ); +/** Return acknowledged only after durable effects finish. Retry unfinished work; + * defer when configuration is missing (replayed on configuration changes or startup). + * Both retry and deferred leave the event unacknowledged. + */ +export interface ResourceLifecycleResult { + eventId: string; + status: "acknowledged" | "retry" | "deferred"; +} + /** * `ctx.jobs` — register handlers for scheduled jobs declared in the manifest. * diff --git a/packages/plugins/sdk/src/worker-rpc-host.ts b/packages/plugins/sdk/src/worker-rpc-host.ts index 5b1492106be..9f08052250e 100644 --- a/packages/plugins/sdk/src/worker-rpc-host.ts +++ b/packages/plugins/sdk/src/worker-rpc-host.ts @@ -39,6 +39,7 @@ import { createPluginIdleDrain } from "./idle-drain.js"; * @see PLUGIN_SPEC.md §14 — SDK Surface */ +import type { ResourceLifecycleEvent } from "./types.js"; import fs from "node:fs"; import { AsyncLocalStorage } from "node:async_hooks"; import path from "node:path"; @@ -1660,6 +1661,9 @@ export function startWorkerRpcHost(options: WorkerRpcHostOptions): WorkerRpcHost return handleExecuteTool(params as ExecuteToolParams); case "detectExternalObjects": return handleDetectExternalObjects(params as DetectExternalObjectsParams); + case "resourceLifecycle": + if (!plugin.definition.onResourceLifecycle) throw methodNotImplemented("resourceLifecycle"); + return plugin.definition.onResourceLifecycle(params as ResourceLifecycleEvent); case "agentLifecycle": if (!plugin.definition.onAgentLifecycle) throw methodNotImplemented("agentLifecycle"); return plugin.definition.onAgentLifecycle(params as AgentLifecycleRequest); @@ -1783,6 +1787,7 @@ export function startWorkerRpcHost(options: WorkerRpcHostOptions): WorkerRpcHost if (plugin.definition.onIdleDrain) supportedMethods.push("prepareIdleSleep", "releaseIdleSleep"); if (plugin.definition.onApiRequest) supportedMethods.push("handleApiRequest"); if (plugin.definition.onDetectExternalObjects) supportedMethods.push("detectExternalObjects"); + if (plugin.definition.onResourceLifecycle) supportedMethods.push("resourceLifecycle"); if (plugin.definition.onAgentLifecycle) supportedMethods.push("agentLifecycle"); if (plugin.definition.onRouteAiConnection) supportedMethods.push("routeAiConnection"); if (plugin.definition.onResolveExternalObject) supportedMethods.push("resolveExternalObject"); diff --git a/packages/plugins/sdk/tests/worker-rpc-host.test.ts b/packages/plugins/sdk/tests/worker-rpc-host.test.ts index 2895d57ece4..0c759222c3c 100644 --- a/packages/plugins/sdk/tests/worker-rpc-host.test.ts +++ b/packages/plugins/sdk/tests/worker-rpc-host.test.ts @@ -73,6 +73,38 @@ describe("isWorkerEntrypoint", () => { }); }); +describe("worker resource lifecycle delivery", () => { + it("advertises and dispatches the native lifecycle callback", async () => { + const stdin = new PassThrough(), stdout = new PassThrough(); + const lines = createInterface({ input: stdout }); + const plugin = definePlugin({ + async setup() {}, + async onResourceLifecycle(event) { return { eventId: event.id, status: "retry" }; }, + }); + const worker = startWorkerRpcHost({ plugin, stdin, stdout }); + async function call(method: string, params: unknown) { + const response = new Promise(resolve => { + const handler = (line: string) => { + const message = parseMessage(line); + if (isJsonRpcResponse(message)) { lines.off("line", handler); resolve(message); } + }; + lines.on("line", handler); + }); + stdin.write(serializeMessage(createRequest(method, params, method))); + return response; + } + try { + const initialized = await call("initialize", { + manifest: { id: "test.lifecycle", apiVersion: 1, version: "1.0.0", displayName: "Lifecycle", description: "Test", author: "Tests", categories: ["automation"], capabilities: ["events.subscribe"], resourceLifecycle: ["project"], entrypoints: { worker: "worker.js" } }, + config: {}, databaseNamespace: null, + }); + expect(initialized).toMatchObject({ result: { supportedMethods: expect.arrayContaining(["resourceLifecycle"]) } }); + expect(await call("resourceLifecycle", { id: "17", companyId: "company", resourceType: "project", resourceId: "project", action: "delete", createdAt: "2020-01-01T00:00:00Z" })) + .toMatchObject({ result: { eventId: "17", status: "retry" } }); + } finally { worker.stop(); lines.close(); stdin.destroy(); stdout.destroy(); } + }); +}); + describe("worker performAction context", () => { it("does not derive context companyId from caller params without host actor context", async () => { const hostToWorker = new PassThrough(); diff --git a/packages/shared/src/types/execution-projection.ts b/packages/shared/src/types/execution-projection.ts index ab65bbfb5b4..b5960ae3915 100644 --- a/packages/shared/src/types/execution-projection.ts +++ b/packages/shared/src/types/execution-projection.ts @@ -13,6 +13,7 @@ export interface ExecutionBlocker { canRetry?: boolean; /** Required workspace files must be recovered before another provider turn. */ workspaceRepairRequired?: boolean; + canRecordWorkspaceRepair?: boolean; savedMessageCount?: number; } diff --git a/packages/shared/src/types/plugin.ts b/packages/shared/src/types/plugin.ts index 95fa9dc8a5b..7a44d3aa7fe 100644 --- a/packages/shared/src/types/plugin.ts +++ b/packages/shared/src/types/plugin.ts @@ -689,6 +689,8 @@ export interface PaperclipPluginManifestV1 { capabilities: PluginCapability[]; /** Required participant in agent preparation, stop, resume, and cleanup. */ agentLifecycle?: true; + /** Durable, acknowledged resource events delivered to onResourceLifecycle. Requires events.subscribe. */ + resourceLifecycle?: Array<"agent" | "project">; /** Opt into the native pooled-connection catalog, setup, and management UI. */ aiConnectionRouter?: { name: string; description: string }; /** Entrypoint paths relative to the package root. */ diff --git a/packages/shared/src/validators/plugin.test.ts b/packages/shared/src/validators/plugin.test.ts index 683272d130f..e4a56eb919c 100644 --- a/packages/shared/src/validators/plugin.test.ts +++ b/packages/shared/src/validators/plugin.test.ts @@ -39,6 +39,15 @@ describe("plugin manifest validators", () => { expect(pluginManifestV1Schema.parse({ ...manifest, capabilities: ["agents.lifecycle.manage"] }).agentLifecycle).toBe(true); }); + it("requires subscription authority and known resource types for durable delivery", () => { + const manifest = { id: "example.lifecycle", apiVersion: 1, version: "1.0.0", displayName: "Lifecycle", description: "Lifecycle tests", author: "Tests", categories: ["automation"], entrypoints: { worker: "worker.js" }, resourceLifecycle: ["project"] }; + expect(pluginManifestV1Schema.safeParse({ ...manifest, capabilities: ["projects.read"] }).success).toBe(false); + expect(pluginManifestV1Schema.parse({ ...manifest, capabilities: ["events.subscribe"] }).resourceLifecycle).toEqual(["project"]); + for (const resourceLifecycle of [[], ["issue"]]) { + expect(pluginManifestV1Schema.safeParse({ ...manifest, resourceLifecycle, capabilities: ["events.subscribe"] }).success).toBe(false); + } + }); + it("requires routing authority for native pooled connector declarations without a custom UI bundle", () => { const manifest = { id: "example.pool", apiVersion: 1, version: "0.1.0", displayName: "Pool", description: "Pool", author: "Tests", categories: ["connector"], entrypoints: { worker: "worker.js" }, aiConnectionRouter: { name: "AI connection pool", description: "Use saved connections" } }; expect(pluginManifestV1Schema.safeParse({ ...manifest, capabilities: ["ui.page.register"] }).success).toBe(false); diff --git a/packages/shared/src/validators/plugin.ts b/packages/shared/src/validators/plugin.ts index 8f67466da56..24faa172fb1 100644 --- a/packages/shared/src/validators/plugin.ts +++ b/packages/shared/src/validators/plugin.ts @@ -783,6 +783,7 @@ export const pluginManifestV1Schema = z.object({ ).optional(), capabilities: z.array(z.enum(PLUGIN_CAPABILITIES)).min(1), agentLifecycle: z.literal(true).optional(), + resourceLifecycle: z.array(z.enum(["agent", "project"])).min(1).max(2).optional(), aiConnectionRouter: z.object({ name: z.string().trim().min(1).max(100), description: z.string().trim().min(1).max(500) }).strict().optional(), entrypoints: z.object({ worker: z.string().min(1), @@ -811,6 +812,10 @@ export const pluginManifestV1Schema = z.object({ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Agent lifecycle participation requires agents.lifecycle.manage", path: ["capabilities"] }); } + if (manifest.resourceLifecycle && !manifest.capabilities.includes("events.subscribe")) { + ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Resource lifecycle delivery requires events.subscribe", path: ["capabilities"] }); + } + // ── Entrypoint ↔ UI slot consistency ────────────────────────────────── // Plugins that declare UI slots must also declare a UI entrypoint so the // host knows where to load the bundle from (PLUGIN_SPEC.md §10.1). diff --git a/server/src/__tests__/fixtures/plugin-worker-delayed.cjs b/server/src/__tests__/fixtures/plugin-worker-delayed.cjs index 8aa7e4efa83..48a68cfcc51 100644 --- a/server/src/__tests__/fixtures/plugin-worker-delayed.cjs +++ b/server/src/__tests__/fixtures/plugin-worker-delayed.cjs @@ -20,7 +20,7 @@ rl.on("line", (line) => { id: message.id, result: { ok: true, - supportedMethods: ["environmentExecute"], + supportedMethods: ["environmentExecute", "configChanged"], }, }); return; @@ -44,6 +44,16 @@ rl.on("line", (line) => { return; } + if (method === "configChanged") { + setTimeout(() => send({ + jsonrpc: "2.0", id: message.id, + ...(message.params.config.fail + ? { error: { code: -32000, message: "configuration rejected" } } + : { result: {} }), + }), Number(message.params.config.delayMs ?? 0)); + return; + } + if (method === "shutdown") { send({ jsonrpc: "2.0", diff --git a/server/src/__tests__/plugin-lifecycle-delivery.test.ts b/server/src/__tests__/plugin-lifecycle-delivery.test.ts new file mode 100644 index 00000000000..f745dcdc0e4 --- /dev/null +++ b/server/src/__tests__/plugin-lifecycle-delivery.test.ts @@ -0,0 +1,217 @@ +import { randomUUID } from "node:crypto"; +import { EventEmitter } from "node:events"; +import { eq } from "drizzle-orm"; +import { companies, createDb, pluginConfig, plugins, resourceLifecycleEvents, type Db } from "@paperclipai/db"; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; +import { logger } from "../middleware/logger.js"; +import { registerPluginLifecycleDelivery } from "../services/plugin-lifecycle-delivery.js"; +import { pluginLifecycleInbox } from "../services/plugin-lifecycle-inbox.js"; +import { pluginRegistryService } from "../services/plugin-registry.js"; +import { recordProjectLifecycleEvent, recordResourceCreationEvent } from "../services/resource-lifecycle-events.js"; +import { DELIVERY_QUEUES, subscribeDeliveryWork } from "../services/delivery-work-notifications.js"; +import type { createDeliveryWorkCoordinator } from "../services/delivery-work-coordinator.js"; +import type { PluginLifecycleManager } from "../services/plugin-lifecycle.js"; +import type { PluginWorkerManager } from "../services/plugin-worker-manager.js"; + +const support = await getEmbeddedPostgresTestSupport(); +const describePostgres = support.supported ? describe : describe.skip; +if (!support.supported) console.warn(`Skipping lifecycle delivery database tests: ${support.reason}`); + +describePostgres("durable plugin lifecycle delivery", () => { + let database: Awaited>; + let db: Db; + let companyId: string; + let pluginId: string; + beforeAll(async () => { database = await startEmbeddedPostgresTestDatabase("paperclip-plugin-delivery-"); db = createDb(database.connectionString); }, 90_000); + afterAll(async () => { await database?.cleanup(); }); + afterEach(() => { vi.restoreAllMocks(); }); + beforeEach(async () => { + await db.delete(plugins); + companyId = randomUUID(); pluginId = randomUUID(); + await db.insert(companies).values({ id: companyId, name: "Delivery fixture", issuePrefix: `L${companyId.slice(0, 6)}` }); + await db.insert(plugins).values({ id: pluginId, pluginKey: pluginId, packageName: "delivery-fixture", version: "1.0.0", status: "ready", + manifestJson: { resourceLifecycle: ["project"], capabilities: ["events.subscribe"] } as never }); + await db.insert(pluginConfig).values({ pluginId, companyId, configJson: {} }); + }); + async function event(resourceId = randomUUID(), action: "create" | "update" | "delete" = "create") { + await db.transaction(async tx => { + if (action === "create") await recordResourceCreationEvent(tx as unknown as Db, companyId, "project", resourceId); + else await recordProjectLifecycleEvent(tx as unknown as Db, companyId, resourceId, action); + }); + return resourceId; + } + function delivery() { + let task!: Parameters["register"]>[1]; + const wake = vi.fn(); + const coordinator = { register: vi.fn((_queue, input) => { task = input; return { wake, ready: Promise.resolve() }; }) }; + const call = vi.fn(async (_pluginId, _method, input) => ({ eventId: input.id, status: "acknowledged" })); + const running = vi.fn(() => true); + const lifecycle = new EventEmitter(); + const configApplied = new Set<() => void>(); + const onConfigApplied = (listener: () => void) => { configApplied.add(listener); return () => { configApplied.delete(listener); }; }; + const worker = registerPluginLifecycleDelivery(coordinator as never, db, { call, isRunning: running, onConfigApplied } as unknown as PluginWorkerManager, lifecycle as unknown as PluginLifecycleManager); + return { call, running, wake, lifecycle, worker, applied: () => { for (const listener of configApplied) listener(); }, run: () => task.run(new AbortController().signal), pending: () => task.hasPending(), nextRunAt: () => task.nextRunAt?.(), settled: () => task.onSettled?.() }; + } + it("drains creation before transitions, recovers acknowledgments across dispatcher replacement, and ignores agent events", async () => { + const id = await event(); await event(id, "update"); await event(id, "delete"); + await db.transaction(tx => recordResourceCreationEvent(tx as unknown as Db, companyId, "agent", randomUUID())); + const first = delivery(); await first.run(); first.worker.dispose(); + expect(first.call.mock.calls.map(c => c[2].action)).toEqual(["create"]); + const restarted = delivery(); await restarted.run(); await restarted.run(); await restarted.run(); + expect(restarted.call.mock.calls.map(c => c[2].action)).toEqual(["update", "delete"]); + expect(await restarted.pending()).toBe(false); + expect((await pluginLifecycleInbox(db, pluginId).list(companyId)).map(e => e.resourceType)).toEqual(["agent"]); + restarted.worker.dispose(); + }); + it("replays failures and invalid acknowledgments without blocking independent resources", async () => { + let now = Date.now(); vi.spyOn(Date, "now").mockImplementation(() => now); + const failed = await event(); await event(failed, "delete"); await event(); + const d = delivery(); + d.call.mockImplementationOnce(async () => { throw new Error("worker exited after provider effect"); }); + await d.run(); + expect(d.call).toHaveBeenCalledTimes(2); expect(await d.pending()).toBe(true); + d.call.mockImplementationOnce(async () => ({ eventId: "wrong", status: "acknowledged" })); + now += 30_000; await d.run(); expect(await d.pending()).toBe(true); + now += 30_000; await d.run(); await d.run(); + expect(d.call.mock.calls.map(c => c[2].action)).toEqual(["create", "create", "create", "create", "delete"]); + expect(await pluginLifecycleInbox(db, pluginId).list(companyId)).toEqual([]); + d.worker.dispose(); + }); + it("healthy acknowledgments and new events do not bypass a failed event's retry deadline", async () => { + let now = Date.now(); vi.spyOn(Date, "now").mockImplementation(() => now); + const failed = await event(), healthy = await event(); + await event(healthy, "update"); await event(healthy, "delete"); + const d = delivery(); + d.call.mockImplementation(async (_p, _m, e) => ({ eventId: e.id, status: e.resourceId === failed ? "retry" : "acknowledged" })); + await d.run(); + const deadline = now + 30_000; + expect(d.nextRunAt()).toBe(deadline); + now += 1000; + await d.run(); await d.run(); await event(); d.settled(); await d.run(); + expect(d.call.mock.calls.filter(c => c[2].resourceId === healthy).map(c => c[2].action)).toEqual(["create", "update", "delete"]); + expect(d.call.mock.calls.filter(c => c[2].resourceId === failed)).toHaveLength(1); + expect(d.nextRunAt()).toBe(deadline); + now = deadline; + expect(d.nextRunAt()).toBeNull(); // Paused dispatch waits for the normal retry, not a zero-delay loop. + await d.run(); + expect(d.call.mock.calls.filter(c => c[2].resourceId === failed)).toHaveLength(2); + expect(d.nextRunAt()).toBe(now + 30_000); + d.worker.dispose(); + }); + it("defers without a retry deadline, retries unfinished work, and wakes on plugin startup", async () => { + await event(); const d = delivery(); + d.call.mockImplementationOnce(async (_p, _m, e) => ({ eventId: e.id, status: "deferred" })); + await d.run(); expect(await d.pending()).toBe(false); expect(d.wake).not.toHaveBeenCalled(); + d.lifecycle.emit("plugin.worker_started"); expect(d.wake).toHaveBeenCalledTimes(1); + d.call.mockImplementationOnce(async (_p, _m, e) => ({ eventId: e.id, status: "retry" })); + await d.run(); expect(await d.pending()).toBe(true); + d.running.mockReturnValue(false); await d.run(); expect(await d.pending()).toBe(true); + expect(d.call).toHaveBeenCalledTimes(2); d.worker.dispose(); + d.lifecycle.emit("plugin.worker_started"); expect(d.wake).toHaveBeenCalledTimes(1); + }); + it("requires ready, configured, company-enabled plugins and rechecks disabling within a batch", async () => { + await event(); await event(); const d = delivery(); const registry = pluginRegistryService(db); + await registry.upsertCompanySettings(pluginId, companyId, { enabled: false, settingsJson: {} }); + await d.run(); expect(d.call).not.toHaveBeenCalled(); + await registry.upsertCompanySettings(pluginId, companyId, { enabled: true, settingsJson: {} }); + d.call.mockImplementationOnce(async (_p, _m, e) => { + await registry.upsertCompanySettings(pluginId, companyId, { enabled: false, settingsJson: {} }); + return { eventId: e.id, status: "acknowledged" }; + }); + await d.run(); expect(d.call).toHaveBeenCalledTimes(1); + await registry.upsertCompanySettings(pluginId, companyId, { enabled: true, settingsJson: {} }); + await db.update(plugins).set({ status: "disabled" }).where(eq(plugins.id, pluginId)); + await d.run(); expect(d.call).toHaveBeenCalledTimes(1); + await db.update(plugins).set({ status: "ready" }).where(eq(plugins.id, pluginId)); + await db.delete(pluginConfig).where(eq(pluginConfig.pluginId, pluginId)); + await d.run(); expect(d.call).toHaveBeenCalledTimes(1); d.worker.dispose(); + }); + it("notifies only after the outer transaction settles and wakes deferred work for config changes", async () => { + const wake = vi.fn(); const unsubscribe = subscribeDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle, wake); + try { + await db.transaction(async tx => { + await eventInTransaction(tx as unknown as Db); + expect(wake).not.toHaveBeenCalled(); + }); + expect(wake).toHaveBeenCalledTimes(1); + const registry = pluginRegistryService(db); + await registry.upsertConfig(pluginId, companyId, { configJson: { enabled: true } }); + await registry.patchConfig(pluginId, companyId, { configJson: { enabled: false } }); + await registry.upsertCompanySettings(pluginId, companyId, { enabled: true, settingsJson: {} }); + expect(wake).toHaveBeenCalledTimes(4); + } finally { unsubscribe(); } + async function eventInTransaction(tx: Db) { + await tx.transaction(inner => recordResourceCreationEvent(inner as unknown as Db, companyId, "project", randomUUID())); + } + }); + it("retains startup and settled work while idle drain prevents dispatch", async () => { + const d = delivery(); + // The coordinator can call hasPending without run when admission is paused. + expect(await d.pending()).toBe(true); + await d.run(); expect(await d.pending()).toBe(false); + await event(); d.settled(); + expect(await d.pending()).toBe(true); + expect(d.call).not.toHaveBeenCalled(); + await d.run(); + expect(d.call).toHaveBeenCalledTimes(1); + expect(await d.pending()).toBe(true); // An acknowledgment must drain its successor. + await d.run(); expect(await d.pending()).toBe(false); + d.lifecycle.emit("plugin.enabled"); expect(await d.pending()).toBe(true); + await d.run(); expect(await d.pending()).toBe(false); + d.worker.wake(); expect(await d.pending()).toBe(true); + d.worker.dispose(); + }); + it("wakes deferred events after live configuration finishes applying", async () => { + await event(); const d = delivery(); + let configured = false; + d.call.mockImplementation(async (_p, _m, e) => ({ eventId: e.id, status: configured ? "acknowledged" : "deferred" })); + await d.run(); expect(await d.pending()).toBe(false); + // Commit notification races ahead of the asynchronous worker callback. + d.settled(); await d.run(); expect(await d.pending()).toBe(false); + configured = true; d.applied(); + expect(await d.pending()).toBe(true); expect(d.wake).toHaveBeenCalledTimes(1); + await d.run(); expect(await pluginLifecycleInbox(db, pluginId).list(companyId)).toEqual([]); + d.worker.dispose(); d.wake.mockClear(); d.applied(); expect(d.wake).not.toHaveBeenCalled(); + }); + it("retains a failed scan across a retry skipped during idle drain", async () => { + await event(); const d = delivery(); + const select = vi.spyOn(db, "select").mockImplementationOnce(() => { throw new Error("database unavailable"); }); + await expect(d.run()).rejects.toThrow("database unavailable"); + // The coordinator's drain path checks hasPending without calling run. + expect(await d.pending()).toBe(true); expect(d.call).not.toHaveBeenCalled(); + select.mockRestore(); await d.run(); await d.run(); + expect(await d.pending()).toBe(false); + expect(await pluginLifecycleInbox(db, pluginId).list(companyId)).toEqual([]); + d.worker.dispose(); + }); + it("saves config and settings without borrowing another pool connection", async () => { + const single = createDb(database.connectionString, { maxConnections: 1 }); + const registry = pluginRegistryService(single); + await registry.upsertConfig(pluginId, companyId, { configJson: { first: true } }); + await registry.patchConfig(pluginId, companyId, { configJson: { second: true } }); + await registry.upsertCompanySettings(pluginId, companyId, { enabled: true, settingsJson: {} }); + expect((await registry.getConfig(pluginId, companyId))?.configJson).toEqual({ first: true, second: true }); + }, 5000); + it("records failed delivery scope and stage without provider secrets", async () => { + await event(); const d = delivery(); + const warn = vi.spyOn(logger, "warn").mockImplementation(() => {}); + try { + d.call.mockImplementationOnce(async () => { throw Object.assign(new Error("provider-secret-fixture"), { code: -32001 }); }); + await d.run(); + expect(warn).toHaveBeenCalledWith(expect.objectContaining({ pluginId, companyId, eventId: expect.any(String), stage: "callback", rpcCode: -32001 }), expect.any(String)); + expect(JSON.stringify(warn.mock.calls)).not.toContain("provider-secret-fixture"); + expect(await d.pending()).toBe(true); + } finally { warn.mockRestore(); d.worker.dispose(); } + }); + it("rollbacks never expose queue rows even when settlement wakes the dispatcher", async () => { + const d = delivery(); + await expect(db.transaction(async tx => { + await recordResourceCreationEvent(tx as unknown as Db, companyId, "project", randomUUID()); + throw new Error("rollback"); + })).rejects.toThrow("rollback"); + await d.run(); expect(d.call).not.toHaveBeenCalled(); expect(await d.pending()).toBe(false); + expect(await db.select().from(resourceLifecycleEvents).where(eq(resourceLifecycleEvents.companyId, companyId))).toEqual([]); + d.worker.dispose(); + }); +}); diff --git a/server/src/__tests__/plugin-worker-manager.test.ts b/server/src/__tests__/plugin-worker-manager.test.ts index 58b0c9a6682..c97c14a8355 100644 --- a/server/src/__tests__/plugin-worker-manager.test.ts +++ b/server/src/__tests__/plugin-worker-manager.test.ts @@ -31,6 +31,7 @@ import { appendStderrExcerpt, createDuplexRouteSlotController, createPluginWorkerHandle, + createPluginWorkerManager, formatWorkerFailureMessage, resolveRpcCallTimeoutMs, } from "../services/plugin-worker-manager.js"; @@ -56,6 +57,28 @@ const TEST_MANIFEST: PaperclipPluginManifestV1 = { entrypoints: { worker: "dist/worker.js" }, }; +describe("worker configuration completion", () => { + it("notifies only after a successful config callback and releases subscriptions", async () => { + const manager = createPluginWorkerManager(); + const applied = vi.fn(), unsubscribe = manager.onConfigApplied(applied); + try { + await manager.startWorker("test.plugin", { + entrypointPath: DELAYED_WORKER_ENTRYPOINT, manifest: TEST_MANIFEST, config: {}, + instanceInfo: { instanceId: "instance-1", hostVersion: "1.0.0" }, apiVersion: 1, hostHandlers: {}, + }); + expect(applied).not.toHaveBeenCalled(); + const updating = manager.call("test.plugin", "configChanged", { companyId: "company-1", config: { delayMs: 20 } }); + expect(applied).not.toHaveBeenCalled(); + await updating; expect(applied).toHaveBeenCalledTimes(1); + await expect(manager.call("test.plugin", "configChanged", { companyId: "company-1", config: { fail: true } })).rejects.toThrow("configuration rejected"); + expect(applied).toHaveBeenCalledTimes(1); + unsubscribe(); + await manager.call("test.plugin", "configChanged", { companyId: "company-1", config: {} }); + expect(applied).toHaveBeenCalledTimes(1); + } finally { unsubscribe(); await manager.stopAll(); } + }); +}); + describe("resolveRpcCallTimeoutMs", () => { const MAX_RPC_TIMEOUT_MS = 15 * 60 * 1_000; const MAX_NODE_TIMER_TIMEOUT_MS = 2_147_483_647; diff --git a/server/src/__tests__/resource-lifecycle-events.test.ts b/server/src/__tests__/resource-lifecycle-events.test.ts index dd54856dfd1..f7b263d40f9 100644 --- a/server/src/__tests__/resource-lifecycle-events.test.ts +++ b/server/src/__tests__/resource-lifecycle-events.test.ts @@ -53,7 +53,7 @@ describePostgres("Resource lifecycle events", () => { it("records direct and approval-free hires once, including concurrent duplicate submissions", async () => { const agent = await createAgent(); - await Promise.all(Array.from({ length: 5 }, () => recordResourceCreationEvent(db, companyId, "agent", agent.id))); + await Promise.all(Array.from({ length: 5 }, () => db.transaction(tx => recordResourceCreationEvent(tx as unknown as Db, companyId, "agent", agent.id)))); expect(await events()).toEqual([expect.objectContaining({ resourceType: "agent", resourceId: agent.id, action: "create" })]); }); @@ -235,11 +235,11 @@ describePostgres("Resource lifecycle events", () => { vi.stubEnv("PAPERCLIP_MANAGED_CONFIG", undefined); vi.stubEnv("PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN", "fixture-token"); const id = randomUUID(); - await recordResourceCreationEvent(db, companyId, "agent", id); - await recordResourceCreationEvent(db, companyId, "project", id); + await db.transaction(tx => recordResourceCreationEvent(tx as unknown as Db, companyId, "agent", id)); + await db.transaction(tx => recordResourceCreationEvent(tx as unknown as Db, companyId, "project", id)); const otherCompanyId = randomUUID(); await db.insert(companies).values({ id: otherCompanyId, name: "Other company", issuePrefix: "OTHER" }); - await recordResourceCreationEvent(db, otherCompanyId, "agent", id); + await db.transaction(tx => recordResourceCreationEvent(tx as unknown as Db, otherCompanyId, "agent", id)); expect(await events()).toHaveLength(2); expect(await db.select().from(resourceLifecycleEvents).where(eq(resourceLifecycleEvents.companyId, otherCompanyId))).toHaveLength(1); await db.delete(companies).where(eq(companies.id, otherCompanyId)); @@ -267,6 +267,23 @@ describePostgres("Resource lifecycle events", () => { expect(beforeArchive.sort((a, b) => a.id - b.id).map(row => row.action)).toEqual(["create", "update", "update", "update", "update", "update"]); }); + it("records deletion atomically and retains the event after removing the project", async () => { + const service = projectService(db); + const project = await service.create(companyId, { name: "Delete fixture" }); + await db.execute(sql`ALTER TABLE resource_lifecycle_events ADD CONSTRAINT fixture_reject_delete CHECK (action <> 'delete') NOT VALID`); + try { + await expect(service.remove(project.id)).rejects.toThrow(); + expect(await service.getById(project.id)).not.toBeNull(); + expect((await events()).map(row => row.action)).toEqual(["create"]); + } finally { + await db.execute(sql`ALTER TABLE resource_lifecycle_events DROP CONSTRAINT fixture_reject_delete`); + } + await service.remove(project.id); + await service.remove(project.id); + expect(await service.getById(project.id)).toBeNull(); + expect((await events()).map(row => row.action)).toEqual(["create", "delete"]); + }); + it("records archive once per transition and update on restore, retaining repositories", async () => { const service = projectService(db); const project = await service.createWithRepositories(companyId, { name: "Archive fixture" }, [ diff --git a/server/src/__tests__/workspace-restore-repair-route.test.ts b/server/src/__tests__/workspace-restore-repair-route.test.ts index dd1f0723ddd..db0b434e957 100644 --- a/server/src/__tests__/workspace-restore-repair-route.test.ts +++ b/server/src/__tests__/workspace-restore-repair-route.test.ts @@ -5,7 +5,7 @@ import { and, eq, inArray } from "drizzle-orm"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { activityLog, agents, agentWakeupRequests, companies, createDb, - environmentLeases, heartbeatRuns, issueRecoveryActions, issues, + environmentLeases, heartbeatRuns, issueRecoveryActions, issues, chatConversations, chatEndpoints, toolApplications, toolConnections, } from "@paperclipai/db"; import { getEmbeddedPostgresTestSupport, startEmbeddedPostgresTestDatabase } from "./helpers/embedded-postgres.js"; import { errorHandler } from "../middleware/index.js"; @@ -93,6 +93,23 @@ const support = external ? { supported: true } : await getEmbeddedPostgresTestSu return { app, wake }; } + it.each(["active", "unavailable", "endpoint_removed"])("keeps original file recovery instructions for a %s chat", async state => { + const f = await seed(); + const endpointId = randomUUID(); + const applicationId = randomUUID(), connectionId = randomUUID(); + await db.insert(toolApplications).values({ id: applicationId, companyId: f.companyId, name: "Chat recovery", type: "chat" }); + await db.insert(toolConnections).values({ id: connectionId, companyId: f.companyId, applicationId, + name: "Chat recovery", uid: randomUUID(), transport: "chat_sdk", connectionPurpose: "channel", status: "active", enabled: true }); + await db.insert(chatEndpoints).values({ id: endpointId, companyId: f.companyId, connectionId, + provider: "slack", publicId: randomUUID(), assignedAgentId: f.currentAgentId, status: "active" }); + await db.insert(chatConversations).values({ companyId: f.companyId, endpointId, issueId: f.issueId, + externalConversationId: randomUUID(), externalLabel: "Recovery conversation", state }); + expect(await getExecutionBlocker(db, f.companyId, f.issueId)).toMatchObject({ + recoveryActionId: f.actionId, workspaceRepairRequired: true, canRecordWorkspaceRepair: true, + nextAction: "Repair the retained workspace before continuing.", canRetry: false, canContinue: false, + }); + }); + async function assertSourceUnchanged(f: Awaited>) { expect((await db.select().from(issues).where(eq(issues.id, f.issueId)))[0]).toEqual(f.issueBefore); expect((await db.select().from(environmentLeases).where(eq(environmentLeases.id, f.leaseId)))[0]).toEqual(f.leaseBefore); diff --git a/server/src/app.ts b/server/src/app.ts index 651c66bcbf1..21a2c487f02 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -192,6 +192,7 @@ import { import { subscribeAllCompanyLiveEvents } from "./services/live-events.js"; import { heartbeatService } from "./services/heartbeat.js"; import { pluginLifecycleManager } from "./services/plugin-lifecycle.js"; +import { registerPluginLifecycleDelivery } from "./services/plugin-lifecycle-delivery.js"; import { createPluginJobCoordinator } from "./services/plugin-job-coordinator.js"; import { buildHostServices, @@ -1253,6 +1254,7 @@ export async function createApp( onError: (err, queue) => logger.error({ err, queue }, "Delivery reconciliation failed"), }); app.locals.deliveryWork = deliveryWork; + const resourceLifecycleDelivery = registerPluginLifecycleDelivery(deliveryWork, db, workerManager, lifecycle); if (opts.feedbackExportService) { deliveryWork.register(DELIVERY_QUEUES.feedback, { retryMs: FEEDBACK_EXPORT_FLUSH_INTERVAL_MS, @@ -1411,7 +1413,7 @@ export async function createApp( logger.error({ err }, "Failed to load ready plugins on startup"); }); app.locals.bundledPluginsStartup = trackIdleWork(bundledPluginsStartup); - void bundledPluginsStartup.then(() => { lifecyclePluginsReady = true; return agentLifecycle.sweep(); }).catch(() => logger.warn("Agent lifecycle recovery failed; retrying.")); + void bundledPluginsStartup.then(() => { lifecyclePluginsReady = true; resourceLifecycleDelivery.wake(); return agentLifecycle.sweep(); }).catch(() => logger.warn("Agent lifecycle recovery failed; retrying.")); // The shutdown hook runs at most once. It caches the in-flight promise, so a // second caller (for example the `exit` handler) awaits the same completion // instead of starting a second teardown. @@ -1426,6 +1428,7 @@ export async function createApp( await publicMcpEvents?.stop(); await dotMcpEvents?.stop(); jobCoordinator.stop(); + resourceLifecycleDelivery.dispose(); await deliveryWork.stop(); unsubscribeChatPublicationSignals(); chatReconciliation.stop(); diff --git a/server/src/services/delivery-work-notifications.ts b/server/src/services/delivery-work-notifications.ts index 2bc6840e690..315b548b40c 100644 --- a/server/src/services/delivery-work-notifications.ts +++ b/server/src/services/delivery-work-notifications.ts @@ -23,6 +23,7 @@ export const DELIVERY_QUEUES = { chatCompletion: "chat-completions", connection: "connection-continuations", question: "question-responses", + pluginLifecycle: "plugin-resource-lifecycle", toolAction: "tool-action-receipts", } as const; export type DeliveryQueue = typeof DELIVERY_QUEUES[keyof typeof DELIVERY_QUEUES]; diff --git a/server/src/services/execution-blocker.ts b/server/src/services/execution-blocker.ts index b4efb1a7e6c..9b3a16a3e0c 100644 --- a/server/src/services/execution-blocker.ts +++ b/server/src/services/execution-blocker.ts @@ -7,7 +7,7 @@ import { canContinueCancelledRun, readRunCancellation } from "./run-cancellation import { canRetryStoppedRun } from "./cancelled-native-startup.js"; import { queuedCommentIdsFromWakePayload } from "./issue-queued-comment-queue.js"; import { EXECUTION_RECONCILIATION_CAUSES, type ExecutionBlocker } from "@paperclipai/shared"; -import { LEGACY_WORKSPACE_RECOVERY_SCHEMA } from "./workspace-restore-recovery-state.js"; +import { hasRequiredWorkspaceRecovery, LEGACY_WORKSPACE_RECOVERY_SCHEMA } from "./workspace-restore-recovery-state.js"; /** Resolved recovery bookkeeping can still carry an effective no-replay hold. */ export function executionBlockerPredicate() { @@ -105,7 +105,7 @@ export async function getExecutionBlocker(db: Db, companyId: string, issueId: st )).limit(1) : []; let nextAction = action.nextAction; if (canRetry && !eligibleContinuation) nextAction += " Try again or send a new message to continue once the previous execution has stopped."; - if (chatBinding) { + if (chatBinding && !restoreHold) { if (chatBinding.state === "endpoint_removed" || !chatBinding.endpointStatus || chatBinding.endpointStatus === "archived") { nextAction = "This chat connection was removed. Inspect the stopped run and create a new task to continue the work."; } else if (chatBinding.state === "unavailable" || !["active", "verifying"].includes(chatBinding.endpointStatus) || @@ -129,7 +129,7 @@ export async function getExecutionBlocker(db: Db, companyId: string, issueId: st runError: runError?.slice(0, 1024) ?? null, canContinue: eligibleContinuation && !chatBinding, canRetry: canRetry && !chatBinding, - ...(restoreHold ? { workspaceRepairRequired: true } : {}), + ...(restoreHold ? { workspaceRepairRequired: true, canRecordWorkspaceRepair: hasRequiredWorkspaceRecovery(action.evidence) } : {}), savedMessageCount: saved.length, }; } diff --git a/server/src/services/plugin-lifecycle-delivery.ts b/server/src/services/plugin-lifecycle-delivery.ts new file mode 100644 index 00000000000..24c5dbc26da --- /dev/null +++ b/server/src/services/plugin-lifecycle-delivery.ts @@ -0,0 +1,122 @@ +import { and, asc, eq, isNull, or } from "drizzle-orm"; +import { pluginCompanySettings, pluginConfig, plugins, type Db } from "@paperclipai/db"; +import { logger } from "../middleware/logger.js"; +import { pluginLifecycleInbox } from "./plugin-lifecycle-inbox.js"; +import { DELIVERY_QUEUES } from "./delivery-work-notifications.js"; +import type { createDeliveryWorkCoordinator } from "./delivery-work-coordinator.js"; +import type { PluginWorkerManager } from "./plugin-worker-manager.js"; +import type { PluginLifecycleManager } from "./plugin-lifecycle.js"; + +type Coordinator = ReturnType; + +/** The journal is the queue. Notifications only wake its single local dispatcher. */ +export function registerPluginLifecycleDelivery( + coordinator: Coordinator, + db: Db, + manager: Pick, + lifecycle: Pick, +) { + // Startup and every wake remain pending until an admitted run inspects the inbox. + // The coordinator may reconcile signals while idle drain prevents dispatch. + let pending = true; + const retryAt = new Map(); + const retryMs = 30_000; + const delivery = coordinator.register(DELIVERY_QUEUES.pluginLifecycle, { + retryMs, + hasPending: async () => pending, + onSettled: () => { pending = true; }, + nextRunAt() { + if (!pending) return null; + let next: number | null = null; + for (const at of retryAt.values()) { + // An expired deadline during idle drain must not schedule a busy loop. + if (at > Date.now() && (next === null || at < next)) next = at; + } + return next; + }, + async run(signal) { + pending = false; + try { + for (const [key, at] of retryAt) if (at <= Date.now()) retryAt.delete(key); + const configured = await db.select({ plugin: plugins, companyId: pluginConfig.companyId }) + .from(plugins).innerJoin(pluginConfig, eq(pluginConfig.pluginId, plugins.id)) + .where(eq(plugins.status, "ready")) + .orderBy(asc(plugins.installOrder), asc(pluginConfig.companyId)); + for (const { plugin, companyId } of configured) { + const types = plugin.manifestJson.resourceLifecycle; + if (!types?.length || !plugin.manifestJson.capabilities.includes("events.subscribe")) continue; + const [disabled] = await db.select({ id: pluginCompanySettings.id }).from(pluginCompanySettings) + .where(and(eq(pluginCompanySettings.pluginId, plugin.id), eq(pluginCompanySettings.companyId, companyId), eq(pluginCompanySettings.enabled, false))); + if (disabled) continue; + const inbox = pluginLifecycleInbox(db, plugin.id); + let afterId: string | undefined; + // Visit independent resources past a failed or deferred head. + for (;;) { + if (signal.aborted) return; + const events = await inbox.list(companyId, 100, afterId, types); + for (const event of events) { + if (signal.aborted) return; + const retryKey = `${plugin.id}:${event.id}`; + if (retryAt.has(retryKey)) { pending = true; continue; } + if (!manager.isRunning(plugin.id)) { pending = true; break; } + let stage = "eligibility"; + try { + // Recheck enablement for each invocation; a disable during a batch must stop delivery. + const [enabled] = await db.select({ manifest: plugins.manifestJson }).from(plugins) + .leftJoin(pluginCompanySettings, and(eq(pluginCompanySettings.pluginId, plugins.id), eq(pluginCompanySettings.companyId, companyId))) + .where(and(eq(plugins.id, plugin.id), eq(plugins.status, "ready"), + or(isNull(pluginCompanySettings.enabled), eq(pluginCompanySettings.enabled, true)))); + if (!enabled?.manifest.resourceLifecycle?.includes(event.resourceType) || !enabled.manifest.capabilities.includes("events.subscribe")) break; + stage = "callback"; + const result = await manager.call(plugin.id, "resourceLifecycle", event, 30_000); + stage = "result"; + if (result?.eventId !== event.id || !["acknowledged", "retry", "deferred"].includes(result.status)) { + throw new Error("Invalid resource lifecycle result"); + } + if (signal.aborted) return; + if (result.status === "acknowledged") { + stage = "acknowledgment"; + await inbox.acknowledge(companyId, event.id); + // An acknowledgment exposes the next event for this resource. Drain immediately. + wake(); + } else if (result.status === "retry") { + retryAt.set(retryKey, Date.now() + retryMs); + pending = true; + } + } catch (error) { + retryAt.set(retryKey, Date.now() + retryMs); + // Provider error messages/data may contain secrets; retain only routing + // identifiers, the failed stage, and the protocol's numeric error code. + const code = (error as { code?: unknown } | null)?.code; + logger.warn({ pluginId: plugin.id, companyId, eventId: event.id, stage, + ...(typeof code === "number" ? { rpcCode: code } : {}) }, "Resource lifecycle delivery failed; retry scheduled"); + // Failed RPCs and uncertain acknowledgments leave durable work for replay. + pending = true; + } + } + if (events.length < 100) break; + afterId = events.at(-1)!.id; + } + } + } catch (error) { + pending = true; // A failed scan must survive a retry skipped during idle drain. + throw error; + } + }, + }); + function wake() { pending = true; delivery.wake(); } + const unsubscribeConfig = manager.onConfigApplied(wake); + lifecycle.on("plugin.loaded", wake); + lifecycle.on("plugin.enabled", wake); + lifecycle.on("plugin.worker_started", wake); + return { + ...delivery, + wake, + dispose() { + unsubscribeConfig(); + lifecycle.off("plugin.loaded", wake); + lifecycle.off("plugin.enabled", wake); + lifecycle.off("plugin.worker_started", wake); + }, + }; +} diff --git a/server/src/services/plugin-lifecycle-inbox.ts b/server/src/services/plugin-lifecycle-inbox.ts index 7187d83b069..e7aa0a4bf71 100644 --- a/server/src/services/plugin-lifecycle-inbox.ts +++ b/server/src/services/plugin-lifecycle-inbox.ts @@ -1,10 +1,10 @@ import { pluginCompanySettings, pluginLifecycleAcknowledgments, plugins, resourceLifecycleEvents, type Db } from "@paperclipai/db"; import type { ResourceLifecycleEvent } from "@paperclipai/plugin-sdk"; import { z } from "zod"; -import { and, asc, eq, gt, sql } from "drizzle-orm"; +import { and, asc, eq, gt, inArray, sql } from "drizzle-orm"; import { badRequest, conflict, forbidden, notFound } from "../errors.js"; -/** Durable pull delivery. Provider work stays outside tenant transactions. */ +/** Durable delivery inbox. Provider work stays outside tenant transactions. */ export function pluginLifecycleInbox(db: Db, pluginId: string) { const unacknowledged = sql`NOT EXISTS ( SELECT 1 FROM plugin_lifecycle_acknowledgments ack @@ -31,12 +31,12 @@ export function pluginLifecycleInbox(db: Db, pluginId: string) { } return { - async list(companyId: string, limit = 50, afterId?: string): Promise { + async list(companyId: string, limit = 50, afterId?: string, resourceTypes?: ResourceLifecycleEvent["resourceType"][]): Promise { if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw badRequest("limit must be an integer from 1 to 100"); if (afterId !== undefined && (typeof afterId !== "string" || !/^[1-9]\d*$/.test(afterId) || !Number.isSafeInteger(Number(afterId)))) throw badRequest("Invalid lifecycle page id"); await assertAvailable(db, companyId); const rows = await db.select().from(resourceLifecycleEvents) - .where(and(eq(resourceLifecycleEvents.companyId, companyId), unacknowledged, firstForResource, afterId === undefined ? undefined : gt(resourceLifecycleEvents.id, Number(afterId)))) + .where(and(eq(resourceLifecycleEvents.companyId, companyId), unacknowledged, firstForResource, resourceTypes ? inArray(resourceLifecycleEvents.resourceType, resourceTypes) : undefined, afterId === undefined ? undefined : gt(resourceLifecycleEvents.id, Number(afterId)))) .orderBy(asc(resourceLifecycleEvents.id)).limit(limit); return rows.map(row => ({ ...row, id: String(row.id), createdAt: row.createdAt.toISOString() })) as ResourceLifecycleEvent[]; }, diff --git a/server/src/services/plugin-registry.ts b/server/src/services/plugin-registry.ts index f86a550b949..2ff529cc2d8 100644 --- a/server/src/services/plugin-registry.ts +++ b/server/src/services/plugin-registry.ts @@ -28,6 +28,7 @@ import type { PluginWebhookDeliveryStatus, } from "@paperclipai/shared"; import { conflict, notFound } from "../errors.js"; +import { DELIVERY_QUEUES, notifyDeliveryWork } from "./delivery-work-notifications.js"; // --------------------------------------------------------------------------- // Helpers @@ -65,8 +66,8 @@ export function pluginRegistryService(db: Db) { // Internal helpers // ----------------------------------------------------------------------- - async function getById(id: string) { - return db + async function getById(id: string, database: Db = db) { + return database .select() .from(plugins) .where(eq(plugins.id, id)) @@ -323,37 +324,41 @@ export function pluginRegistryService(db: Db) { * otherwise a new row is inserted. */ upsertConfig: async (pluginId: string, companyId: string, input: UpsertPluginConfig) => { - const plugin = await getById(pluginId); - if (!plugin) throw notFound("Plugin not found"); + return db.transaction(async tx => { + const db = tx as unknown as Db; + await notifyDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle); + const plugin = await getById(pluginId, db); + if (!plugin) throw notFound("Plugin not found"); - const existing = await db - .select() - .from(pluginConfig) - .where(and(eq(pluginConfig.pluginId, pluginId), eq(pluginConfig.companyId, companyId))) - .then((rows) => rows[0] ?? null); - - if (existing) { - return db - .update(pluginConfig) - .set({ - configJson: input.configJson, - lastError: null, - updatedAt: new Date(), - }) + const existing = await db + .select() + .from(pluginConfig) .where(and(eq(pluginConfig.pluginId, pluginId), eq(pluginConfig.companyId, companyId))) + .then((rows) => rows[0] ?? null); + + if (existing) { + return db + .update(pluginConfig) + .set({ + configJson: input.configJson, + lastError: null, + updatedAt: new Date(), + }) + .where(and(eq(pluginConfig.pluginId, pluginId), eq(pluginConfig.companyId, companyId))) + .returning() + .then((rows) => rows[0]); + } + + return db + .insert(pluginConfig) + .values({ + pluginId, + companyId, + configJson: input.configJson, + }) .returning() .then((rows) => rows[0]); - } - - return db - .insert(pluginConfig) - .values({ - pluginId, - companyId, - configJson: input.configJson, - }) - .returning() - .then((rows) => rows[0]); + }); }, /** @@ -361,38 +366,42 @@ export function pluginRegistryService(db: Db) { * If no config row exists yet one is created with the supplied values. */ patchConfig: async (pluginId: string, companyId: string, input: PatchPluginConfig) => { - const plugin = await getById(pluginId); - if (!plugin) throw notFound("Plugin not found"); + return db.transaction(async tx => { + const db = tx as unknown as Db; + await notifyDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle); + const plugin = await getById(pluginId, db); + if (!plugin) throw notFound("Plugin not found"); - const existing = await db - .select() - .from(pluginConfig) - .where(and(eq(pluginConfig.pluginId, pluginId), eq(pluginConfig.companyId, companyId))) - .then((rows) => rows[0] ?? null); - - if (existing) { - const merged = { ...existing.configJson, ...input.configJson }; - return db - .update(pluginConfig) - .set({ - configJson: merged, - lastError: null, - updatedAt: new Date(), - }) + const existing = await db + .select() + .from(pluginConfig) .where(and(eq(pluginConfig.pluginId, pluginId), eq(pluginConfig.companyId, companyId))) + .then((rows) => rows[0] ?? null); + + if (existing) { + const merged = { ...existing.configJson, ...input.configJson }; + return db + .update(pluginConfig) + .set({ + configJson: merged, + lastError: null, + updatedAt: new Date(), + }) + .where(and(eq(pluginConfig.pluginId, pluginId), eq(pluginConfig.companyId, companyId))) + .returning() + .then((rows) => rows[0]); + } + + return db + .insert(pluginConfig) + .values({ + pluginId, + companyId, + configJson: input.configJson, + }) .returning() .then((rows) => rows[0]); - } - - return db - .insert(pluginConfig) - .values({ - pluginId, - companyId, - configJson: input.configJson, - }) - .returning() - .then((rows) => rows[0]); + }); }, /** @@ -439,43 +448,47 @@ export function pluginRegistryService(db: Db) { companyId: string, input: { enabled?: boolean; settingsJson: Record; lastError?: string | null }, ): Promise => { - const plugin = await getById(pluginId); - if (!plugin) throw notFound("Plugin not found"); + return db.transaction(async tx => { + const db = tx as unknown as Db; + await notifyDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle); + const plugin = await getById(pluginId, db); + if (!plugin) throw notFound("Plugin not found"); - const existing = await db - .select() - .from(pluginCompanySettings) - .where(and( - eq(pluginCompanySettings.pluginId, pluginId), - eq(pluginCompanySettings.companyId, companyId), - )) - .then((rows) => rows[0] ?? null); + const existing = await db + .select() + .from(pluginCompanySettings) + .where(and( + eq(pluginCompanySettings.pluginId, pluginId), + eq(pluginCompanySettings.companyId, companyId), + )) + .then((rows) => rows[0] ?? null); + + if (existing) { + return db + .update(pluginCompanySettings) + .set({ + enabled: input.enabled ?? existing.enabled, + settingsJson: input.settingsJson, + lastError: input.lastError ?? null, + updatedAt: new Date(), + }) + .where(eq(pluginCompanySettings.id, existing.id)) + .returning() + .then((rows) => rows[0]) as Promise; + } - if (existing) { return db - .update(pluginCompanySettings) - .set({ - enabled: input.enabled ?? existing.enabled, + .insert(pluginCompanySettings) + .values({ + pluginId, + companyId, + enabled: input.enabled ?? true, settingsJson: input.settingsJson, lastError: input.lastError ?? null, - updatedAt: new Date(), }) - .where(eq(pluginCompanySettings.id, existing.id)) .returning() .then((rows) => rows[0]) as Promise; - } - - return db - .insert(pluginCompanySettings) - .values({ - pluginId, - companyId, - enabled: input.enabled ?? true, - settingsJson: input.settingsJson, - lastError: input.lastError ?? null, - }) - .returning() - .then((rows) => rows[0]) as Promise; + }); }, // ----- Entities ------------------------------------------------------- diff --git a/server/src/services/plugin-worker-manager.ts b/server/src/services/plugin-worker-manager.ts index 2d3f961d530..5569f80c651 100644 --- a/server/src/services/plugin-worker-manager.ts +++ b/server/src/services/plugin-worker-manager.ts @@ -796,6 +796,8 @@ export interface WorkerDiagnostics { * for starting/stopping all workers and routing RPC calls. */ export interface PluginWorkerManager { + /** Subscribe after a worker successfully applies live company configuration. */ + onConfigApplied(listener: () => void): () => void; inspectIdleSleep?(hold: { ownerId: string; expiresAt: number }): Promise<{ backgroundWork: "none" | "present" | "unknown"; pluginIds: string[]; }>; @@ -3726,6 +3728,7 @@ export function createPluginWorkerManager( ): PluginWorkerManager { const log = logger.child({ service: "plugin-worker-manager" }); const workers = new Map(); + const configApplied = new Set<() => void>(); /** Per-plugin startup locks to prevent concurrent spawn races. */ const startupLocks = new Map>(); // The one shared, process-scoped aggregate route-slot controller. The manager @@ -3736,6 +3739,10 @@ export function createPluginWorkerManager( ); return { + onConfigApplied(listener) { + configApplied.add(listener); + return () => { configApplied.delete(listener); }; + }, async inspectIdleSleep(hold) { if (startupLocks.size) return { backgroundWork: "unknown", pluginIds: [] }; const snapshot = [...workers.entries()]; @@ -3872,7 +3879,14 @@ export function createPluginWorkerManager( new Error(`No worker registered for plugin "${pluginId}"`), ); } - return handle.call(method, params, timeoutMs, executeLogSink); + const result = handle.call(method, params, timeoutMs, executeLogSink); + if (method === "configChanged") void result.then(() => { + for (const listener of configApplied) { + try { listener(); } + catch { log.warn({ pluginId }, "Plugin configuration listener failed"); } + } + }, () => {}); + return result; }, openLoginPtySession(pluginId: string, input: LoginPtyOpenInput) { diff --git a/server/src/services/projects.ts b/server/src/services/projects.ts index cdd88c948c1..67c320f530f 100644 --- a/server/src/services/projects.ts +++ b/server/src/services/projects.ts @@ -1073,6 +1073,7 @@ export function projectService(db: Db, options: { captureWorkspaceUpdates?: bool if (retained) throw conflict("This project still supplies access policy for retained task workspaces. Archive the project or explicitly clean up its workspaces before deleting it."); const workspaces = await tx.select().from(projectWorkspaces).where(eq(projectWorkspaces.projectId, id)).for("update"); for (const workspace of workspaces) await preserveConfiguredProjectWorkspaceRootProvenance(tx, workspace); + await recordProjectLifecycleEvent(tx as unknown as Db, project.companyId, id, "delete"); const [row] = await tx.delete(projects).where(eq(projects.id, id)).returning(); return row ? { ...row, urlKey: deriveProjectUrlKey(row.name, row.id) } : null; }), diff --git a/server/src/services/resource-lifecycle-events.ts b/server/src/services/resource-lifecycle-events.ts index 73bd9444fb3..7d4c29692fa 100644 --- a/server/src/services/resource-lifecycle-events.ts +++ b/server/src/services/resource-lifecycle-events.ts @@ -1,5 +1,6 @@ import { resourceLifecycleEvents, type Db } from "@paperclipai/db"; import { sql } from "drizzle-orm"; +import { DELIVERY_QUEUES, notifyDeliveryWork } from "./delivery-work-notifications.js"; /** Call inside the transaction that creates the resource or approves the hire. */ export async function recordResourceCreationEvent( @@ -8,6 +9,7 @@ export async function recordResourceCreationEvent( resourceType: "agent" | "project", resourceId: string, ): Promise { + await notifyDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle); await db.insert(resourceLifecycleEvents).values({ companyId, resourceType, resourceId, action: "create" }).onConflictDoNothing({ target: [resourceLifecycleEvents.companyId, resourceLifecycleEvents.resourceType, resourceLifecycleEvents.resourceId], where: sql`${resourceLifecycleEvents.action} = 'create'`, @@ -21,10 +23,12 @@ export async function recordAgentStatusEvent(db: Db, companyId: string, agentId: : after === "paused" ? "pause" : before === "paused" && ["active", "idle", "running", "error"].includes(after) ? "resume" : null; if (!action) return; + await notifyDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle); await db.insert(resourceLifecycleEvents).values({ companyId, resourceType: "agent", resourceId: agentId, action }); } /** Call while holding the project row lock, in the mutation transaction. */ -export async function recordProjectLifecycleEvent(db: Db, companyId: string, projectId: string, action: "update" | "archive" = "update"): Promise { +export async function recordProjectLifecycleEvent(db: Db, companyId: string, projectId: string, action: "update" | "archive" | "delete" = "update"): Promise { + await notifyDeliveryWork(db, DELIVERY_QUEUES.pluginLifecycle); await db.insert(resourceLifecycleEvents).values({ companyId, resourceType: "project", resourceId: projectId, action }); } diff --git a/ui/src/api/issues.ts b/ui/src/api/issues.ts index f3a56c5ef63..ae713e11191 100644 --- a/ui/src/api/issues.ts +++ b/ui/src/api/issues.ts @@ -294,7 +294,7 @@ export const issuesApi = { workspaceBaseRef?: { requestedRef: string; branch: string }; actionId?: string; outcome: "restored" | "false_positive" | "blocked" | "cancelled"; - sourceIssueStatus: "todo" | "done" | "in_review" | "blocked"; + sourceIssueStatus: Issue["status"]; resolutionNote?: string | null; }, ) => diff --git a/ui/src/components/ExecutionBlockerNotice.tsx b/ui/src/components/ExecutionBlockerNotice.tsx index a0ecd58085f..e56dbf1659b 100644 --- a/ui/src/components/ExecutionBlockerNotice.tsx +++ b/ui/src/components/ExecutionBlockerNotice.tsx @@ -1,5 +1,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import type { ExecutionBlocker } from "@paperclipai/shared"; +import type { ExecutionBlocker, Issue } from "@paperclipai/shared"; +import { WorkspaceFileRecovery } from "./WorkspaceFileRecovery"; import { agentsApi } from "../api/agents"; import { activityApi } from "../api/activity"; import { queryKeys } from "../lib/queryKeys"; @@ -7,10 +8,12 @@ import { Button } from "./ui/button"; import { cn } from "../lib/utils"; import { Link } from "../lib/router"; -export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried, className }: { +export function ExecutionBlockerNotice({ companyId, issueId, issueStatus, canManage = false, blocker, onRetried, className }: { className?: string; companyId: string; issueId: string; + issueStatus?: Issue["status"]; + canManage?: boolean; blocker: ExecutionBlocker; onRetried: () => void; }) { @@ -72,6 +75,8 @@ export function ExecutionBlockerNotice({ companyId, issueId, blocker, onRetried,

{retry.error.message}

)} {runsError &&

{runsError.message}

} + {issueStatus && } ); } diff --git a/ui/src/components/WorkspaceFileRecovery.test.tsx b/ui/src/components/WorkspaceFileRecovery.test.tsx new file mode 100644 index 00000000000..975d7b08072 --- /dev/null +++ b/ui/src/components/WorkspaceFileRecovery.test.tsx @@ -0,0 +1,69 @@ +// @vitest-environment jsdom +import { act } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { ExecutionBlocker, Issue } from "@paperclipai/shared"; +import { WorkspaceFileRecovery } from "./WorkspaceFileRecovery"; +const resolve = vi.hoisted(() => vi.fn()); +vi.mock("../api/issues", () => ({ issuesApi: { resolveRecoveryAction: resolve } })); +const blocker = { recoveryActionId: "held-source", runId: "original-run", cause: "legacy_execution_requires_reconciliation", + workspaceRepairRequired: true, canRecordWorkspaceRepair: true, canRetry: false, canContinue: false, nextAction: "Recover the original files." } as ExecutionBlocker; + +describe("workspace file recovery", () => { + let root: Root, container: HTMLDivElement, client: QueryClient; + const onRecorded = vi.fn(); + beforeEach(() => { + resolve.mockReset().mockResolvedValue({}); onRecorded.mockReset(); + container = document.createElement("div"); document.body.append(container); root = createRoot(container); + client = new QueryClient({ defaultOptions: { mutations: { retry: false } } }); + }); + afterEach(async () => { await act(async () => root.unmount()); client.clear(); container.remove(); }); + async function mount(overrides: Partial[0]> = {}) { + await act(async () => root.render( + )); + } + async function enterEvidence() { + const textarea = container.querySelector("textarea")!; + await act(async () => { + Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")!.set!.call(textarea, + "Recovered and verified both repository patches in the linked backup artifact."); + textarea.dispatchEvent(new Event("input", { bubbles: true })); + }); + } + async function confirm() { await act(async () => container.querySelector('[role="checkbox"]')!.click()); } + async function submit() { + await act(async () => Array.from(container.querySelectorAll("button")).find(b => b.textContent === "Record file recovery")!.click()); + await act(async () => { await new Promise(resolve => setTimeout(resolve, 10)); }); + } + it.each(["in_progress", "in_review", "done", "cancelled"] as Issue["status"][])("records verified source files without changing the %s task", async issueStatus => { + await mount({ issueStatus }); + const button = Array.from(container.querySelectorAll("button")).find(b => b.textContent === "Record file recovery")!; + expect(button.disabled).toBe(true); await enterEvidence(); expect(button.disabled).toBe(true); + await confirm(); expect(button.disabled).toBe(false); await submit(); + expect(resolve).toHaveBeenCalledWith("task", expect.objectContaining({ actionId: "held-source", sourceIssueStatus: issueStatus, + outcome: "restored", executionReconciliation: expect.objectContaining({ runId: "original-run", providerStopped: true, + workspaceRepairEvidence: "Recovered and verified both repository patches in the linked backup artifact." }) })); + expect(onRecorded).toHaveBeenCalledOnce(); + expect(container.querySelector('[role="status"]')?.textContent).toContain("task status is unchanged"); + }); + it("keeps the hold and entered evidence when the server rejects unverified shutdown", async () => { + resolve.mockRejectedValueOnce(new Error("The original workspace has not confirmed that it stopped.")); + await mount(); await enterEvidence(); await confirm(); await submit(); + expect(container.querySelector('[role="alert"]')?.textContent).toContain("not confirmed"); + expect(container.querySelector("textarea")?.value).toContain("both repository patches"); + expect(onRecorded).not.toHaveBeenCalled(); + }); + it("does not offer attestation without board runtime access", async () => { + await mount({ canManage: false }); expect(container.querySelector("button")).toBeNull(); + expect(container.textContent).toContain("board member with runtime access"); expect(resolve).not.toHaveBeenCalled(); + }); + it.each([ + { workspaceRepairRequired: false }, { canRecordWorkspaceRepair: false }, { cause: "native_workspace_sync_out_retry_exhausted" }, + { recoveryActionId: null }, { runId: null }, + ])("does not expose file-only repair for other or unbound holds: %j", async patch => { + await mount({ blocker: { ...blocker, ...patch } }); expect(container.textContent).toBe(""); + expect(resolve).not.toHaveBeenCalled(); + }); +}); diff --git a/ui/src/components/WorkspaceFileRecovery.tsx b/ui/src/components/WorkspaceFileRecovery.tsx new file mode 100644 index 00000000000..2c5f52286ea --- /dev/null +++ b/ui/src/components/WorkspaceFileRecovery.tsx @@ -0,0 +1,55 @@ +import { useId, useState } from "react"; +import { useMutation } from "@tanstack/react-query"; +import type { ExecutionBlocker, Issue } from "@paperclipai/shared"; +import { issuesApi } from "../api/issues"; +import { Button } from "./ui/button"; +import { Checkbox } from "./ui/checkbox"; +import { Label } from "./ui/label"; +import { Textarea } from "./ui/textarea"; + +export function WorkspaceFileRecovery({ issueId, issueStatus, blocker, canManage, onRecorded }: { + issueId: string; + issueStatus: Issue["status"]; + blocker: ExecutionBlocker; + canManage: boolean; + onRecorded: () => void; +}) { + const noteId = useId(); + const confirmationId = useId(); + const [evidence, setEvidence] = useState(""); + const [confirmed, setConfirmed] = useState(false); + const record = useMutation({ + mutationFn: () => issuesApi.resolveRecoveryAction(issueId, { + actionId: blocker.recoveryActionId!, outcome: "restored", sourceIssueStatus: issueStatus, + resolutionNote: evidence.trim(), + executionReconciliation: { + runId: blocker.runId!, providerStopped: true, actionOutcome: "mixed", + outcomeEvidence: "This request records recovered workspace files only. It does not replay the source run or certify its external action outcomes.", + workspaceRepairEvidence: evidence.trim(), + }, + }), + onSuccess: onRecorded, + }); + if (!blocker.workspaceRepairRequired || !blocker.canRecordWorkspaceRepair || blocker.cause !== "legacy_execution_requires_reconciliation" + || !blocker.recoveryActionId || !blocker.runId) return null; + return
+ {record.isSuccess ?

File recovery recorded. The task status is unchanged. Continue the task when it is ready.

+ : canManage ? <> + +