Align Pi qualification fixtures and attachment contracts with master

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-07 07:23:59 -05:00
1 parent 510e51b227
commit 5cc87a37fe
7 files changed
+48 -13

No files matched your search

@@ -5,6 +5,30 @@ set of release gates. Pi remains a candidate until every required gate passes.
Keep behavioral qualification on the accepted fixtures. Do not expand it to
widgets, images, Cursor or Copilot qualification. The existing draft stack must be reviewed in dependency order.
## Final fixture reconciliation — 2026-10-07
Fresh Linux CI passed the server, database, UI and package checks. It exposed
the remaining stale Pi profile-15 fixtures in the native backend, provisioning
suite and public-install probe. Those checks now bind profile 16. Thinking
changes are rejected while the unchanged identity can still attach successfully.
The Rust and TypeScript instruction composers now share master's current-turn
`AGENT_HOME` guidance; the shared positive and negative suffix fixtures remain
the attachment authority.
The corrected native backend passes 22 integration tests, the Rust core passes
350 unit tests, and the shared instruction/provisioning suites pass 13 tests.
Recursive typecheck and the full workspace build pass. Product E2E free checks
pass 2,479 Vitest tests and 128 native completion checks, and token gates pass.
The final complete Rust run and fresh Linux CI are still required. Local
PostgreSQL integration remains blocked by this host's shared-memory capacity;
the corresponding Linux CI lanes passed before this fixture-only update.
The dedicated qualification key's fresh read records $0.430881296 lifetime usage
and $4.569118704 remaining under its $5 cap, with no reset or BYOK usage. The
signed-in Default Workspace has all 72 BYOK providers unconfigured. These
account observations made zero model calls. Current-source installed admission,
the real task, and all 26 Product plus seven Runner cases remain unqualified.
## Master safety restoration — 2026-10-07
The earlier merge retained obsolete accounting code and removed master budget
@@ -204,7 +204,7 @@ fn registered_asset_suffix(context: &Value) -> Option<String> {
return None;
}
blocks.push(if copy.get("kind").and_then(Value::as_str) == Some("agent_files") {
format!("Your persistent agent directory (AGENT_HOME) is {path}. Your instruction entry is {entry}, relative to that directory. All supported files and subfolders there are restored across tasks and sessions, and collected after this provider stops. Write task deliverables in the task working directory. Only changed or deleted files synchronize; the last sync wins for the same file. Temporary copies are cleaned up without retaining file history. Check the save receipt before claiming persistence.")
format!("Your persistent agent directory (AGENT_HOME) is {path}. This is the current turn's copy; its absolute path may change between turns. In shell commands, use the current $AGENT_HOME environment variable instead of an absolute agent-directory path from an earlier turn. Your instruction entry is {entry}, relative to that directory. All supported files and subfolders there are restored across tasks and sessions, and collected after this provider stops. Write task deliverables in the task working directory. Only changed or deleted files synchronize; the last sync wins for the same file. Temporary copies are cleaned up without retaining file history. Check the save receipt before claiming persistence.")
} else {
format!("Your editable agent instruction file is {path}/{entry}. Edit this registered private copy normally. After this run stops, Paperclip saves changed content as a persistent revision if your responsible user still has permission and the baseline has not changed. Check the run's instruction-save receipt before claiming persistence. Conflicts are preserved for explicit resolution. Repository instruction files, skills, and this run's loaded prompt are separate and are not collected.")
});
@@ -50,7 +50,11 @@ fn config(mode: &str) -> AcpxProviderSessionConfig {
working_directory: std::env::temp_dir(),
permission_mode: AcpxPermissionMode::ApproveReads,
mode: None,
pi_thinking_level: None,
pi_thinking_level: if mode.starts_with("controls") {
Some(paperclip_runner_core::acpx_provider_session::PiThinkingLevel::Low)
} else {
None
},
permission_mode_pinned: true,
provider_policy: if mode.starts_with("controls") {
Some(
@@ -17,7 +17,7 @@ use sha2::{Digest, Sha256};
const CODEX_ACPX_DIGEST: &str =
"sha256:c4538599d1ab767db5dff50934f13bb5ba313a59d9c4a83e993fac4617ea63d3";
const PI_ACPX_DIGEST: &str =
"sha256:790f8b954be995ef63aef0ebdb0e06215e4c0d1416d40d605b33966a3d6ba053";
"sha256:28eefeccb2556d2668e20aee2f12a90d1fef50b9be954d5f6dd97c757e2e945e";
fn temporary_directory(label: &str) -> PathBuf {
let nonce = SystemTime::now()
@@ -1397,11 +1397,17 @@ fn pi_thinking_change_cannot_attach_to_an_existing_provider_identity() {
let error = executor
.execute(&command(3 + index as u64, "run.attach", changed))
.unwrap_err();
assert!(error
.to_string()
.contains("immutable_provider_identity_changed"));
assert!(
error
.to_string()
.contains("requires the same settled ACPX provider profile and session"),
"unexpected thinking identity rejection: {error}"
);
assert_eq!(fs::read(&state_path).unwrap(), prior);
}
executor
.execute(&command(6, "run.attach", payload))
.unwrap();
executor.shutdown().unwrap();
fs::remove_dir_all(directory).unwrap();
}
@@ -25,10 +25,11 @@ async function fixture(vendored = true) {
await mkdir(cli, { recursive: true });
await writeFile(join(root, "package.json"), JSON.stringify({ name: vendored ? "@paperclipai/server" : "@paperclipai/paperclip-runner" }));
const entry = join(cli, "provision-pi.cjs"); await writeFile(entry, "fixture");
return { root, cli, entry, parent: join(root, "provider-assets/pi"), output: join(root, "provider-assets/pi", `${process.platform}-${process.arch}`) };
const assetRoot = vendored ? join(root, "dist/vendor/paperclip-runner") : root;
return { root, cli, entry, assetRoot, parent: join(assetRoot, "provider-assets/pi"), output: join(assetRoot, "provider-assets/pi", `${process.platform}-${process.arch}`) };
}
it("recognizes both published layouts without resolving a private npm package", async () => {
for (const vendored of [true, false]) { const f = await fixture(vendored); expect((await provisionPackageRoot(f.entry)).root).toBe(f.root); }
for (const vendored of [true, false]) { const f = await fixture(vendored); expect(await provisionPackageRoot(f.entry)).toMatchObject({ root: f.root, assetRoot: f.assetRoot }); }
});
it("rejects wrong package, linked entrypoint and escaping asset roots before materialization", async () => {
const f = await fixture(); const other = await fixture();
@@ -37,7 +38,7 @@ it("rejects wrong package, linked entrypoint and escaping asset roots before mat
await writeFile(join(f.root, "package.json"), '{"name":"@paperclipai/server"}');
await rm(f.entry); await symlink(other.entry, f.entry);
await expect(provisionPi(f.entry)).rejects.toThrow("linked");
await rm(f.entry); await writeFile(f.entry, "fixture"); await symlink(other.root, join(f.root, "provider-assets"));
await rm(f.entry); await writeFile(f.entry, "fixture"); await symlink(other.root, join(f.assetRoot, "provider-assets"));
await expect(provisionPi(f.entry)).rejects.toThrow("escapes");
expect(mocks.build).not.toHaveBeenCalled();
});
@@ -19,7 +19,7 @@
}
},
"entry": "Custom entry quoting /registered/run-copy stays unchanged.",
"instructions": "Pinned prompt.\n\nCustom entry quoting /registered/run-copy stays unchanged.\n\nPinned connection policy.\n\nYour persistent agent directory (AGENT_HOME) is /registered/run-copy. Your instruction entry is AGENTS.md, relative to that directory. All supported files and subfolders there are restored across tasks and sessions, and collected after this provider stops. Write task deliverables in the task working directory. Only changed or deleted files synchronize; the last sync wins for the same file. Temporary copies are cleaned up without retaining file history. Check the save receipt before claiming persistence.\n\nRead-only instruction sibling root: /registered/bundle"
"instructions": "Pinned prompt.\n\nCustom entry quoting /registered/run-copy stays unchanged.\n\nPinned connection policy.\n\nYour persistent agent directory (AGENT_HOME) is /registered/run-copy. This is the current turn's copy; its absolute path may change between turns. In shell commands, use the current $AGENT_HOME environment variable instead of an absolute agent-directory path from an earlier turn. Your instruction entry is AGENTS.md, relative to that directory. All supported files and subfolders there are restored across tasks and sessions, and collected after this provider stops. Write task deliverables in the task working directory. Only changed or deleted files synchronize; the last sync wins for the same file. Temporary copies are cleaned up without retaining file history. Check the save receipt before claiming persistence.\n\nRead-only instruction sibling root: /registered/bundle"
},
{
"context": {
+3 -3
View File
@@ -20,8 +20,8 @@ assert.equal(JSON.parse(await readFile(join(server, 'package.json'), 'utf8')).na
const { resolvePaperclipRunnerBinary } = await import(pathToFileURL(join(server, 'dist/services/native-runtime/native-codex-runner.js')));
const { createCapabilityRunnerdCodexTransport } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/live/runnerd-codex-transport.js')));
const { QUALIFIED_ACPX_PROFILES } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/drivers/acpx/qualified-profiles.js')));
assert.equal(QUALIFIED_ACPX_PROFILES.pi.agentProfileVersion, 15);
assert.equal(QUALIFIED_ACPX_PROFILES.pi.commandDigest, 'sha256:790f8b954be995ef63aef0ebdb0e06215e4c0d1416d40d605b33966a3d6ba053');
assert.equal(QUALIFIED_ACPX_PROFILES.pi.agentProfileVersion, 16);
assert.equal(QUALIFIED_ACPX_PROFILES.pi.commandDigest, 'sha256:28eefeccb2556d2668e20aee2f12a90d1fef50b9be954d5f6dd97c757e2e945e');
assert.equal(QUALIFIED_ACPX_PROFILES.pi.qualificationModel, 'openrouter/deepseek/deepseek-v4-flash-0731');
const daemon = resolvePaperclipRunnerBinary();
assert.equal(await realpath(daemon), join(server, 'dist/vendor/paperclip-runner/bin/paperclip-runnerd'));
@@ -63,7 +63,7 @@ try {
assert.equal(state.activeTurnId, null);
assert.equal(state.identity, null);
assert.equal(state.providerExitUnconfirmed, false);
console.log(JSON.stringify({ schema: 'paperclip.pi.public-npm-install.v1', target: `${process.platform}-${process.arch}`, normalPackagedDaemon: true, exactPiProfile: 15, runtime: '1.0.0', credentials: 'none', promptCalls: 0, settledMs, cleanRunnerExit: true }));
console.log(JSON.stringify({ schema: 'paperclip.pi.public-npm-install.v1', target: `${process.platform}-${process.arch}`, normalPackagedDaemon: true, exactPiProfile: 16, runtime: '1.0.0', credentials: 'none', promptCalls: 0, settledMs, cleanRunnerExit: true }));
} finally {
try { await bundle?.transport.close(); }
finally { clearTimeout(watchdog); await rm(root, { recursive: true, force: true }); }