mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-13 07:12:00 +01:00
## Thinking Path > - Paperclip helps people manage AI agents and their work. > - Protected TestOps runs admit one immutable catalog source. > - Three held scenario observers now have reviewed, locally qualified repairs. > - A separate Cloud recovery test started its heading wait before the Cloud return. > - This PR pins the qualified successor and corrects that test boundary. > - It changes no product behavior, model, execution budget, or private evidence protocol. ## Linked Issues or Issue Description Refs: #19155, #19093, #19095, #19112. The next candidate retains the reviewed skill, company-secret, and team-install observer repairs. It adds a narrow timeline v9 correction: a recorded task identifier can be the subject of its actual reassignment. The real timeline attempt remains failed; the diagnostic correction is not a regrade or a new native pass. Earlier failed attempts stay retained; a source pin and component tests do not establish a native pass. The Cloud recovery fixture failed both direct-task variants in the first CI attempt of #19155. Its five-second heading check started before the external Cloud roundtrip ended. The failure image already showed the expected task shortly afterward. One unchanged local direct-task control passed. The single authorized CI recovery also passed; this did not establish a reliable boundary. This PR waits for one handoff and the exact return URL before the original heading check. ## What Changed - Pin source `5aef4bffa2b106f2066bf2485338798f04a702bd`, tree `464be838a448c728d44e2d5593b9d7faea266663`. - Update the two manifest target literals and expected pin test literal. Bind the exact v9 descriptor snapshot and its one changed timeline oracle leaf. - Add the reviewed two-line Cloud return readiness witness. Keep the original five-second heading assertion and existing overall test limit. - Use descriptor `744a566faaa90b8048fff526e0a30b7475b86c2de88d697783f8da9e02f70e20`. Keep the launcher, all private helpers, 244 identities, requests, setup, human/native counts, clocks, profile, model, retry policy, permissions, and the 196/244 gate unchanged. ## Verification - Current infrastructure worktree: **131 pin, admission, selection, streaming, API, and reconciliation controls passed**. - The fixture owner ran the exact patched Cloud spec: **all five real auth cases passed** in 37.4s. A six-second Cloud hold passed the unchanged heading check after return. Missing handoff and missing return controls rejected. This PR's fresh master base is verified by current-head CI, not credited from the earlier run. - Fresh v9 source qualification: **268 focused timeline controls**, the 242-declaration gate, fresh server declarations, and full E2E typecheck passed under local Node **26.4.0**. The preceding held-helper qualification (239 Node controls and one offline Chromium route control) remains evidence for the byte-identical unaffected helper closures. The original browser sandbox failure is retained. Actual AWS Node and model configuration remain unchanged. - All 506 registered SDK input Git blobs and registry `efa70621e9fb42aa185f7ee616a1a726aa3eccb367e67e28817cedf26ef2ade8` equal the prior qualified source. No new SDK build or runtime refresh. - All 75 private protocol files, production trees, YAML, lockfile, launcher, identities, counts, clocks, and sourceFiles declarations equal the prior source. Only the timeline descriptor oracle advances v8 to v9. - Prior head `ce18f2bd52ed00ce750302f63aecc4167a3daff3` finished CI with 43 passed, one expected skip, four originating runner interruptions, and a failed dependent verify aggregate. Two logs show runner shutdown signals; two annotations show lost runner communication. No assertion failure was observed. Those outcomes are retained; no unchanged recovery was requested. - Required fresh current-head CI, genuine review, thread clearance, and Root's exact-diff acceptance are pending. No merge or new paid run is authorized by this PR's publication. - The earlier 3 PASS / 7 FAIL uniform attempt and all later original outcomes remain unchanged. No new native pass or whole-catalog readiness claim is made. ## Risks The source remains a frozen candidate with unmerged catalog repairs. A real measured run is still required. The readiness witness requires both a real handoff and the exact task URL; it does not extend the heading timeout, bypass auth, skip a case, or change production code. The active measurement on the previous pin is not altered. ## Model Used OpenAI GPT-6.1 (`gpt-6.1-sol`), with reasoning, code execution, and repository tools. The host did not expose a context-window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched for and linked related public PRs above - [x] I have linked public PRs and described the issue - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change - [x] Relevant local pin tests passed; source and fixture qualification scope is stated above - [x] I have updated the expected pin test and the observed fixture boundary - [x] Owning source metadata and this description document the change - [x] I have considered and documented the risks - [ ] All current-head infrastructure CI gates are green - [ ] Greptile is 5/5 with no unresolved material findings - [x] I will address reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
369 lines
14 KiB
JavaScript
369 lines
14 KiB
JavaScript
import test from 'node:test';
|
|
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';
|
|
|
|
const { parseDocument } = createRequire(import.meta.url)('yaml');
|
|
const bytes = readFileSync(new URL('./chat-full-catalog-testops-manifest.json', import.meta.url));
|
|
const manifest = JSON.parse(bytes);
|
|
const row = id => fullCatalogCases.find(value => value.key === id);
|
|
|
|
test('the manifest joins all244 source YAML scenarios without importing a domain or fabricating a result', () => {
|
|
const directory = new URL('./chat-scenarios/', import.meta.url);
|
|
const scenarios = readdirSync(directory).filter(file => file.endsWith('.yaml') && file !== 'defaults.yaml').flatMap(file => {
|
|
const parsed = parseDocument(readFileSync(new URL(file, directory), 'utf8'), { uniqueKeys: true });
|
|
assert.deepEqual(parsed.errors, []);
|
|
return parsed.toJS().scenarios;
|
|
});
|
|
assert.equal(scenarios.length, 244);
|
|
assert.equal(fullCatalogCases.length, 244);
|
|
assert.deepEqual(fullCatalogCases.map(value => value.key).sort(), scenarios.map(value => value.id).sort());
|
|
for (const scenario of scenarios) {
|
|
const actual = row(scenario.id);
|
|
const override = field => manifest.source.repairMetadata.changedFields.find(change => change.key === scenario.id && change.field === field);
|
|
for (const [field, original] of [['originalQuery', scenario.query], ['originalContext', scenario.context], ['expectedOutcome', scenario.expected_outcome]]) {
|
|
const change = override(field);
|
|
if (change) { assert.equal(change.previous, original); assert.equal(actual[field], change.current); }
|
|
else assert.equal(actual[field], original);
|
|
}
|
|
assert.ok(!Object.hasOwn(actual, 'status') && !Object.hasOwn(actual, 'passed'));
|
|
}
|
|
});
|
|
|
|
test('legacy aliases preserve existing TestOps identities while execution uses actual legacy task IDs', () => {
|
|
for (const task of ['create-backlog', 'delegate-announcement']) {
|
|
const actual = row(`tasks.${task}`);
|
|
assert.equal(actual.scenario, task);
|
|
assert.equal(actual.executionId, `chat-features.runner-opencode.local.${task}`);
|
|
assert.equal(actual.fullName, `paperclip.Product E2E.chat-features.tasks.${task}`);
|
|
assert.deepEqual(actual.aliases, [`paperclip.Product E2E.chat-features.${task}`]);
|
|
}
|
|
});
|
|
|
|
test('source bounds retain legacy and pilot runs, native zero turn policy and original finite attempts', () => {
|
|
for (const [id, minimum, maximum, attempt, checks] of [
|
|
['tasks.create-backlog', 1, 1, 420000, 10], ['tasks.delegate-announcement', 3, 3, 420000, 15],
|
|
['instance-operations.restart-service', 1, 1, 420000, 9], ['projects.project-defaults', 3, 3, 660000, 11],
|
|
['projects.project-work', 1, 3, 300000, 15],
|
|
]) {
|
|
const actual = row(id);
|
|
assert.deepEqual([actual.minimumRuns, actual.maximumRuns, actual.attemptTimeoutMs, actual.checks], [minimum, maximum, attempt, checks]);
|
|
assert.equal(actual.turnTimeoutMs, id.startsWith('tasks.') ? 120000 : 0);
|
|
}
|
|
assert.equal(row('access.sign-in').maximumRuns, 0);
|
|
assert.equal(row('assistant-connections.use-scoped-client').maximumRuns, 0);
|
|
assert.equal(fullCatalogCases.filter(value => value.turnTimeoutMs === 0).length, 242);
|
|
});
|
|
|
|
test('definition gaps and private prerequisites remain explicit instead of inferred runnable passes', () => {
|
|
assert.deepEqual(fullCatalogCases.filter(value => value.coverageGaps.length).map(value => value.key), [
|
|
'access.invite-teammate', 'access.manage-membership', 'activity.routine-audit', 'agents.restore-instructions', 'budgets-costs.subscription-fees',
|
|
]);
|
|
assert.equal(fullCatalogCases.filter(value => value.requiredCredentials.length).length, 18);
|
|
assert.equal(fullCatalogCases.filter(value => value.externalInput !== null).length, 37);
|
|
assert.equal(row('skills.enable-for-agent').coverageGaps.length, 0);
|
|
});
|
|
|
|
test('surface evidence declarations never manufacture a generic final screenshot for boundary cases', () => {
|
|
assert.equal(row('agent-chat.chat-disabled').screenshot, 'runtime-disabled-agent-chat.png');
|
|
for (const id of ['access.sign-in', 'onboarding.choose-first-runtime', 'onboarding.new-instance', 'onboarding.resume-wizard', 'assistant-connections.use-scoped-client']) {
|
|
assert.equal(row(id).screenshot, null);
|
|
assert.match(row(id).evidenceReadiness, /requires-final-state\.png/);
|
|
}
|
|
assert.equal(row('projects.project-work').screenshot, 'final-state.png');
|
|
assert.ok(manifest.source.requiredOriginalPassEvidence.includes('final-state.png'));
|
|
});
|
|
|
|
test('source identities and descriptor provenance are stable, deeply frozen and deterministic', () => {
|
|
assert.equal(catalogTargetSha, manifest.targetSha);
|
|
assert.equal(catalogManifestSha256, createHash('sha256').update(bytes).digest('hex'));
|
|
assert.equal(fullCatalogPlanName, 'Chat feature catalog — OpenCode 1017 (candidate)');
|
|
assert.equal(new Set(fullCatalogCases.map(value => value.fullName)).size, 244);
|
|
assert.equal(manifest.source.scenarioFiles.length, 35);
|
|
assert.equal(manifest.metadataSourceSha, 'de0941835cc8f0d01c982574ee258e110babdbeb');
|
|
assert.throws(() => { fullCatalogCases[0].coverageGaps.push('changed'); }, TypeError);
|
|
assert.throws(() => { fullCatalogCases[0].maximumRuns = 999; }, TypeError);
|
|
assert.ok(!bytes.toString().match(/sk-or-v1-[a-f0-9]{32,}/));
|
|
});
|
|
|
|
test('foreign identity, duplicated case and changed run-bound metadata are rejected by the pure loader', () => {
|
|
const copy = () => structuredClone(manifest);
|
|
const foreign = copy(); foreign.cases[0].executionId = 'chat-features.other.local.case';
|
|
assert.throws(() => validateFullCatalogManifest(foreign), /row_invalid/);
|
|
const duplicate = copy(); duplicate.cases[1] = duplicate.cases[0];
|
|
assert.throws(() => validateFullCatalogManifest(duplicate), /identity_conflict/);
|
|
const bounds = copy(); bounds.cases[0].minimumRuns = bounds.cases[0].maximumRuns + 1;
|
|
assert.throws(() => validateFullCatalogManifest(bounds), /row_invalid/);
|
|
});
|
|
|
|
|
|
test('repair source metadata binds the corrected candidate and explicit changed definitions', () => {
|
|
const repair = manifest.source.repairMetadata;
|
|
assert.equal(repair.baseTargetSha, '3b66c3c144360ddf32714a0742fd774b8ceafb16');
|
|
assert.equal(repair.previousTargetSha, 'ab2019e976076ec0eb60b026844f3d9211cb0208');
|
|
assert.equal(manifest.targetSha, '5aef4bffa2b106f2066bf2485338798f04a702bd');
|
|
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));
|
|
assert.equal(createHash('sha256').update(descriptorBytes).digest('hex'), repair.candidateDescriptorSha256);
|
|
const declarations = JSON.parse(descriptorBytes);
|
|
assert.equal(declarations.length, 242);
|
|
assert.equal(new Set(declarations.map(value => value.scenarioId)).size, 242);
|
|
assert.deepEqual(repair.changedFields.map(value => [value.key, value.field]), [
|
|
[
|
|
"activity.work-timeline",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"activity.work-timeline",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"activity.work-timeline",
|
|
"expectedOutcome"
|
|
],
|
|
[
|
|
"agents.choose-primary",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"app-permissions.ask-before-action",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"app-permissions.ask-before-action",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"app-permissions.test-as-agent",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"app-permissions.test-as-agent",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"app-permissions.test-as-agent",
|
|
"originalContext"
|
|
],
|
|
[
|
|
"app-permissions.test-as-agent",
|
|
"expectedOutcome"
|
|
],
|
|
[
|
|
"cli-operations.choose-cli-context",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"cli-operations.choose-cli-context",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"companies.agent-observability",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"companies.configure-decision-model",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"companies.fast-responses",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"companies.fast-responses",
|
|
"originalQuery"
|
|
],
|
|
[
|
|
"companies.prompt-injection-guard",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"goals.create-goal-tree",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"goals.create-goal-tree",
|
|
"expectedOutcome"
|
|
],
|
|
[
|
|
"navigation-preferences.dismiss-announcement",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"navigation-preferences.dismiss-announcement",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"questions-and-approvals.duplicate-answer",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"questions-and-approvals.formal-hire-revision",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"questions-and-approvals.formal-hire-revision",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"questions-and-approvals.multi-part-question",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"questions-and-approvals.reject-and-revise",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"questions-and-approvals.reject-and-revise",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"questions-and-approvals.review-plan",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"questions-and-approvals.review-plan",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"secrets.configure-company-secret",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"secrets.configure-company-secret",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"secrets.personal-secret",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"secrets.personal-secret",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"skills.author-skill",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.author-skill",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"skills.author-skill",
|
|
"expectedOutcome"
|
|
],
|
|
[
|
|
"skills.enable-for-agent",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.explicit-restriction",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.find-and-install",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.fork-read-only",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.review-update",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.studio-test",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"skills.studio-test",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"skills.studio-test",
|
|
"maximumRuns"
|
|
],
|
|
[
|
|
"teams.configure-team",
|
|
"sourceFiles"
|
|
],
|
|
[
|
|
"teams.configure-team",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"connection-setup.secure-api-key",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"secrets.least-scope-delivery",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"secrets.secret-proposal",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"secrets.vault-reference",
|
|
"oracleVersion"
|
|
],
|
|
[
|
|
"teams.install-team",
|
|
"oracleVersion"
|
|
]
|
|
]);
|
|
for (const change of repair.changedFields) {
|
|
assert.deepEqual(row(change.key)[change.field], change.current);
|
|
assert.notDeepEqual(change.current, change.previous);
|
|
}
|
|
assert.equal(repair.descriptorChanges.length, 49);
|
|
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']);
|
|
for (const change of repair.descriptorChanges) {
|
|
assert.notDeepEqual(change.current, change.previous);
|
|
const declaration = declarations.find(value => value.scenarioId === change.key);
|
|
assert.ok(declaration);
|
|
assert.deepEqual(change.current, declaration[change.field]);
|
|
if (Object.hasOwn(row(change.key), change.field)) assert.deepEqual(row(change.key)[change.field], change.current);
|
|
}
|
|
assert.deepEqual(repair.changedDefinitionFiles.map(value => value.path), [
|
|
'tests/runner-e2e/chat-scenarios/companies.yaml', 'tests/runner-e2e/chat-scenarios/goals.yaml', 'tests/runner-e2e/chat-scenarios/skills.yaml', 'tests/runner-e2e/chat-scenarios/app-permissions.yaml', 'tests/runner-e2e/chat-scenarios/activity.yaml',
|
|
]);
|
|
assert.ok(repair.changedDefinitionFiles.every(value => /^[a-f0-9]{64}$/.test(value.sha256)));
|
|
for (const declaration of declarations) {
|
|
const value = row(declaration.scenarioId);
|
|
assert.deepEqual(value.sourceFiles, declaration.sourceFiles);
|
|
assert.equal(value.oracleVersion, declaration.oracleVersion);
|
|
assert.equal(value.minimumRuns, declaration.minimumRunCount ?? declaration.expectedRunCount);
|
|
assert.equal(value.maximumRuns, declaration.expectedRunCount);
|
|
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.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);
|
|
assert.equal(repair.runtimeConditions.sdkPublicProof.sourceInputsChanged, true);
|
|
assert.equal(repair.runtimeConditions.sdkPublicProof.registrySha256, "efa70621e9fb42aa185f7ee616a1a726aa3eccb367e67e28817cedf26ef2ade8");
|
|
assert.equal(repair.runtimeConditions.sdkPublicProof.plugin558StillHeld, true);
|
|
assert.equal(repair.runtimeConditions.sdkPublicProof.expectedLockSha256, "54a81b2ed3f1f85eaa085c034560ac248e384db34fbc7aba8c5a3846c29b3ce8");
|
|
assert.equal(repair.runtimeConditions.sdkPublicProof.runtimeCompilerParserZodIdentitiesUnchanged, true);
|
|
assert.equal(repair.runtimeConditions.toolProposal487.corePRUnmerged, false);
|
|
assert.deepEqual(repair.runtimeConditions.toolProposal487.replay, {
|
|
"corePR": 18742,
|
|
"coreHead": "61996a96dd09e90a5788e8711a94fc5ae631f425",
|
|
"corePRUnmerged": false,
|
|
"condition": "A new Ask-first request only for an authenticated later same-person human intent after the prior decline; same-intent retry and derived or foreign sources remain denied. No approval/execution authority change.",
|
|
"acceptedDirectSources": [
|
|
"issue.comment",
|
|
"issue.comment.reopen"
|
|
],
|
|
"actualPostAssertionPGCasesPassed": 2
|
|
});
|
|
});
|