diff --git a/tests/runner-e2e/CHAT-FEATURES.md b/tests/runner-e2e/CHAT-FEATURES.md index 60c7642d086..aca91dd2511 100644 --- a/tests/runner-e2e/CHAT-FEATURES.md +++ b/tests/runner-e2e/CHAT-FEATURES.md @@ -261,11 +261,17 @@ Initial setup uses the existing reporting environment: evaluation after an uncertain request. Initial route verification uses a small selection and does not qualify all 244 cases. -Each worker executes one case. Selections of up to three cases allow two cases -at once. Larger selections allow eight independent cases and one external-input -case at once. External-input cases remain serial because they can share QA -resources. A full run can take hours. Its elapsed time depends on setup and fleet -capacity; the declared case clocks are not a completion-time guarantee. +Each worker executes one case. Native cases allow two workers at once for any +selection size. Selections of up to three cases that include external input keep +the native lane at one worker. The separate external-input lane remains serial +because those cases can share QA resources. A larger mixed selection can therefore +run two native cases and one external-input case at once. + +This lower-load configuration changes scheduling, not case clocks, model settings +or zero automatic retries. It does not qualify eight-way native execution or +establish a cause for earlier noncompletion. A full run can take hours. Its elapsed +time depends on setup and fleet capacity; declared case clocks are not a +completion-time guarantee. Each finished worker starts its own protected reader and safe exporter. A separate reporting job uploads that case and verifies its result and two JSON @@ -335,3 +341,13 @@ The canonical candidate above retains Source5 and adds initial personal-secret a The reviewed product overlay from unmerged core PR #18742 at `61996a96dd09e90a5788e8711a94fc5ae631f425` allows a new Ask-first review for a strictly authenticated later same-person human intent following a declined request, including the normal reopened-comment source. Repeating the same intent stays denied; this grants no approval or execution authority. Both PostgreSQL route regressions ran and passed on that core head. The candidate preserves the compatible gateway and actual database row contracts; this is not a core merge or a qualified chat pass. Core PRs #18717, #18734 and #18742 remain unmerged. SDK115 completion remains unknown; the other five SDK cases remain held. For one selected case, the trusted controller downloads each exact bundle and publication artifact name into an explicit matching child directory. For multiple cases, it keeps separate named artifact directories. The gather command authenticates the frozen selection, every prepared bundle and its hashes before it copies the safe projections. Final coverage still authenticates the original publication receipts and verifies TestOps results without another export or upload. Empty, mixed, foreign and symbolic-link layouts fail closed. The producer's optional once-upload marker has an exact closed schema; it does not replace a verified publication receipt. + + +The optional Company-secret binding approval is declared only by +`secrets.configure-company-secret` with `optionalCompanySecretBindingApproval: true`. +Its two base human inputs may be followed by one independently verified UI approval +(maximum three actual human inputs). Its native bounds are explicitly 2..3 and its +original 480000ms attempt, model and zero automatic retries remain unchanged. +The controller copies this static marker from the candidate descriptor and checks +it against source task metadata; metadata extraction never executes fixture hooks. +This conditional accounting does not pre-complete approval or qualify a live pass. diff --git a/tests/runner-e2e/chat-completion-testops-cli.mjs b/tests/runner-e2e/chat-completion-testops-cli.mjs index f70f02fd48d..06749940598 100644 --- a/tests/runner-e2e/chat-completion-testops-cli.mjs +++ b/tests/runner-e2e/chat-completion-testops-cli.mjs @@ -37,7 +37,7 @@ export function executionPartition(selection, context, jobRunId) { const size = [matrix, native, external, selection.cases.map(row => row.executionId)] .reduce((sum, value) => sum + JSON.stringify(value).length * 2, 0); if (size > 1_000_000) reject('matrix_size'); - return { matrix, native, external, parallelism: small ? external.include.length ? '1' : '2' : '8' }; + return { matrix, native, external, parallelism: small && external.include.length ? '1' : '2' }; } async function readOwned(file, root, limit) { const absolute = path.resolve(file), base = await realpath(root); diff --git a/tests/runner-e2e/chat-completion-testops-contract.d.mts b/tests/runner-e2e/chat-completion-testops-contract.d.mts index 9fbfdd58ca0..99022cede53 100644 --- a/tests/runner-e2e/chat-completion-testops-contract.d.mts +++ b/tests/runner-e2e/chat-completion-testops-contract.d.mts @@ -10,7 +10,7 @@ export interface PilotCase { executionId: string; fullName: string; } -export type CatalogCase = Omit & { checks: number | null }; +export type CatalogCase = Omit & { checks: number | null; optionalCompanySecretBindingApproval?: true; expectedHumanMessageCount?: 2 }; export const cases: readonly Readonly[]; export const targetSha: string; export const launchSha256: string; diff --git a/tests/runner-e2e/chat-completion-testops-host-control.mjs b/tests/runner-e2e/chat-completion-testops-host-control.mjs index 350cb69f54e..24390ede523 100644 --- a/tests/runner-e2e/chat-completion-testops-host-control.mjs +++ b/tests/runner-e2e/chat-completion-testops-host-control.mjs @@ -3,6 +3,7 @@ import { mkdtemp, readFile, realpath, rm } from 'node:fs/promises'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; import { parse } from 'yaml'; +import { validateCompanySecretBindingApprovalSource } from './chat-full-catalog-testops-manifest.mjs'; import { model, catalogDefinition, catalogWorkflow } from './chat-completion-testops-contract.mjs'; export function paidWorker(definition, called) { @@ -55,6 +56,7 @@ export async function verifyHostContract(workspace, temporaryRoot, env = process || execution.task.attemptTimeoutMs.local !== descriptor.attemptTimeoutMs || execution.suite.definitionMetadata?.companyAndAgentBudgetCents !== 500 || execution.suite.definitionMetadata?.maximumAttemptsPerCell !== 1)) throw new Error('Pinned source selection mismatch'); + if (full) validateCompanySecretBindingApprovalSource(descriptor, execution.task); const controlEnv = { GITHUB_ACTIONS: 'true', GITHUB_REPOSITORY: 'paperclipai/paperclip', GITHUB_REF: 'refs/heads/master', GITHUB_WORKFLOW_REF: `paperclipai/paperclip/${workflow}@refs/heads/master`, GITHUB_RUN_ID: '1', GITHUB_RUN_ATTEMPT: '1', PAPERCLIP_RUNNER_E2E_SOURCE_SHA: targetSha, PAPERCLIP_CHAT_COMPLETION_TESTOPS: paid.env.PAPERCLIP_CHAT_COMPLETION_TESTOPS, diff --git a/tests/runner-e2e/chat-full-catalog-testops-adapter.test.mjs b/tests/runner-e2e/chat-full-catalog-testops-adapter.test.mjs index 73dbb5f35f0..49cf7e09b82 100644 --- a/tests/runner-e2e/chat-full-catalog-testops-adapter.test.mjs +++ b/tests/runner-e2e/chat-full-catalog-testops-adapter.test.mjs @@ -120,7 +120,7 @@ test('full catalog source admission is independent from the old three-case workf }); test('native and external partitions preserve exact selections, bounded overlapping small runs and the GitHub output bound', () => { - for (const rows of [selected, selected.slice(0, 3), selected.filter(row => row.externalInput), [selected[0], selected.find(row => row.externalInput)]]) { + for (const rows of [selected, selected.filter(row => !row.externalInput).slice(0, 10), selected.slice(0, 3), selected.filter(row => row.externalInput), [selected[0], selected.find(row => row.externalInput)]]) { const input = { ...selection, cases: rows }, partition = executionPartition(input, context, '77'); const merged = [...partition.native.include, ...partition.external.include].map(row => row.executionId); assert.equal(new Set(merged).size, rows.length); @@ -129,7 +129,7 @@ test('native and external partitions preserve exact selections, bounded overlapp assert.equal(partition.parallelism, rows.some(row => row.externalInput) ? '1' : '2'); assert.equal(partition.external.include.length, rows.filter(row => row.externalInput).length); } else { - assert.equal(partition.parallelism, '8'); assert.ok(partition.external.include.every(row => row.external)); + assert.equal(partition.parallelism, '2'); assert.ok(partition.external.include.every(row => row.external)); } assert.ok([partition.matrix, partition.native, partition.external, rows.map(row => row.executionId)] .reduce((sum, value) => sum + JSON.stringify(value).length * 2, 0) < 1_000_000); diff --git a/tests/runner-e2e/chat-full-catalog-testops-manifest.d.mts b/tests/runner-e2e/chat-full-catalog-testops-manifest.d.mts index 2e03ebd5e00..1a33f7d6cd9 100644 --- a/tests/runner-e2e/chat-full-catalog-testops-manifest.d.mts +++ b/tests/runner-e2e/chat-full-catalog-testops-manifest.d.mts @@ -4,3 +4,6 @@ export const catalogLauncherSha256: string; export const catalogManifestSha256: string; export const fullCatalogPlanName: string; export const fullCatalogCases: readonly Readonly[]; +export function companySecretBindingApprovalMetadata(declaration: Record): + { optionalCompanySecretBindingApproval?: true; expectedHumanMessageCount?: 2 }; +export function validateCompanySecretBindingApprovalSource(row: Record, task: Record): void; diff --git a/tests/runner-e2e/chat-full-catalog-testops-manifest.json b/tests/runner-e2e/chat-full-catalog-testops-manifest.json index 236431f640b..52a647156b9 100644 --- a/tests/runner-e2e/chat-full-catalog-testops-manifest.json +++ b/tests/runner-e2e/chat-full-catalog-testops-manifest.json @@ -1,6 +1,6 @@ { "schema": "paperclip.chat-full-catalog-testops-manifest/v1", - "targetSha": "d1ab241cac0db7f700fadaaf62ffb3d2b51cf0c3", + "targetSha": "fe547be84826b4611d3ba9ee1a7d7aa0bd0a2843", "metadataSourceSha": "de0941835cc8f0d01c982574ee258e110babdbeb", "profile": "runner-opencode", "model": "openrouter/deepseek/deepseek-v4.1-flash-1017", @@ -172,7 +172,7 @@ "baseTargetSha": "3b66c3c144360ddf32714a0742fd774b8ceafb16", "previousTargetSha": "ab2019e976076ec0eb60b026844f3d9211cb0208", "definitionSourceSha": "de0941835cc8f0d01c982574ee258e110babdbeb", - "candidateDescriptorSha256": "60db015337e807ff53a376b6db11c375a239097b224d2253dad68ecd0b0f61f3", + "candidateDescriptorSha256": "50e90d8c02ad57f444e27b522efe694db9734d47bdbc3a058475af77bac8b2ca", "changedDefinitionFiles": [ { "path": "tests/runner-e2e/chat-scenarios/companies.yaml", @@ -199,7 +199,7 @@ "sha256": "91a9ddc4f5357f19a2cdc73ff8a090c741d14780e4ed502c856ebd557986f7e1" } ], - "conditions": "Historical qualification conditions (retained; statements that all requests or sourceFiles remain fixed refer to those earlier successors): New candidate measurements only. Original definitions, prior failed attempts and source6 ab201 remain historical. This target retains that catalog and integrates merged runtime fixes #18711, #18713 and #18718, with the prior-run restoration condition adapted to its existing owner. It adds the reviewed previous-question locator, attributed human-only denial and conditional-outcome oracle, and actor-sensitive timeline narrative/recorded-reassignment fixes. Closed finish diagnostics retain an effective schema digest, hashed call correlation and bounded type/category enums only. Historical MCP timeout and continuation-shape causes remain unresolved; no new all-ten PASS is claimed. Queries, model, human/native counts and original clocks remain fixed; the earlier Studio144 explicit 2..3 accounting stays. Core PRs18717/18734/18742 are now merged. SDK115 completion remains UNKNOWN and the other five SDK cases remain held. Full244 runtime catalog is not merged to master and the196/244 gate is unchanged. This successor binds the seven reviewed scenario repairs plus framework evidence: canonical skill navigation, original announcement phase clock, attributed timeline context, bound hire revision history, credential warning and configuration-question admission, completed team visibility, and canonical private framework evidence. Twelve declaration fields across eleven cases change only oracle versions and skill source-file provenance. All original requests, setup, human/native bounds, model and clocks remain fixed. Historical failed attempts and missing-original UNKNOWN grades remain unchanged; no uniform-ten PASS is claimed. The existing SDK506/efa qualification is reused only for byte-identical registered source inputs. This next successor adds reviewed question-surface observation, grounded scenario grading and safe credential-guidance boundaries after the retained uniform attempt. Eleven existing oracle-version leaves advance; all sourceFiles declarations, requests, setup, counts, clocks, model, launcher and private protocol helpers remain fixed. Component qualification does not attest a new uniform-ten pass.\n\nPrevious successor conditions (retained): This successor measures five reviewed repairs at the retained original model and bounds. Exactly one request is clarified: multipart53 now explicitly asks to walk through the visible form; its original broad request and failures4623/4717 remain historical. The previous inventory0cf530 is retained and the disclosed clarified inventory is814bb363. Other243 catalog requests are unchanged. The company-secret second human message now references the actual saved non-secret metadata instead of repeating the initial request; native continuation must remain attributed to the actual agent. Five oracle-version leaves and four source-file additions change; launcher, private helpers, SDK506/efa, identities, counts, clocks and the196/244 gate remain fixed. All five successor journeys remain unrun; component proof is not a PASS. For Team610, independently saved canonical skill/version/member proof establishes recovery; no required recovery-success phrase is imposed. A contradictory current denial still fails. If the required skill remains missing, truthful disclosure of that condition remains required.\n\nPrevious retained successor conditions: This target adds the reviewed personal secure-entry confirmation handoff, the typographic team partial-warning observer correction, and the company-secret authorization fix from unmerged core PR #19325. Personal v10 retains two declared logical human actions: one canonical chat request plus one independently verified same-human secure-save confirmation handoff. Raw chat comments remain one. Team v5 accepts the actual named-skill warning with a typographic apostrophe; saved-state, repeat, runtime, link, and false-success checks remain required. The company route returns scoped 403 guidance before malformed-body validation and preserves board-only creation. Its effect on the unfinished native turn is unproven. The three affected journeys are unrun on this target. Existing failed attempts remain failed. All queries, expected outcomes, native and human bounds, clocks, profile, model, launcher, private protocol, SDK506/efa, and the 196/244 admission gate remain fixed, including the recorded case53 query clarification. Local component proof is not native feature qualification or main/core merge credit.\n\nPrevious retained successor conditions: This target repairs personal secure-entry observation and truthful human acknowledgment, company secure-entry language and ordinary visible menu navigation, team current-skill claim attribution, and native completion feedback that asks for usable product links. Personal v11 expands visible Details before entry when needed, binds the exact value-entry dialog instead of the underlying definition sheet, and sends a truthful secure-save acknowledgment on the plain-comment path. The latter path was not reached by the prior failed attempt. Company v9 and the four shared-language consumers v5 keep explicit non-chat exclusions local to secure entry. Team v6 retains independent saved-state proof and rejects contradictory current claims. The source includes the separately reviewed, unmerged completion-feedback core hunk. All affected native journeys remain unrun on this target; original failures remain failed. The prior personal first native took580602ms, so completion within the unchanged660s remains unproven. Queries, counts, clocks, model, retries, SDK506/efa, launcher and private transfer interfaces are unchanged from the previous target; the recorded case53 clarification remains retained. Local control proof is not native PASS or a core merge.\n\nPrevious retained successor conditions: This unrun successor adds only the reviewed Company reference, credential-request and actor-boundary corrections plus Team current-assertion temporal scoping. Canonical server default reference metadata is accepted without accepting unknown fields or relaxed policy. Independent unsafe transfer requests remain rejected. Team current denials cannot be hidden by unrelated historical recovery prose. Versions and descriptor bytes are unchanged from the previous unrun target. The six changed source files pass233 affected controls and full E2E types. All prior native failures and the personal latency limitation remain. All queries, run counts, clocks, model, retries, launcher, SDK506 and private interfaces remain unchanged.\n\nPrevious retained successor conditions: This final reviewed successor adds only bounded Team historical-assertion scope, the actual visible Account-menu path to Company settings, and nearest secure-destination attribution with ordinary secure page/form suffixes. Current contradictions and intervening public destinations remain rejected. The six changed source paths pass271 affected controls, two offline browser canaries and full E2E types. Descriptor bytes and unrun versions remain unchanged. All original failures, queries, counts, clocks, model, retries, SDK506, launcher, production inputs and private interfaces are retained. Native acceptance remains unproved until the next actual run.\n\nPrevious retained successor conditions: The measured parent reached seven independently reviewed passes out of ten. This successor changes only author-skill observation and the staged unsafe credential destination guard. Author v5 accepts proven author-only self-use without inventing assignment requirements, but preserves explicit global-assignment denial and source/resource proof. Shared guard semantics are versioned once across all six declared dependents: company v10, personal v12, and four external v6 consumers. The seven descriptor changes are oracle-version leaves only. All244 YAML identities, queries, context, expected outcomes, native/human bounds, clocks, credentials, model, SDK506, launcher and private interfaces remain unchanged. Local qualification passes214 affected controls and full E2E types. These versions remain native-unrun. Timeline and company native noncompletion remain unresolved; this source makes no latency or concurrency fix claim. The isolated company concurrency diagnostic run42124035785 remains a separate measurement. All original failures remain failed.\n\nPrevious retained successor conditions: The isolated company attempt5055 failed after a succeeded255949ms native turn produced a valid actor-bound secure saved-name question with a non-secret proposed metadata default. The previous oracle rejected any initialText. This successor admits only blank or bounded metadata defaults in the company saved-secret-name variant and verifies the same canonical payload in independent human accounting. Company v11 is native-unrun. Secure entry and the second native were not reached in the failed original. This is an assertion repair, not a runtime, latency or concurrency fix. All244 definitions retain their queries, contexts, expected outcomes, native/human bounds, clocks, credentials and model. The parent full-ten remains seven of ten measured passes. New local qualification passes286 distinct affected controls and full E2E types; SDK506, launcher and private interfaces remain unchanged. All failed originals stay failed.\n\nCurrent successor conditions: This successor scopes the Company private-entry observer to the actual Create secret dialog. A successful201 closes that form while opening a separate details Sheet; the old generic dialog locator could rebind and wait on the wrong Sheet. The actual worker-branch model-free canary proves successful creation with details still open and rejects failed POST or missing form. Full E2E types pass;286 unchanged functional controls are reused. Company v11 and descriptor bytes remain unchanged and native-unrun. No actual private Company save failure is claimed from the prior pre-entry attempt. The earlier CI runner shutdown cancellations and the test573 deferred-observer assertion are retained and unresolved; this observer repair makes no claim to fix them. New normal CI remains required. All244 conditions, queries, counts, clocks, credentials, model, SDK506, trusted private protocol and launcher remain unchanged.", + "conditions": "Historical qualification conditions (retained; statements that all requests or sourceFiles remain fixed refer to those earlier successors): New candidate measurements only. Original definitions, prior failed attempts and source6 ab201 remain historical. This target retains that catalog and integrates merged runtime fixes #18711, #18713 and #18718, with the prior-run restoration condition adapted to its existing owner. It adds the reviewed previous-question locator, attributed human-only denial and conditional-outcome oracle, and actor-sensitive timeline narrative/recorded-reassignment fixes. Closed finish diagnostics retain an effective schema digest, hashed call correlation and bounded type/category enums only. Historical MCP timeout and continuation-shape causes remain unresolved; no new all-ten PASS is claimed. Queries, model, human/native counts and original clocks remain fixed; the earlier Studio144 explicit 2..3 accounting stays. Core PRs18717/18734/18742 are now merged. SDK115 completion remains UNKNOWN and the other five SDK cases remain held. Full244 runtime catalog is not merged to master and the196/244 gate is unchanged. This successor binds the seven reviewed scenario repairs plus framework evidence: canonical skill navigation, original announcement phase clock, attributed timeline context, bound hire revision history, credential warning and configuration-question admission, completed team visibility, and canonical private framework evidence. Twelve declaration fields across eleven cases change only oracle versions and skill source-file provenance. All original requests, setup, human/native bounds, model and clocks remain fixed. Historical failed attempts and missing-original UNKNOWN grades remain unchanged; no uniform-ten PASS is claimed. The existing SDK506/efa qualification is reused only for byte-identical registered source inputs. This next successor adds reviewed question-surface observation, grounded scenario grading and safe credential-guidance boundaries after the retained uniform attempt. Eleven existing oracle-version leaves advance; all sourceFiles declarations, requests, setup, counts, clocks, model, launcher and private protocol helpers remain fixed. Component qualification does not attest a new uniform-ten pass.\n\nPrevious successor conditions (retained): This successor measures five reviewed repairs at the retained original model and bounds. Exactly one request is clarified: multipart53 now explicitly asks to walk through the visible form; its original broad request and failures4623/4717 remain historical. The previous inventory0cf530 is retained and the disclosed clarified inventory is814bb363. Other243 catalog requests are unchanged. The company-secret second human message now references the actual saved non-secret metadata instead of repeating the initial request; native continuation must remain attributed to the actual agent. Five oracle-version leaves and four source-file additions change; launcher, private helpers, SDK506/efa, identities, counts, clocks and the196/244 gate remain fixed. All five successor journeys remain unrun; component proof is not a PASS. For Team610, independently saved canonical skill/version/member proof establishes recovery; no required recovery-success phrase is imposed. A contradictory current denial still fails. If the required skill remains missing, truthful disclosure of that condition remains required.\n\nPrevious retained successor conditions: This target adds the reviewed personal secure-entry confirmation handoff, the typographic team partial-warning observer correction, and the company-secret authorization fix from unmerged core PR #19325. Personal v10 retains two declared logical human actions: one canonical chat request plus one independently verified same-human secure-save confirmation handoff. Raw chat comments remain one. Team v5 accepts the actual named-skill warning with a typographic apostrophe; saved-state, repeat, runtime, link, and false-success checks remain required. The company route returns scoped 403 guidance before malformed-body validation and preserves board-only creation. Its effect on the unfinished native turn is unproven. The three affected journeys are unrun on this target. Existing failed attempts remain failed. All queries, expected outcomes, native and human bounds, clocks, profile, model, launcher, private protocol, SDK506/efa, and the 196/244 admission gate remain fixed, including the recorded case53 query clarification. Local component proof is not native feature qualification or main/core merge credit.\n\nPrevious retained successor conditions: This target repairs personal secure-entry observation and truthful human acknowledgment, company secure-entry language and ordinary visible menu navigation, team current-skill claim attribution, and native completion feedback that asks for usable product links. Personal v11 expands visible Details before entry when needed, binds the exact value-entry dialog instead of the underlying definition sheet, and sends a truthful secure-save acknowledgment on the plain-comment path. The latter path was not reached by the prior failed attempt. Company v9 and the four shared-language consumers v5 keep explicit non-chat exclusions local to secure entry. Team v6 retains independent saved-state proof and rejects contradictory current claims. The source includes the separately reviewed, unmerged completion-feedback core hunk. All affected native journeys remain unrun on this target; original failures remain failed. The prior personal first native took580602ms, so completion within the unchanged660s remains unproven. Queries, counts, clocks, model, retries, SDK506/efa, launcher and private transfer interfaces are unchanged from the previous target; the recorded case53 clarification remains retained. Local control proof is not native PASS or a core merge.\n\nPrevious retained successor conditions: This unrun successor adds only the reviewed Company reference, credential-request and actor-boundary corrections plus Team current-assertion temporal scoping. Canonical server default reference metadata is accepted without accepting unknown fields or relaxed policy. Independent unsafe transfer requests remain rejected. Team current denials cannot be hidden by unrelated historical recovery prose. Versions and descriptor bytes are unchanged from the previous unrun target. The six changed source files pass233 affected controls and full E2E types. All prior native failures and the personal latency limitation remain. All queries, run counts, clocks, model, retries, launcher, SDK506 and private interfaces remain unchanged.\n\nPrevious retained successor conditions: This final reviewed successor adds only bounded Team historical-assertion scope, the actual visible Account-menu path to Company settings, and nearest secure-destination attribution with ordinary secure page/form suffixes. Current contradictions and intervening public destinations remain rejected. The six changed source paths pass271 affected controls, two offline browser canaries and full E2E types. Descriptor bytes and unrun versions remain unchanged. All original failures, queries, counts, clocks, model, retries, SDK506, launcher, production inputs and private interfaces are retained. Native acceptance remains unproved until the next actual run.\n\nPrevious retained successor conditions: The measured parent reached seven independently reviewed passes out of ten. This successor changes only author-skill observation and the staged unsafe credential destination guard. Author v5 accepts proven author-only self-use without inventing assignment requirements, but preserves explicit global-assignment denial and source/resource proof. Shared guard semantics are versioned once across all six declared dependents: company v10, personal v12, and four external v6 consumers. The seven descriptor changes are oracle-version leaves only. All244 YAML identities, queries, context, expected outcomes, native/human bounds, clocks, credentials, model, SDK506, launcher and private interfaces remain unchanged. Local qualification passes214 affected controls and full E2E types. These versions remain native-unrun. Timeline and company native noncompletion remain unresolved; this source makes no latency or concurrency fix claim. The isolated company concurrency diagnostic run42124035785 remains a separate measurement. All original failures remain failed.\n\nPrevious retained successor conditions: The isolated company attempt5055 failed after a succeeded255949ms native turn produced a valid actor-bound secure saved-name question with a non-secret proposed metadata default. The previous oracle rejected any initialText. This successor admits only blank or bounded metadata defaults in the company saved-secret-name variant and verifies the same canonical payload in independent human accounting. Company v11 is native-unrun. Secure entry and the second native were not reached in the failed original. This is an assertion repair, not a runtime, latency or concurrency fix. All244 definitions retain their queries, contexts, expected outcomes, native/human bounds, clocks, credentials and model. The parent full-ten remains seven of ten measured passes. New local qualification passes286 distinct affected controls and full E2E types; SDK506, launcher and private interfaces remain unchanged. All failed originals stay failed.\n\nPrevious retained successor conditions: This successor scopes the Company private-entry observer to the actual Create secret dialog. A successful201 closes that form while opening a separate details Sheet; the old generic dialog locator could rebind and wait on the wrong Sheet. The actual worker-branch model-free canary proves successful creation with details still open and rejects failed POST or missing form. Full E2E types pass;286 unchanged functional controls are reused. Company v11 and descriptor bytes remain unchanged and native-unrun. No actual private Company save failure is claimed from the prior pre-entry attempt. The earlier CI runner shutdown cancellations and the test573 deferred-observer assertion are retained and unresolved; this observer repair makes no claim to fix them. New normal CI remains required. All244 conditions, queries, counts, clocks, credentials, model, SDK506, trusted private protocol and launcher remain unchanged.\n\nCurrent successor conditions: This target retains D1 and adds two evidence-backed assertion/setup repairs. Timeline v11 accepts a completed saved artifact edit with an immediately bound command Result bullet; actual5056 fully described the ordered history but remains originally FAILED. Company v12 accepts only the actual governed secret-binding approval after secure save, with current-person/question/payload/resolver/audit/continuation and saved binding proof. Its conditions explicitly change from exactly two native runs to minimum two, maximum three, and from two base human inputs to those same two plus at most one independently verified UI approval (maximum three actual human inputs). The original 480000ms Company attempt is unchanged. Actual5057 retained a saved secret and pending binding approval, remains FAILED, and did not prove native3 completion. All244 queries, model, clocks, credentials and other case bounds remain unchanged. The controller native partition is explicitly capped at two as a bounded lower-concurrency experiment, not a demonstrated performance or reliability fix; external serialization remains. Next selected466+578 experiment and later full ten remain UNRUN. SDK506, launcher and private evidence wire are unchanged; source contract/flow accounting and the static case-only marker change explicitly. No core or catalog merge/PASS is claimed.", "changedFields": [ { "key": "activity.work-timeline", @@ -236,7 +236,7 @@ "key": "activity.work-timeline", "field": "oracleVersion", "previous": "paperclip.chat-catalog.internal.v1", - "current": "paperclip.chat-catalog.work-timeline.v10" + "current": "paperclip.chat-catalog.work-timeline.v11" }, { "key": "activity.work-timeline", @@ -620,6 +620,7 @@ ], "current": [ "chat-catalog-domains/external-company-secret.ts", + "chat-catalog-domains/external-secret-proposal.ts", "chat-catalog-domains/external-secret-question.ts", "chat-catalog-domains/external-secret-route.ts", "chat-catalog-domains/external-company-secret-menu.ts", @@ -643,7 +644,7 @@ "key": "secrets.configure-company-secret", "field": "oracleVersion", "previous": "paperclip.chat-catalog.external.v1", - "current": "paperclip.chat-catalog.company-secret-controller-baseline.v11" + "current": "paperclip.chat-catalog.company-secret-controller-baseline.v12" }, { "key": "secrets.personal-secret", @@ -950,6 +951,24 @@ "field": "originalQuery", "previous": "Help me choose the details for the launch page before you build it.", "current": "Walk me through the launch-page question form so I can choose the audience and tone and enter my own headline before you build anything." + }, + { + "key": "secrets.configure-company-secret", + "field": "maximumRuns", + "previous": 2, + "current": 3 + }, + { + "key": "secrets.configure-company-secret", + "field": "optionalCompanySecretBindingApproval", + "previous": null, + "current": true + }, + { + "key": "secrets.configure-company-secret", + "field": "expectedHumanMessageCount", + "previous": null, + "current": 2 } ], "candidateDescriptorSnapshot": "fixtures/chat-repair-candidate-descriptors.json", @@ -989,7 +1008,7 @@ "key": "activity.work-timeline", "field": "oracleVersion", "previous": "paperclip.chat-catalog.internal.v1", - "current": "paperclip.chat-catalog.work-timeline.v10" + "current": "paperclip.chat-catalog.work-timeline.v11" }, { "key": "agents.choose-primary", @@ -1343,6 +1362,7 @@ ], "current": [ "chat-catalog-domains/external-company-secret.ts", + "chat-catalog-domains/external-secret-proposal.ts", "chat-catalog-domains/external-secret-question.ts", "chat-catalog-domains/external-secret-route.ts", "chat-catalog-domains/external-company-secret-menu.ts", @@ -1366,7 +1386,7 @@ "key": "secrets.configure-company-secret", "field": "oracleVersion", "previous": "paperclip.chat-catalog.external.v1", - "current": "paperclip.chat-catalog.company-secret-controller-baseline.v11" + "current": "paperclip.chat-catalog.company-secret-controller-baseline.v12" }, { "key": "secrets.personal-secret", @@ -1722,6 +1742,24 @@ "field": "oracleVersion", "previous": "paperclip.chat-catalog.external.v1", "current": "paperclip.chat-catalog.team-install.v6" + }, + { + "key": "secrets.configure-company-secret", + "field": "expectedRunCount", + "previous": 2, + "current": 3 + }, + { + "key": "secrets.configure-company-secret", + "field": "optionalCompanySecretBindingApproval", + "previous": null, + "current": true + }, + { + "key": "secrets.configure-company-secret", + "field": "minimumRunCount", + "previous": null, + "current": 2 } ], "runtimeConditions": { @@ -1855,7 +1893,7 @@ }, "candidateLauncherSnapshot": { "path": "fixtures/chat-repair-candidate-launcher.txt", - "targetSha": "d1ab241cac0db7f700fadaaf62ffb3d2b51cf0c3", + "targetSha": "fe547be84826b4611d3ba9ee1a7d7aa0bd0a2843", "sourcePath": "tests/runner-e2e/launch.ts", "gitBlobOid": "c88df15f509b2ac8638af826647a97d45f16bd1f" } @@ -2398,7 +2436,7 @@ "activity/timeline" ], "checks": null, - "oracleVersion": "paperclip.chat-catalog.work-timeline.v10", + "oracleVersion": "paperclip.chat-catalog.work-timeline.v11", "sourceFiles": [ "chat-catalog-domains/internal-definition.ts", "chat-catalog-domains/internal-support.ts", @@ -11170,7 +11208,7 @@ "fullName": "paperclip.Product E2E.chat-features.secrets.configure-company-secret", "aliases": [], "minimumRuns": 2, - "maximumRuns": 2, + "maximumRuns": 3, "turnTimeoutMs": 0, "declaredTurnTimeoutMs": 120000, "attemptTimeoutMs": 480000, @@ -11190,9 +11228,10 @@ "connection-setup/credentials" ], "checks": null, - "oracleVersion": "paperclip.chat-catalog.company-secret-controller-baseline.v11", + "oracleVersion": "paperclip.chat-catalog.company-secret-controller-baseline.v12", "sourceFiles": [ "chat-catalog-domains/external-company-secret.ts", + "chat-catalog-domains/external-secret-proposal.ts", "chat-catalog-domains/external-secret-question.ts", "chat-catalog-domains/external-secret-route.ts", "chat-catalog-domains/external-company-secret-menu.ts", @@ -11214,7 +11253,9 @@ "screenshot": "final-state.png", "evidenceKind": "private-final-state-png", "evidenceReadiness": "original-private-evidence-validation-required", - "boundaryTarget": null + "boundaryTarget": null, + "optionalCompanySecretBindingApproval": true, + "expectedHumanMessageCount": 2 }, { "key": "secrets.least-scope-delivery", diff --git a/tests/runner-e2e/chat-full-catalog-testops-manifest.mjs b/tests/runner-e2e/chat-full-catalog-testops-manifest.mjs index 8bc8df28ed6..8ba888bf7c4 100644 --- a/tests/runner-e2e/chat-full-catalog-testops-manifest.mjs +++ b/tests/runner-e2e/chat-full-catalog-testops-manifest.mjs @@ -8,6 +8,26 @@ const surfaces = new Set(['agent-chat', 'browser-boundary', 'scoped-external-ass const nonnegative = value => Number.isSafeInteger(value) && value >= 0; const text = value => typeof value === 'string' && value.trim().length > 0; +export function companySecretBindingApprovalMetadata(declaration) { + if (!Object.hasOwn(declaration, 'optionalCompanySecretBindingApproval')) return {}; + if (declaration.optionalCompanySecretBindingApproval !== true + || declaration.scenarioId !== 'secrets.configure-company-secret' + || declaration.expectedRunCount !== 3 || declaration.minimumRunCount !== 2 + || declaration.expectedHumanMessageCount !== 2 || declaration.attemptTimeoutMs !== 480000) { + throw new Error('chat_full_catalog_company_approval_invalid'); + } + return { optionalCompanySecretBindingApproval: true, expectedHumanMessageCount: 2 }; +} + +export function validateCompanySecretBindingApprovalSource(row, task) { + const declared = companySecretBindingApprovalMetadata({ ...row, scenarioId: row.key, + expectedRunCount: row.maximumRuns, minimumRunCount: row.minimumRuns }); + const actual = companySecretBindingApprovalMetadata({ ...task, scenarioId: task.id, + minimumRunCount: task.minimumExpectedRunCount ?? task.expectedRunCount, + attemptTimeoutMs: task.attemptTimeoutMs?.local }); + if (JSON.stringify(actual) !== JSON.stringify(declared)) throw new Error('Pinned source approval mismatch'); +} + export function validateFullCatalogManifest(value) { if (value?.schema !== 'paperclip.chat-full-catalog-testops-manifest/v1' || !/^[a-f0-9]{64}$/.test(value.launcherSha256 ?? '') @@ -15,6 +35,8 @@ export function validateFullCatalogManifest(value) { || value.profile !== 'runner-opencode' || value.model !== 'openrouter/deepseek/deepseek-v4.1-flash-1017' || !Array.isArray(value.cases) || value.cases.length !== 244) throw new Error('chat_full_catalog_manifest_invalid'); for (const row of value.cases) { + companySecretBindingApprovalMetadata({ ...row, scenarioId: row.key, + expectedRunCount: row.maximumRuns, minimumRunCount: row.minimumRuns }); const task = legacy.has(row.key) ? row.key.slice(6) : row.key; if (!/^[a-z0-9-]+\.[a-z0-9-]+$/.test(row.key ?? '') || row.canonicalScenario !== row.key || row.scenario !== task || row.area !== row.key.split('.')[0] || !text(row.name) || !text(row.title) diff --git a/tests/runner-e2e/chat-full-catalog-testops-manifest.test.mjs b/tests/runner-e2e/chat-full-catalog-testops-manifest.test.mjs index 61b43d4ff9b..7592fbcf882 100644 --- a/tests/runner-e2e/chat-full-catalog-testops-manifest.test.mjs +++ b/tests/runner-e2e/chat-full-catalog-testops-manifest.test.mjs @@ -3,7 +3,7 @@ import assert from 'node:assert/strict'; import { createHash } from 'node:crypto'; import { readFileSync, readdirSync } from 'node:fs'; import { createRequire } from 'node:module'; -import { catalogTargetSha, catalogManifestSha256, fullCatalogPlanName, fullCatalogCases, validateFullCatalogManifest } from './chat-full-catalog-testops-manifest.mjs'; +import { catalogTargetSha, catalogManifestSha256, fullCatalogPlanName, fullCatalogCases, validateFullCatalogManifest, companySecretBindingApprovalMetadata, validateCompanySecretBindingApprovalSource } from './chat-full-catalog-testops-manifest.mjs'; const { parseDocument } = createRequire(import.meta.url)('yaml'); const bytes = readFileSync(new URL('./chat-full-catalog-testops-manifest.json', import.meta.url)); @@ -103,7 +103,7 @@ test('repair source metadata binds the corrected candidate and explicit changed const repair = manifest.source.repairMetadata; assert.equal(repair.baseTargetSha, '3b66c3c144360ddf32714a0742fd774b8ceafb16'); assert.equal(repair.previousTargetSha, 'ab2019e976076ec0eb60b026844f3d9211cb0208'); - assert.equal(manifest.targetSha, 'd1ab241cac0db7f700fadaaf62ffb3d2b51cf0c3'); + assert.equal(manifest.targetSha, 'fe547be84826b4611d3ba9ee1a7d7aa0bd0a2843'); assert.equal(repair.definitionSourceSha, manifest.metadataSourceSha); assert.equal(repair.candidateDescriptorSnapshot, 'fixtures/chat-repair-candidate-descriptors.json'); const descriptorBytes = readFileSync(new URL(repair.candidateDescriptorSnapshot, import.meta.url)); @@ -319,7 +319,10 @@ test('repair source metadata binds the corrected candidate and explicit changed [ "questions-and-approvals.multi-part-question", "originalQuery" - ] + ], + ['secrets.configure-company-secret', 'maximumRuns'], + ['secrets.configure-company-secret', 'optionalCompanySecretBindingApproval'], + ['secrets.configure-company-secret', 'expectedHumanMessageCount'] ]); for (const change of repair.changedFields) { assert.deepEqual(row(change.key)[change.field], change.current); @@ -330,9 +333,9 @@ test('repair source metadata binds the corrected candidate and explicit changed assert.equal(repair.runtimeConditions.multipart53QuestionClarification.unchangedOtherCatalogQueries, 243); assert.equal(repair.runtimeConditions.multipart53QuestionClarification.previousInventoryDigest, '0cf530c11449508e91b5ce664d9375fc8e76466fa908255a1af0ff55169d0220'); assert.equal(repair.runtimeConditions.multipart53QuestionClarification.inventoryDigest, '814bb3633268cb0488563aed51fce2f987bd47650ddfb5a3302d0354292c2323'); - assert.equal(repair.descriptorChanges.length, 49); + assert.equal(repair.descriptorChanges.length, 52); assert.equal(new Set(repair.descriptorChanges.map(change => change.key)).size, 33); - assert.deepEqual([...new Set(repair.descriptorChanges.map(change => change.field))].sort(), ['expectedRunCount', 'minimumRunCount', 'oracleVersion', 'setupPolicy', 'sourceFiles']); + assert.deepEqual([...new Set(repair.descriptorChanges.map(change => change.field))].sort(), ['expectedRunCount', 'minimumRunCount', 'optionalCompanySecretBindingApproval', 'oracleVersion', 'setupPolicy', 'sourceFiles']); for (const change of repair.descriptorChanges) { assert.notDeepEqual(change.current, change.previous); const declaration = declarations.find(value => value.scenarioId === change.key); @@ -350,10 +353,13 @@ test('repair source metadata binds the corrected candidate and explicit changed assert.equal(value.oracleVersion, declaration.oracleVersion); assert.equal(value.minimumRuns, declaration.minimumRunCount ?? declaration.expectedRunCount); assert.equal(value.maximumRuns, declaration.expectedRunCount); + const approval = companySecretBindingApprovalMetadata(declaration); + assert.deepEqual(Object.fromEntries(Object.keys(approval).map(key => [key, value[key]])), approval); + assert.equal(Object.hasOwn(value, 'optionalCompanySecretBindingApproval'), Object.hasOwn(approval, 'optionalCompanySecretBindingApproval')); assert.equal(value.declaredTurnTimeoutMs, declaration.turnTimeoutMs); assert.equal(value.attemptTimeoutMs, declaration.attemptTimeoutMs); } - assert.deepEqual(repair.changedFields.filter(change => ['minimumRuns', 'maximumRuns'].includes(change.field)), [{ key: 'skills.studio-test', field: 'maximumRuns', previous: 2, current: 3 }]); + assert.deepEqual(repair.changedFields.filter(change => ['minimumRuns', 'maximumRuns'].includes(change.field)), [{ key: 'skills.studio-test', field: 'maximumRuns', previous: 2, current: 3 }, { key: 'secrets.configure-company-secret', field: 'maximumRuns', previous: 2, current: 3 }]); assert.deepEqual(repair.runtimeConditions.studio144Accounting, { key: 'skills.studio-test', previousMinimumRuns: 2, previousMaximumRuns: 2, minimumRuns: 2, maximumRuns: 3, requiredActualAcceptedRuns: 3, originalAttemptTimeoutMs: 540000, humanMessages: 1, condition: 'Initial CEO + real Studio worker + exactly one source-bound completion delivery;2-run no-delivery positive is rejected.' }); assert.equal(repair.runtimeConditions.toolGuidance106.corePRUnmerged, false); assert.equal(repair.runtimeConditions.sdkPublicProof.scannerAndCanaryCertificatesUnchanged, true); @@ -375,3 +381,44 @@ test('repair source metadata binds the corrected candidate and explicit changed "actualPostAssertionPGCasesPassed": 2 }); }); + + +test('Company approval metadata is projected without executing a fixture and rejects foreign or changed bounds', () => { + const declaration = { scenarioId: 'secrets.configure-company-secret', optionalCompanySecretBindingApproval: true, + expectedRunCount: 3, minimumRunCount: 2, expectedHumanMessageCount: 2, attemptTimeoutMs: 480000, + setup() { throw new Error('Fixture must not execute'); } }; + assert.deepEqual(companySecretBindingApprovalMetadata(declaration), + { optionalCompanySecretBindingApproval: true, expectedHumanMessageCount: 2 }); + assert.deepEqual(companySecretBindingApprovalMetadata({ scenarioId: 'other' }), {}); + const future = structuredClone(manifest); + Object.assign(future.cases.find(value => value.key === declaration.scenarioId), + companySecretBindingApprovalMetadata(declaration), { minimumRuns: 2, maximumRuns: 3 }); + assert.equal(validateFullCatalogManifest(future), future); + for (const mutation of [ + { scenarioId: 'secrets.personal-secret' }, { optionalCompanySecretBindingApproval: false }, + { expectedRunCount: 2 }, { minimumRunCount: 3 }, { expectedHumanMessageCount: 3 }, { attemptTimeoutMs: 480001 }, + ]) assert.throws(() => companySecretBindingApprovalMetadata({ ...declaration, ...mutation }), /company_approval_invalid/); + for (const mutation of [ + { key: 'secrets.personal-secret' }, { optionalCompanySecretBindingApproval: false }, + { maximumRuns: 2 }, { minimumRuns: 3 }, { expectedHumanMessageCount: 3 }, { attemptTimeoutMs: 480001 }, + ]) { + const invalid = structuredClone(future); + Object.assign(invalid.cases.find(value => value.key === declaration.scenarioId), mutation); + assert.throws(() => validateFullCatalogManifest(invalid), /company_approval_invalid/); + } +}); + + +test('source admission joins the optional Company approval and base humans to actual static task metadata', () => { + const row = { key: 'secrets.configure-company-secret', optionalCompanySecretBindingApproval: true, + minimumRuns: 2, maximumRuns: 3, expectedHumanMessageCount: 2, attemptTimeoutMs: 480000 }; + const task = { id: row.key, optionalCompanySecretBindingApproval: true, minimumExpectedRunCount: 2, + expectedRunCount: 3, expectedHumanMessageCount: 2, attemptTimeoutMs: { local: 480000 } }; + assert.doesNotThrow(() => validateCompanySecretBindingApprovalSource(row, task)); + for (const mutation of [{ optionalCompanySecretBindingApproval: undefined }, { expectedHumanMessageCount: 3 }, + { id: 'secrets.personal-secret' }, { expectedRunCount: 2 }, { minimumExpectedRunCount: 3 }]) { + assert.throws(() => validateCompanySecretBindingApprovalSource(row, { ...task, ...mutation })); + } + const unmarked = { ...row }; delete unmarked.optionalCompanySecretBindingApproval; + assert.throws(() => validateCompanySecretBindingApprovalSource(unmarked, task), /approval mismatch/); +}); diff --git a/tests/runner-e2e/chat-full-catalog-testops-reconcile.mjs b/tests/runner-e2e/chat-full-catalog-testops-reconcile.mjs index 35251f87925..9d398aa4b57 100644 --- a/tests/runner-e2e/chat-full-catalog-testops-reconcile.mjs +++ b/tests/runner-e2e/chat-full-catalog-testops-reconcile.mjs @@ -31,6 +31,7 @@ function desiredMetadata(row) { `Full-catalog lane targets candidate ${catalogTargetSha}; other lanes keep their own actual source; target is recorded per result. This registration does not qualify current master or record a PASS.`, `Assumed context:\n${row.originalContext}`, `Required credential names: ${row.requiredCredentials.join(', ') || 'None declared for this case; the controller profile credential is separate.'}`, + ...(row.optionalCompanySecretBindingApproval ? ['Company secret binding uses two base human inputs plus at most one source-bound, accepted UI approval (maximum three actual human inputs). Native runs: minimum two, maximum three; original 480000ms attempt remains.'] : []), `External input contract: ${row.externalInput == null ? 'None declared.' : JSON.stringify(row.externalInput)}`, `Installed-source requirement: ${row.installedSource ?? 'None declared.'}`, `Source evidence kind: ${row.evidenceKind}`, diff --git a/tests/runner-e2e/fixtures/chat-repair-candidate-descriptors.json b/tests/runner-e2e/fixtures/chat-repair-candidate-descriptors.json index c38dccd72b7..2ec5b97539e 100644 --- a/tests/runner-e2e/fixtures/chat-repair-candidate-descriptors.json +++ b/tests/runner-e2e/fixtures/chat-repair-candidate-descriptors.json @@ -258,7 +258,7 @@ "../../server/src/services/native-runtime/status-decision-committer.ts", "../../server/src/services/work-timeline.ts" ], - "oracleVersion": "paperclip.chat-catalog.work-timeline.v10", + "oracleVersion": "paperclip.chat-catalog.work-timeline.v11", "coverageGaps": [], "expectedRunCount": 3, "minimumRunCount": 3, @@ -4891,6 +4891,7 @@ "scenarioId": "secrets.configure-company-secret", "sourceFiles": [ "chat-catalog-domains/external-company-secret.ts", + "chat-catalog-domains/external-secret-proposal.ts", "chat-catalog-domains/external-secret-question.ts", "chat-catalog-domains/external-secret-route.ts", "chat-catalog-domains/external-company-secret-menu.ts", @@ -4909,9 +4910,11 @@ "chat-flow.ts", "chat-scenarios/secrets.yaml" ], - "oracleVersion": "paperclip.chat-catalog.company-secret-controller-baseline.v11", - "expectedRunCount": 2, + "oracleVersion": "paperclip.chat-catalog.company-secret-controller-baseline.v12", + "expectedRunCount": 3, + "minimumRunCount": 2, "expectedHumanMessageCount": 2, + "optionalCompanySecretBindingApproval": true, "turnTimeoutMs": 120000, "attemptTimeoutMs": 480000, "authentication": "human-session",