pluribus-context 0.3.35 → 0.3.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +3 -2
- package/bin/pluribus.js +12 -0
- package/docs/agent-firewall-denial-audit.md +95 -0
- package/docs/ai-pr-review-receipts.md +20 -0
- package/docs/community-review-packet.md +19 -0
- package/docs/compaction-resume-receipts.md +43 -0
- package/docs/context-budget-receipts.md +1 -1
- package/docs/controlled-learning-queue.md +48 -0
- package/docs/install-plan-receipts.md +2 -0
- package/docs/loaded-resource-boundary.md +97 -0
- package/docs/mcp-runtime-config-receipts.md +91 -0
- package/docs/memory-write-policy-receipts.md +41 -0
- package/docs/parallel-session-review-ledger.md +103 -0
- package/docs/phase-boundary-contracts.md +87 -0
- package/docs/review-primitive-gate.md +2 -0
- package/docs/skill-install-receipts.md +102 -0
- package/docs/skill-policy-receipts.md +1 -1
- package/docs/skill-use-rate-receipts.md +104 -0
- package/examples/agent-firewall-denial-audit/README.md +14 -0
- package/examples/agent-firewall-denial-audit/check-denial-audit.mjs +116 -0
- package/examples/agent-firewall-denial-audit/denial-envelope.json +9 -0
- package/examples/agent-firewall-denial-audit/operator-audit-record.json +20 -0
- package/examples/agent-skills/README.md +10 -0
- package/examples/ai-pr-review-receipts/.github/workflows/ai-pr-review-receipt.yml +25 -0
- package/examples/ai-pr-review-receipts/README.md +51 -1
- package/examples/ai-pr-review-receipts/incomplete-review-primitive-receipt.json +43 -0
- package/examples/ai-pr-review-receipts/review-primitive-receipt.json +60 -0
- package/examples/compaction-resume-receipts/README.md +12 -0
- package/examples/compaction-resume-receipts/check-resume-receipt.mjs +116 -0
- package/examples/compaction-resume-receipts/safe-resume-receipt.json +52 -0
- package/examples/compaction-resume-receipts/unsafe-resume-receipt.json +41 -0
- package/examples/controlled-learning-queue/README.md +26 -0
- package/examples/controlled-learning-queue/check-learning-queue.mjs +44 -0
- package/examples/controlled-learning-queue/leads/acme-job-card.md +12 -0
- package/examples/controlled-learning-queue/learning_queue.md +27 -0
- package/examples/controlled-learning-queue/memory/durable.md +10 -0
- package/examples/controlled-learning-queue/memory/working-notes.md +5 -0
- package/examples/controlled-learning-queue/role/job-contract.md +18 -0
- package/examples/controlled-learning-queue/skills/qualify-lead.md +17 -0
- package/examples/loaded-resource-boundary/README.md +22 -0
- package/examples/loaded-resource-boundary/check-loaded-resource-boundary.mjs +65 -0
- package/examples/loaded-resource-boundary/loaded-resource-boundary.json +69 -0
- package/examples/mcp-runtime-config-receipts/README.md +15 -0
- package/examples/mcp-runtime-config-receipts/check-mcp-runtime-config-receipt.mjs +127 -0
- package/examples/mcp-runtime-config-receipts/mcp-runtime-config-receipt.json +82 -0
- package/examples/memory-write-policy/README.md +28 -0
- package/examples/memory-write-policy/approved-memory-update.json +48 -0
- package/examples/memory-write-policy/check-memory-update.mjs +120 -0
- package/examples/memory-write-policy/quarantined-memory-update.json +43 -0
- package/examples/parallel-session-review-ledger/README.md +13 -0
- package/examples/parallel-session-review-ledger/check-parallel-session-review-ledger.mjs +69 -0
- package/examples/parallel-session-review-ledger/parallel-session-review-ledger.json +72 -0
- package/examples/phase-boundary-contract/README.md +23 -0
- package/examples/phase-boundary-contract/check-phase-boundary.mjs +73 -0
- package/examples/phase-boundary-contract/phase-boundary-contract.json +68 -0
- package/examples/skill-install-receipts/README.md +31 -0
- package/examples/skill-install-receipts/check-skill-install-receipt.mjs +75 -0
- package/examples/skill-install-receipts/skill-install-receipt.json +79 -0
- package/examples/skill-use-rate-receipts/README.md +16 -0
- package/examples/skill-use-rate-receipts/check-skill-use-rate.mjs +89 -0
- package/examples/skill-use-rate-receipts/skill-use-rate-receipt.json +79 -0
- package/package.json +2 -1
- package/{examples/agent-skills → skills}/context-receipts/README.md +4 -4
- package/skills/context-receipts/SKILL.md +206 -0
- package/{examples/agent-skills → skills}/skill-policy-receipts/README.md +1 -1
- package/skills/skill-policy-receipts/SKILL.md +77 -0
- package/src/commands/demo.js +155 -0
- package/src/index.js +1 -0
- package/src/utils/version.js +1 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "pluribus.parallel_session_review_ledger.v1",
|
|
3
|
+
"generated_at": "2026-06-04T19:00:00Z",
|
|
4
|
+
"run": {
|
|
5
|
+
"orchestrator": "human",
|
|
6
|
+
"repo": "redacted-service",
|
|
7
|
+
"coordination_mode": "parallel_sessions"
|
|
8
|
+
},
|
|
9
|
+
"sessions": [
|
|
10
|
+
{
|
|
11
|
+
"id": "session-a",
|
|
12
|
+
"agent": "claude-code",
|
|
13
|
+
"assignment": "update validation for billing webhook retries",
|
|
14
|
+
"branch": "agent/billing-webhook-retry-validation",
|
|
15
|
+
"allowed_scope": {
|
|
16
|
+
"files": ["src/billing/**", "test/billing/**"],
|
|
17
|
+
"commands": ["npm test -- --test-name-pattern=billing"],
|
|
18
|
+
"network": "none"
|
|
19
|
+
},
|
|
20
|
+
"touched_files": ["src/billing/retries.js", "test/billing/retries.test.js"],
|
|
21
|
+
"agent_claim": "added retry validation and regression coverage",
|
|
22
|
+
"evidence": [
|
|
23
|
+
{ "type": "commit", "ref": "abc1234" },
|
|
24
|
+
{ "type": "test", "name": "billing retry validation", "status": "passed" }
|
|
25
|
+
],
|
|
26
|
+
"missing_checks": [],
|
|
27
|
+
"privacy_flags": [],
|
|
28
|
+
"state": "complete",
|
|
29
|
+
"safe_next_action": "review_diff"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "session-b",
|
|
33
|
+
"agent": "cursor-agent",
|
|
34
|
+
"assignment": "draft migration notes for webhook retry config",
|
|
35
|
+
"branch": "agent/retry-config-notes",
|
|
36
|
+
"allowed_scope": {
|
|
37
|
+
"files": ["docs/**"],
|
|
38
|
+
"commands": ["npm test -- --test-name-pattern=docs"],
|
|
39
|
+
"network": "none"
|
|
40
|
+
},
|
|
41
|
+
"touched_files": ["docs/webhook-retry-config.md"],
|
|
42
|
+
"agent_claim": "documented retry config and rollback notes",
|
|
43
|
+
"evidence": [
|
|
44
|
+
{ "type": "diff", "ref": "docs-only" }
|
|
45
|
+
],
|
|
46
|
+
"missing_checks": ["link-check docs/webhook-retry-config.md"],
|
|
47
|
+
"privacy_flags": [],
|
|
48
|
+
"state": "partial",
|
|
49
|
+
"safe_next_action": "run_missing_check"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "session-c",
|
|
53
|
+
"agent": "codex-cli",
|
|
54
|
+
"assignment": "investigate retry metrics regression",
|
|
55
|
+
"branch": "agent/retry-metrics-investigation",
|
|
56
|
+
"allowed_scope": {
|
|
57
|
+
"files": ["src/metrics/**", "test/metrics/**"],
|
|
58
|
+
"commands": ["npm test -- --test-name-pattern=metrics"],
|
|
59
|
+
"network": "none"
|
|
60
|
+
},
|
|
61
|
+
"touched_files": ["src/metrics/retry-metrics.js"],
|
|
62
|
+
"agent_claim": "found possible metrics label mismatch, not fixed",
|
|
63
|
+
"evidence": [
|
|
64
|
+
{ "type": "note", "ref": "suspected-label-mismatch" }
|
|
65
|
+
],
|
|
66
|
+
"missing_checks": ["confirm label contract with owner"],
|
|
67
|
+
"privacy_flags": ["scope_expanded_without_approval"],
|
|
68
|
+
"state": "unsafe_to_resume",
|
|
69
|
+
"safe_next_action": "stop_manual_review"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Phase-boundary contract example
|
|
2
|
+
|
|
3
|
+
This example is for multi-model coding workflows where one phase plans, another phase applies, and another verifies. It records the exact handoff boundary without storing prompts, transcripts, raw source, secrets, or full command output.
|
|
4
|
+
|
|
5
|
+
Run:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
node check-phase-boundary.mjs phase-boundary-contract.json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Expected output:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
phase-boundary contract ok: checkout-refactor-2026-06-03 apply->verify
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use the shape as a small gate before moving from Apply to Verify:
|
|
18
|
+
|
|
19
|
+
- approved plan/task refs and hashes entered the phase;
|
|
20
|
+
- output artifact hash exists;
|
|
21
|
+
- changed file-set hash and tests are present;
|
|
22
|
+
- open risks are explicit;
|
|
23
|
+
- stale exploration transcript or rejected designs are intentionally dropped.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const file = process.argv[2] || 'phase-boundary-contract.json';
|
|
5
|
+
const contract = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
6
|
+
const errors = [];
|
|
7
|
+
|
|
8
|
+
const phases = ['explore', 'propose', 'spec', 'design', 'tasks', 'apply', 'verify'];
|
|
9
|
+
const hashRe = /^sha256:[a-f0-9]{64}$/;
|
|
10
|
+
const unsafeRefRe = /(^\/|\.\.|[A-Za-z]:\\|secret|token|password|private_key)/i;
|
|
11
|
+
const requiredGateKeys = ['changed_files', 'tests_run', 'open_risks', 'stop_conditions'];
|
|
12
|
+
|
|
13
|
+
function expect(condition, message) {
|
|
14
|
+
if (!condition) errors.push(message);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
expect(contract.schema === 'pluribus.phase-boundary-contract.v1', 'schema must be pluribus.phase-boundary-contract.v1');
|
|
18
|
+
expect(typeof contract.workflowId === 'string' && contract.workflowId.length >= 6, 'workflowId is required');
|
|
19
|
+
expect(phases.includes(contract.currentPhase), 'currentPhase must be a known phase');
|
|
20
|
+
expect(phases.includes(contract.nextPhase), 'nextPhase must be a known phase');
|
|
21
|
+
expect(contract.currentPhase !== contract.nextPhase, 'currentPhase and nextPhase must differ');
|
|
22
|
+
|
|
23
|
+
expect(Array.isArray(contract.allowedInput) && contract.allowedInput.length > 0, 'allowedInput must list at least one source');
|
|
24
|
+
for (const [index, input] of (contract.allowedInput || []).entries()) {
|
|
25
|
+
expect(typeof input.kind === 'string' && input.kind.length > 0, `allowedInput[${index}].kind is required`);
|
|
26
|
+
expect(typeof input.ref === 'string' && !unsafeRefRe.test(input.ref), `allowedInput[${index}].ref must be a non-secret relative/logical ref`);
|
|
27
|
+
expect(hashRe.test(input.contentHash || ''), `allowedInput[${index}].contentHash must be sha256:<64 hex>`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
expect(contract.outputArtifact && typeof contract.outputArtifact.kind === 'string', 'outputArtifact.kind is required');
|
|
31
|
+
expect(contract.outputArtifact && typeof contract.outputArtifact.ref === 'string' && !unsafeRefRe.test(contract.outputArtifact.ref), 'outputArtifact.ref must be a non-secret logical ref');
|
|
32
|
+
expect(contract.outputArtifact && hashRe.test(contract.outputArtifact.contentHash || ''), 'outputArtifact.contentHash must be sha256:<64 hex>');
|
|
33
|
+
|
|
34
|
+
const gate = contract.evidenceGate || {};
|
|
35
|
+
expect(gate.status === 'pass' || gate.status === 'needs_review' || gate.status === 'fail', 'evidenceGate.status must be pass, needs_review, or fail');
|
|
36
|
+
expect(Array.isArray(gate.requiredBeforeNextPhase), 'evidenceGate.requiredBeforeNextPhase must be an array');
|
|
37
|
+
for (const key of requiredGateKeys) {
|
|
38
|
+
expect((gate.requiredBeforeNextPhase || []).includes(key), `evidenceGate.requiredBeforeNextPhase must include ${key}`);
|
|
39
|
+
}
|
|
40
|
+
expect(gate.changedFiles && Number.isInteger(gate.changedFiles.count) && gate.changedFiles.count >= 0, 'evidenceGate.changedFiles.count is required');
|
|
41
|
+
expect(gate.changedFiles && hashRe.test(gate.changedFiles.fileSetHash || ''), 'evidenceGate.changedFiles.fileSetHash must be sha256:<64 hex>');
|
|
42
|
+
expect(Array.isArray(gate.testsRun), 'evidenceGate.testsRun must be an array');
|
|
43
|
+
for (const [index, test] of (gate.testsRun || []).entries()) {
|
|
44
|
+
expect(typeof test.name === 'string' && test.name.length > 0, `testsRun[${index}].name is required`);
|
|
45
|
+
expect(hashRe.test(test.commandHash || ''), `testsRun[${index}].commandHash must be sha256:<64 hex>`);
|
|
46
|
+
expect(['pass', 'fail', 'skipped'].includes(test.status), `testsRun[${index}].status must be pass/fail/skipped`);
|
|
47
|
+
}
|
|
48
|
+
expect(Array.isArray(gate.openRisks), 'evidenceGate.openRisks must be an array');
|
|
49
|
+
for (const [index, risk] of (gate.openRisks || []).entries()) {
|
|
50
|
+
expect(typeof risk.riskClass === 'string' && risk.riskClass.length > 0, `openRisks[${index}].riskClass is required`);
|
|
51
|
+
expect(['low', 'medium', 'high'].includes(risk.severity), `openRisks[${index}].severity must be low/medium/high`);
|
|
52
|
+
expect(typeof risk.safeToContinue === 'boolean', `openRisks[${index}].safeToContinue must be boolean`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
expect(Array.isArray(contract.droppedContext), 'droppedContext must be an array');
|
|
56
|
+
for (const [index, dropped] of (contract.droppedContext || []).entries()) {
|
|
57
|
+
expect(typeof dropped.kind === 'string' && dropped.kind.length > 0, `droppedContext[${index}].kind is required`);
|
|
58
|
+
expect(typeof dropped.reason === 'string' && dropped.reason.length > 0, `droppedContext[${index}].reason is required`);
|
|
59
|
+
}
|
|
60
|
+
expect(Array.isArray(contract.stopConditions), 'stopConditions must be an array');
|
|
61
|
+
|
|
62
|
+
if (gate.status === 'pass') {
|
|
63
|
+
expect((contract.stopConditions || []).length === 0, 'pass contracts must not have active stopConditions');
|
|
64
|
+
expect((gate.openRisks || []).every((risk) => risk.safeToContinue), 'pass contracts require all open risks to be safeToContinue');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (errors.length) {
|
|
68
|
+
console.error(`phase-boundary contract failed (${errors.length}):`);
|
|
69
|
+
for (const error of errors) console.error(`- ${error}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(`phase-boundary contract ok: ${contract.workflowId} ${contract.currentPhase}->${contract.nextPhase}`);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "pluribus.phase-boundary-contract.v1",
|
|
3
|
+
"workflowId": "checkout-refactor-2026-06-03",
|
|
4
|
+
"currentPhase": "apply",
|
|
5
|
+
"nextPhase": "verify",
|
|
6
|
+
"allowedInput": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "approved_plan",
|
|
9
|
+
"ref": "plans/checkout-refactor.md",
|
|
10
|
+
"contentHash": "sha256:0b70d75d6c8f0e54c74e1d7ea90e7c7b3d83e15dd3a3f7b4b9e9e73339d2b22e",
|
|
11
|
+
"required": true
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "task_list",
|
|
15
|
+
"ref": "tasks/checkout-refactor.md#apply",
|
|
16
|
+
"contentHash": "sha256:63ccf4555f0adbe64bbff5a8f7c4b24708fdb2b5e4d0e417a5cb057b9ed39a76",
|
|
17
|
+
"required": true
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"outputArtifact": {
|
|
21
|
+
"kind": "patch",
|
|
22
|
+
"ref": "git:working-tree",
|
|
23
|
+
"contentHash": "sha256:3f9d6216dc7dcb53e6f29ad23433de97f0c172be6a6f46d574aa67e0edfd0789"
|
|
24
|
+
},
|
|
25
|
+
"evidenceGate": {
|
|
26
|
+
"requiredBeforeNextPhase": [
|
|
27
|
+
"changed_files",
|
|
28
|
+
"tests_run",
|
|
29
|
+
"open_risks",
|
|
30
|
+
"stop_conditions"
|
|
31
|
+
],
|
|
32
|
+
"status": "pass",
|
|
33
|
+
"changedFiles": {
|
|
34
|
+
"count": 4,
|
|
35
|
+
"fileSetHash": "sha256:5f6c8b62349d527cd2e24c6913b8c5f0af8775be1a836bf942853c39efc11f91"
|
|
36
|
+
},
|
|
37
|
+
"testsRun": [
|
|
38
|
+
{
|
|
39
|
+
"name": "unit-tests",
|
|
40
|
+
"commandHash": "sha256:1f2cc9d3f4ce8cb118198c7da4d6951de2f21485af126eb1a0fe5f1d7e0139de",
|
|
41
|
+
"status": "pass"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "typecheck",
|
|
45
|
+
"commandHash": "sha256:54d29f16fd215f74b4c03da61e37e71918f2e5c8db454a76f5f582b76471cb13",
|
|
46
|
+
"status": "pass"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"openRisks": [
|
|
50
|
+
{
|
|
51
|
+
"riskClass": "manual_browser_flow_not_verified",
|
|
52
|
+
"severity": "medium",
|
|
53
|
+
"safeToContinue": true
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"droppedContext": [
|
|
58
|
+
{
|
|
59
|
+
"kind": "exploration_transcript",
|
|
60
|
+
"reason": "not authoritative after approved plan"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"kind": "rejected_design_option",
|
|
64
|
+
"reason": "kept as citation only; not input to verify phase"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"stopConditions": []
|
|
68
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Skill install/load receipt example
|
|
2
|
+
|
|
3
|
+
This example is for setup tools that install Skills across multiple agents and then need to prove whether each target can discover/load the installed resource before the first real session.
|
|
4
|
+
|
|
5
|
+
It complements:
|
|
6
|
+
|
|
7
|
+
- `examples/install-plan-receipts/` — pre-write plan proof (`writes_started=false`);
|
|
8
|
+
- `examples/loaded-resource-boundary/` — runtime proof that an existing resource crossed discovery/attachment/injection/readability stages.
|
|
9
|
+
|
|
10
|
+
## Smoke test
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
node examples/skill-install-receipts/check-skill-install-receipt.mjs \
|
|
14
|
+
examples/skill-install-receipts/skill-install-receipt.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Expected output:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
skill install receipt ok: 3 targets checked
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Review checklist
|
|
24
|
+
|
|
25
|
+
A useful receipt should answer:
|
|
26
|
+
|
|
27
|
+
- What installer/source ref was used, without credentials?
|
|
28
|
+
- Which agent targets and scopes were touched?
|
|
29
|
+
- Which targets were installed, skipped, discovered, deferred, injected, or readable?
|
|
30
|
+
- Was any required target unsafe before the first session?
|
|
31
|
+
- Did the receipt avoid raw skill bodies, prompts, transcripts, env dumps, secrets, and private paths?
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const file = process.argv[2] || new URL('./skill-install-receipt.json', import.meta.url);
|
|
5
|
+
const receipt = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
6
|
+
|
|
7
|
+
const allowedInstall = new Set(['installed', 'skipped', 'failed']);
|
|
8
|
+
const allowedDiscovery = new Set(['discovered', 'not_discovered', 'not_tested', 'failed']);
|
|
9
|
+
const allowedLoad = new Set(['injected', 'readable', 'activation_required', 'deferred', 'not_tested', 'failed']);
|
|
10
|
+
const allowedCost = new Set(['0-1k', '1k-5k', '5k-20k', 'over_budget', 'unknown']);
|
|
11
|
+
const requiredPrivacy = [
|
|
12
|
+
'raw_skill_body',
|
|
13
|
+
'raw_prompt',
|
|
14
|
+
'transcript',
|
|
15
|
+
'secrets',
|
|
16
|
+
'env_dump',
|
|
17
|
+
'private_absolute_path'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function assert(condition, message) {
|
|
21
|
+
if (!condition) {
|
|
22
|
+
throw new Error(message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
assert(receipt.receipt_type === 'agent.skill_install_receipt.v1', 'unexpected receipt_type');
|
|
27
|
+
assert(receipt.run_id, 'missing run_id');
|
|
28
|
+
assert(receipt.installer?.name, 'missing installer.name');
|
|
29
|
+
assert(receipt.installer?.source?.kind, 'missing installer.source.kind');
|
|
30
|
+
assert(receipt.installer?.source?.ref, 'missing installer.source.ref');
|
|
31
|
+
assert(receipt.installer.source.credentials_in_ref === false, 'source ref must not carry credentials');
|
|
32
|
+
assert(Array.isArray(receipt.targets) && receipt.targets.length > 0, 'targets must be a non-empty array');
|
|
33
|
+
assert(Array.isArray(receipt.privacy_exclusions), 'missing privacy_exclusions');
|
|
34
|
+
|
|
35
|
+
for (const item of requiredPrivacy) {
|
|
36
|
+
assert(receipt.privacy_exclusions.includes(item), `privacy_exclusions must include ${item}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let computedOverallSafe = true;
|
|
40
|
+
for (const [index, target] of receipt.targets.entries()) {
|
|
41
|
+
assert(target.agent, `targets[${index}].agent missing`);
|
|
42
|
+
assert(['project', 'global', 'workspace', 'unknown'].includes(target.scope), `targets[${index}].scope invalid`);
|
|
43
|
+
assert(typeof target.required === 'boolean', `targets[${index}].required must be boolean`);
|
|
44
|
+
assert(allowedInstall.has(target.install_status), `targets[${index}].install_status invalid`);
|
|
45
|
+
assert(allowedDiscovery.has(target.discovery_status), `targets[${index}].discovery_status invalid`);
|
|
46
|
+
assert(allowedLoad.has(target.load_status), `targets[${index}].load_status invalid`);
|
|
47
|
+
assert(allowedCost.has(target.context_cost_bucket), `targets[${index}].context_cost_bucket invalid`);
|
|
48
|
+
assert(typeof target.safe_to_start_session === 'boolean', `targets[${index}].safe_to_start_session must be boolean`);
|
|
49
|
+
|
|
50
|
+
if (target.evidence) {
|
|
51
|
+
assert(target.evidence.raw_body_logged === false, `targets[${index}] must not log raw skill body`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const requiredTargetUnsafe = target.required && (
|
|
55
|
+
target.install_status !== 'installed' ||
|
|
56
|
+
target.discovery_status !== 'discovered' ||
|
|
57
|
+
target.load_status === 'failed' ||
|
|
58
|
+
target.load_status === 'not_tested' ||
|
|
59
|
+
target.context_cost_bucket === 'over_budget' ||
|
|
60
|
+
target.safe_to_start_session !== true
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (requiredTargetUnsafe) {
|
|
64
|
+
computedOverallSafe = false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
assert(receipt.overall_safe_to_start_session === computedOverallSafe, 'overall_safe_to_start_session does not match required target safety');
|
|
69
|
+
|
|
70
|
+
const serialized = JSON.stringify(receipt).toLowerCase();
|
|
71
|
+
for (const forbidden of ['private_key', 'api_key=', 'ghp_', 'github_pat_', 'npm_', 'bearer ']) {
|
|
72
|
+
assert(!serialized.includes(forbidden), `receipt appears to include secret marker: ${forbidden}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`skill install receipt ok: ${receipt.targets.length} targets checked`);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"receipt_type": "agent.skill_install_receipt.v1",
|
|
3
|
+
"run_id": "skill-install-demo-2026-06-04T13:00Z",
|
|
4
|
+
"installer": {
|
|
5
|
+
"name": "skills-cli",
|
|
6
|
+
"version_bucket": "0.x",
|
|
7
|
+
"command_class": "skill_package_install",
|
|
8
|
+
"source": {
|
|
9
|
+
"kind": "git_ref",
|
|
10
|
+
"package": "vercel-labs/skills/context-budget-preflight",
|
|
11
|
+
"ref": "sha256:demo-source-package-hash",
|
|
12
|
+
"credentials_in_ref": false
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"mode_requested": "install_then_check",
|
|
16
|
+
"mode_effective": "post_install_check",
|
|
17
|
+
"writes_completed": true,
|
|
18
|
+
"backup_created": true,
|
|
19
|
+
"targets": [
|
|
20
|
+
{
|
|
21
|
+
"agent": "claude-code",
|
|
22
|
+
"scope": "project",
|
|
23
|
+
"required": true,
|
|
24
|
+
"install_status": "installed",
|
|
25
|
+
"discovery_status": "discovered",
|
|
26
|
+
"load_status": "activation_required",
|
|
27
|
+
"activation": "on_demand_skill_description",
|
|
28
|
+
"context_cost_bucket": "0-1k",
|
|
29
|
+
"safe_to_start_session": true,
|
|
30
|
+
"evidence": {
|
|
31
|
+
"manifest_hash": "sha256:demo-claude-manifest-hash",
|
|
32
|
+
"skill_body_hash": "sha256:demo-claude-skill-body-hash",
|
|
33
|
+
"raw_body_logged": false
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"agent": "codex",
|
|
38
|
+
"scope": "project",
|
|
39
|
+
"required": true,
|
|
40
|
+
"install_status": "installed",
|
|
41
|
+
"discovery_status": "discovered",
|
|
42
|
+
"load_status": "deferred",
|
|
43
|
+
"activation": "manual_or_triggered",
|
|
44
|
+
"context_cost_bucket": "0-1k",
|
|
45
|
+
"safe_to_start_session": true,
|
|
46
|
+
"evidence": {
|
|
47
|
+
"manifest_hash": "sha256:demo-codex-manifest-hash",
|
|
48
|
+
"skill_body_hash": "sha256:demo-codex-skill-body-hash",
|
|
49
|
+
"raw_body_logged": false
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"agent": "zed-acp",
|
|
54
|
+
"scope": "project",
|
|
55
|
+
"required": false,
|
|
56
|
+
"install_status": "skipped",
|
|
57
|
+
"discovery_status": "not_tested",
|
|
58
|
+
"load_status": "not_tested",
|
|
59
|
+
"activation": "unsupported_target_in_this_installer",
|
|
60
|
+
"context_cost_bucket": "unknown",
|
|
61
|
+
"safe_to_start_session": true,
|
|
62
|
+
"skipped_reason": "target_not_selected"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"overall_safe_to_start_session": true,
|
|
66
|
+
"next_safe_command": "start a disposable agent session and ask it to cite the installed Skill sentinel line",
|
|
67
|
+
"privacy_exclusions": [
|
|
68
|
+
"raw_skill_body",
|
|
69
|
+
"raw_source",
|
|
70
|
+
"raw_prompt",
|
|
71
|
+
"transcript",
|
|
72
|
+
"raw_tool_output",
|
|
73
|
+
"customer_data",
|
|
74
|
+
"secrets",
|
|
75
|
+
"env_dump",
|
|
76
|
+
"private_absolute_path"
|
|
77
|
+
],
|
|
78
|
+
"audit_gap": "proves installer/discovery/load boundary, not skill semantic quality or future activation"
|
|
79
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Skill use-rate receipts
|
|
2
|
+
|
|
3
|
+
This example shows a privacy-safe receipt for the gap between installing an Agent Skill and actually using it.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
node check-skill-use-rate.mjs skill-use-rate-receipt.json
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Expected output:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
skill use-rate receipt ok: 3 skills checked, 1 unused install warning
|
|
13
|
+
- rust-lsp-helper is installed/attached but has 0 invocations in this window
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The warning is intentional: installation is not adoption. A skill can be installed, attached, and discoverable while still adding context surface area with no observed invocation.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
const receiptPath = process.argv[2] || path.join(import.meta.dirname, 'skill-use-rate-receipt.json')
|
|
6
|
+
const receipt = JSON.parse(fs.readFileSync(receiptPath, 'utf8'))
|
|
7
|
+
const errors = []
|
|
8
|
+
const warnings = []
|
|
9
|
+
|
|
10
|
+
function requireString(value, field) {
|
|
11
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
12
|
+
errors.push(`${field} must be a non-empty string`)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function requireBoolean(value, field) {
|
|
17
|
+
if (typeof value !== 'boolean') {
|
|
18
|
+
errors.push(`${field} must be boolean`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function requireNonNegativeInteger(value, field) {
|
|
23
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
24
|
+
errors.push(`${field} must be a non-negative integer`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (receipt.schema !== 'pluribus.skill_use_rate_receipt.v1') {
|
|
29
|
+
errors.push('schema must be pluribus.skill_use_rate_receipt.v1')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
requireString(receipt.run_id, 'run_id')
|
|
33
|
+
requireString(receipt.generated_at, 'generated_at')
|
|
34
|
+
requireString(receipt.installer?.name, 'installer.name')
|
|
35
|
+
requireString(receipt.window?.started_at, 'window.started_at')
|
|
36
|
+
requireString(receipt.window?.ended_at, 'window.ended_at')
|
|
37
|
+
|
|
38
|
+
if (!Array.isArray(receipt.skills) || receipt.skills.length === 0) {
|
|
39
|
+
errors.push('skills must be a non-empty array')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const [index, skill] of (receipt.skills || []).entries()) {
|
|
43
|
+
const prefix = `skills[${index}]`
|
|
44
|
+
requireString(skill.skill_id, `${prefix}.skill_id`)
|
|
45
|
+
requireString(skill.source_ref, `${prefix}.source_ref`)
|
|
46
|
+
requireString(skill.target_agent, `${prefix}.target_agent`)
|
|
47
|
+
requireString(skill.scope, `${prefix}.scope`)
|
|
48
|
+
requireString(skill.install_method, `${prefix}.install_method`)
|
|
49
|
+
requireBoolean(skill.discovered, `${prefix}.discovered`)
|
|
50
|
+
requireBoolean(skill.installed, `${prefix}.installed`)
|
|
51
|
+
requireBoolean(skill.attached, `${prefix}.attached`)
|
|
52
|
+
requireBoolean(skill.unused_since_install, `${prefix}.unused_since_install`)
|
|
53
|
+
requireNonNegativeInteger(skill.invoked_count, `${prefix}.invoked_count`)
|
|
54
|
+
requireNonNegativeInteger(skill.acted_on_count, `${prefix}.acted_on_count`)
|
|
55
|
+
|
|
56
|
+
if (Number.isInteger(skill.acted_on_count) && Number.isInteger(skill.invoked_count) && skill.acted_on_count > skill.invoked_count) {
|
|
57
|
+
errors.push(`${prefix}.acted_on_count cannot exceed invoked_count`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (skill.installed && skill.attached && skill.invoked_count === 0) {
|
|
61
|
+
if (skill.unused_since_install !== true) {
|
|
62
|
+
errors.push(`${prefix}.unused_since_install must be true when installed/attached but never invoked`)
|
|
63
|
+
}
|
|
64
|
+
warnings.push(`${skill.skill_id || prefix} is installed/attached but has 0 invocations in this window`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (skill.invoked_count > 0) {
|
|
68
|
+
if (skill.unused_since_install !== false) {
|
|
69
|
+
errors.push(`${prefix}.unused_since_install must be false when invoked_count > 0`)
|
|
70
|
+
}
|
|
71
|
+
if (typeof skill.last_invoked_at !== 'string' || skill.last_invoked_at.trim() === '') {
|
|
72
|
+
errors.push(`${prefix}.last_invoked_at must be set when invoked_count > 0`)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!Array.isArray(skill.evidence) || skill.evidence.length === 0) {
|
|
77
|
+
errors.push(`${prefix}.evidence must include at least one privacy-safe evidence ref`)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (errors.length > 0) {
|
|
82
|
+
console.error('skill use-rate receipt invalid:')
|
|
83
|
+
for (const error of errors) console.error(`- ${error}`)
|
|
84
|
+
process.exit(1)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const warningLabel = warnings.length === 1 ? 'warning' : 'warnings'
|
|
88
|
+
console.log(`skill use-rate receipt ok: ${receipt.skills.length} skills checked, ${warnings.length} unused install ${warningLabel}`)
|
|
89
|
+
for (const warning of warnings) console.log(`- ${warning}`)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "pluribus.skill_use_rate_receipt.v1",
|
|
3
|
+
"run_id": "skills-audit-2026-06-05T13:00Z",
|
|
4
|
+
"generated_at": "2026-06-05T13:00:00Z",
|
|
5
|
+
"installer": {
|
|
6
|
+
"name": "skills",
|
|
7
|
+
"version": "1.5.9",
|
|
8
|
+
"command_digest": "sha256:4c2b31d9f1e3a1a5f94c2c65d18f0a9b0c3d14a1c6a4f8cbb6e8f0b2a118f001"
|
|
9
|
+
},
|
|
10
|
+
"window": {
|
|
11
|
+
"started_at": "2026-05-22T00:00:00Z",
|
|
12
|
+
"ended_at": "2026-06-05T13:00:00Z"
|
|
13
|
+
},
|
|
14
|
+
"skills": [
|
|
15
|
+
{
|
|
16
|
+
"skill_id": "frontend-design",
|
|
17
|
+
"source_ref": "github:vercel-labs/agent-skills/skills/frontend-design@main",
|
|
18
|
+
"target_agent": "claude-code",
|
|
19
|
+
"scope": "project",
|
|
20
|
+
"install_method": "symlink",
|
|
21
|
+
"discovered": true,
|
|
22
|
+
"installed": true,
|
|
23
|
+
"attached": true,
|
|
24
|
+
"invoked_count": 7,
|
|
25
|
+
"acted_on_count": 3,
|
|
26
|
+
"last_invoked_at": "2026-06-05T10:12:08Z",
|
|
27
|
+
"unused_since_install": false,
|
|
28
|
+
"context_cost_bucket": "small",
|
|
29
|
+
"evidence": [
|
|
30
|
+
{
|
|
31
|
+
"kind": "session_log_digest",
|
|
32
|
+
"ref": "sha256:0b7d7a9f2950f5e4ab8d9ab2f93e71875a995ee3878d0d595c8a65f8d7bf0c4d"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"skill_id": "rust-lsp-helper",
|
|
38
|
+
"source_ref": "github:example-org/agent-skills/skills/rust-lsp-helper@v1.2.0",
|
|
39
|
+
"target_agent": "claude-code",
|
|
40
|
+
"scope": "global",
|
|
41
|
+
"install_method": "copy",
|
|
42
|
+
"discovered": true,
|
|
43
|
+
"installed": true,
|
|
44
|
+
"attached": true,
|
|
45
|
+
"invoked_count": 0,
|
|
46
|
+
"acted_on_count": 0,
|
|
47
|
+
"last_invoked_at": null,
|
|
48
|
+
"unused_since_install": true,
|
|
49
|
+
"context_cost_bucket": "medium",
|
|
50
|
+
"evidence": [
|
|
51
|
+
{
|
|
52
|
+
"kind": "installer_lock_digest",
|
|
53
|
+
"ref": "sha256:d25b0ddca0d6b8ac8705cc1e845351f5f5a3971b982be9dc6eeb3aadc5a83a54"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"skill_id": "code-review-xhigh",
|
|
59
|
+
"source_ref": "builtin:claude-code/code-review@2.1.152",
|
|
60
|
+
"target_agent": "claude-code",
|
|
61
|
+
"scope": "builtin",
|
|
62
|
+
"install_method": "builtin",
|
|
63
|
+
"discovered": true,
|
|
64
|
+
"installed": true,
|
|
65
|
+
"attached": true,
|
|
66
|
+
"invoked_count": 12,
|
|
67
|
+
"acted_on_count": 9,
|
|
68
|
+
"last_invoked_at": "2026-06-05T12:40:01Z",
|
|
69
|
+
"unused_since_install": false,
|
|
70
|
+
"context_cost_bucket": "unknown",
|
|
71
|
+
"evidence": [
|
|
72
|
+
{
|
|
73
|
+
"kind": "review_report_digest",
|
|
74
|
+
"ref": "sha256:8f489e2b0d3539b17f3d0b6ebc68bf1c9d68022c6ea086b6a3636b107a42f9f4"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pluribus-context",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.37",
|
|
4
4
|
"description": "AI context and rules sync CLI for Claude.md, Claude Code, Cursor, and Copilot instructions, with privacy-safe context receipts that prove what memory, tools, skills, compactions, and security findings crossed agent boundaries without logging raw content.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/caioribeiroclw-pixel/pluribus#readme",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"spec/",
|
|
23
23
|
"schemas/",
|
|
24
24
|
"examples/",
|
|
25
|
+
"skills/",
|
|
25
26
|
"CHANGELOG.md"
|
|
26
27
|
],
|
|
27
28
|
"scripts": {
|
|
@@ -19,10 +19,10 @@ grep -E 'raw_(schema|query|args|result|output|transcript|text)_copied":false|raw
|
|
|
19
19
|
|
|
20
20
|
Then manually check that the receipt contains counts, hashes, ids, buckets, and `audit_gap`, but does **not** contain private prompts, raw schemas, tool args/results, skill bodies, memory bodies, customer names, secrets, or transcript text.
|
|
21
21
|
|
|
22
|
-
For executable fixture examples, see [`../../context-input-evidence/`](../../context-input-evidence/), including the ToolSearch propagation, pruning, and compaction transaction smokes:
|
|
22
|
+
For executable fixture examples, see [`../../examples/context-input-evidence/`](../../examples/context-input-evidence/), including the ToolSearch propagation, pruning, and compaction transaction smokes:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
node ../../context-input-evidence/convert-subagent-toolsearch-propagation-log.mjs
|
|
26
|
-
node ../../context-input-evidence/convert-pruning-log.mjs
|
|
27
|
-
node ../../context-input-evidence/convert-compaction-transaction-log.mjs
|
|
25
|
+
node ../../examples/context-input-evidence/convert-subagent-toolsearch-propagation-log.mjs
|
|
26
|
+
node ../../examples/context-input-evidence/convert-pruning-log.mjs
|
|
27
|
+
node ../../examples/context-input-evidence/convert-compaction-transaction-log.mjs
|
|
28
28
|
```
|