@planu/cli 5.3.15 → 5.3.16

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 CHANGED
@@ -1,3 +1,13 @@
1
+ ## [5.3.16] - 2026-08-11
2
+
3
+ ### Bug Fixes
4
+ - fix(spec-1477): bound publication-window wait per implementation-review arbitration
5
+ - fix(spec-1477): publication window blocks same-identity lease supersession mid-receipt
6
+
7
+ ### Chores
8
+ - chore(specs): record SPEC-1477 done transition and file SPEC-1487 dogfood bug
9
+
10
+
1
11
  ## [5.3.15] - 2026-08-11
2
12
 
3
13
  ### Bug Fixes
@@ -11,7 +11,7 @@ import { resolveStorageLayout } from '../../storage/storage-layout.js';
11
11
  import { reportClassifiedDegradation } from '../../errors/classified-degradation.js';
12
12
  import { createValidationReceiptAuthority, ensureValidationIssuerKey, issueValidationReceipt, LOCAL_VALIDATION_RECEIPT_ISSUER, preflightValidationReceiptSigner, } from '../validation/validation-receipt.js';
13
13
  import { appendArtifact, readArtifact } from '../handoff-artifacts/io.js';
14
- import { assertValidationFreshnessBarrier, beginValidationFreshnessBarrier, bindValidationFreshnessLease, closeValidationFreshnessLease, rebaselineValidationFreshnessLeaseAfterRecompute, } from '../validation/validation-freshness.js';
14
+ import { assertValidationFreshnessBarrier, beginValidationFreshnessBarrier, beginValidationFreshnessPublicationWindow, bindValidationFreshnessLease, closeValidationFreshnessLease, rebaselineValidationFreshnessLeaseAfterRecompute, } from '../validation/validation-freshness.js';
15
15
  import { recoverValidateJobs } from './validate-job-recovery.js';
16
16
  import { getRuntimePolicy } from '../runtime-policy.js';
17
17
  import { isValidateJob, parseValidatePayload, rehydrateValidationPayload, } from './validate-job-payload.js';
@@ -664,62 +664,69 @@ export class ValidateJobExecutor {
664
664
  policy.receiptCheckpointResultMaxBytes,
665
665
  };
666
666
  }
667
+ // eslint-disable-next-line max-lines-per-function -- publication window and the double freshness assert share one auditable receipt-issuance boundary
667
668
  async issueReceipt(identity, payload, checkpoint, publicationLease, fencingToken, signal) {
668
669
  const receiptBindings = checkpoint.receiptInput.bindings;
669
- if (executionAborted(signal)) {
670
- signal.throwIfAborted();
671
- }
672
- if (!(await assertValidationFreshnessBarrier(publicationLease, receiptBindings))) {
673
- throw new Error('[Planu] Validation inputs changed before receipt publication');
674
- }
675
- const database = new RuntimeDatabase({ path: resolveStorageLayout().runtimeDatabase });
670
+ const endPublicationWindow = beginValidationFreshnessPublicationWindow(publicationLease);
676
671
  try {
677
- const signingKeyReference = await ensureValidationIssuerKey({
678
- projectId: identity.projectId,
679
- issuerId: LOCAL_VALIDATION_RECEIPT_ISSUER,
680
- });
681
- signal.throwIfAborted();
682
- const { store } = createValidationReceiptAuthority({
683
- database,
684
- workspaceId: identity.workspaceId,
685
- projectId: identity.projectId,
686
- issuerId: LOCAL_VALIDATION_RECEIPT_ISSUER,
687
- authorizePersistence: (persistenceDatabase) => {
688
- persistenceDatabase.assertActiveJobLease({
689
- id: identity.operationId,
690
- projectId: identity.projectId,
691
- workspaceId: identity.workspaceId,
692
- workerId: this.workerId,
693
- fencingToken,
694
- leaseMs: this.workerTimingPolicy().workerLeaseMs,
695
- now: new Date().toISOString(),
696
- });
697
- },
698
- });
699
- const receipt = await issueValidationReceipt(checkpoint.receiptInput, {
700
- issuerId: LOCAL_VALIDATION_RECEIPT_ISSUER,
701
- signingKeyReference,
702
- store,
703
- });
704
672
  if (executionAborted(signal)) {
705
673
  signal.throwIfAborted();
706
674
  }
707
675
  if (!(await assertValidationFreshnessBarrier(publicationLease, receiptBindings))) {
708
- await store.invalidateCurrentBindings(identity.projectId, payload.bindings.specId);
709
- throw new Error('[Planu] Validation inputs changed during receipt publication');
676
+ throw new Error('[Planu] Validation inputs changed before receipt publication');
710
677
  }
711
- const receiptKey = `validation-receipt:${receipt.receiptId.slice('sha256:'.length)}`;
712
- let enrichedReportSha;
678
+ const database = new RuntimeDatabase({ path: resolveStorageLayout().runtimeDatabase });
713
679
  try {
714
- enrichedReportSha = await this.persistReceiptArtifacts(identity, payload, receipt.receiptId, receiptKey);
680
+ const signingKeyReference = await ensureValidationIssuerKey({
681
+ projectId: identity.projectId,
682
+ issuerId: LOCAL_VALIDATION_RECEIPT_ISSUER,
683
+ });
684
+ signal.throwIfAborted();
685
+ const { store } = createValidationReceiptAuthority({
686
+ database,
687
+ workspaceId: identity.workspaceId,
688
+ projectId: identity.projectId,
689
+ issuerId: LOCAL_VALIDATION_RECEIPT_ISSUER,
690
+ authorizePersistence: (persistenceDatabase) => {
691
+ persistenceDatabase.assertActiveJobLease({
692
+ id: identity.operationId,
693
+ projectId: identity.projectId,
694
+ workspaceId: identity.workspaceId,
695
+ workerId: this.workerId,
696
+ fencingToken,
697
+ leaseMs: this.workerTimingPolicy().workerLeaseMs,
698
+ now: new Date().toISOString(),
699
+ });
700
+ },
701
+ });
702
+ const receipt = await issueValidationReceipt(checkpoint.receiptInput, {
703
+ issuerId: LOCAL_VALIDATION_RECEIPT_ISSUER,
704
+ signingKeyReference,
705
+ store,
706
+ });
707
+ if (executionAborted(signal)) {
708
+ signal.throwIfAborted();
709
+ }
710
+ if (!(await assertValidationFreshnessBarrier(publicationLease, receiptBindings))) {
711
+ await store.invalidateCurrentBindings(identity.projectId, payload.bindings.specId);
712
+ throw new Error('[Planu] Validation inputs changed during receipt publication');
713
+ }
714
+ const receiptKey = `validation-receipt:${receipt.receiptId.slice('sha256:'.length)}`;
715
+ let enrichedReportSha;
716
+ try {
717
+ enrichedReportSha = await this.persistReceiptArtifacts(identity, payload, receipt.receiptId, receiptKey);
718
+ }
719
+ catch (error) {
720
+ throw new ReceiptPublicationInterruptedError({ cause: error });
721
+ }
722
+ return buildReceiptResult(checkpoint.result, receipt.receiptId, receiptKey, enrichedReportSha);
715
723
  }
716
- catch (error) {
717
- throw new ReceiptPublicationInterruptedError({ cause: error });
724
+ finally {
725
+ database.close();
718
726
  }
719
- return buildReceiptResult(checkpoint.result, receipt.receiptId, receiptKey, enrichedReportSha);
720
727
  }
721
728
  finally {
722
- database.close();
729
+ endPublicationWindow();
723
730
  }
724
731
  }
725
732
  async preparePublicationLease(payload, expectedBindings) {
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-aarch64-apple-darwin",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-aarch64-apple-darwin",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -17,15 +17,15 @@
17
17
  "platform": "darwin",
18
18
  "arch": "arm64",
19
19
  "libc": null,
20
- "artifactSha256": "0fbd8c0babdbfb88cae442db9cc57521f67ee345aa1872ed0cd04f464f992584",
20
+ "artifactSha256": "2a6db4c53e8d2e492a44dc8c276b3f1ec9e00f20056225221aa142752a9dfb4b",
21
21
  "sbom": {
22
22
  "format": "CycloneDX-1.5",
23
23
  "path": "planu-core.darwin-arm64.node.sbom.json",
24
- "sha256": "41999cac08a7c64a9c899eac628df8e4bc10d70dc1b1923d5951a89f5ac8e878"
24
+ "sha256": "6bfb518a006ae80f0a94328c6fc0be1fa7d86b9a5bab2d7476a80fc8171dfd71"
25
25
  },
26
26
  "provenance": {
27
27
  "builder": "scripts/build-rust-local.sh",
28
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
28
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
29
29
  "sourceTreeDirty": false,
30
30
  "sourceDateEpoch": 1,
31
31
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -34,6 +34,6 @@
34
34
  "signature": {
35
35
  "algorithm": "Ed25519",
36
36
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
37
- "value": "bTat4AK64vTOtmTHrAf9w8z5d8GbFuxJZizc1YuZm05VpDaKzSaHkztZ5Hrb+sKu0lppYebpOGwYqTcFmfUMDQ=="
37
+ "value": "6QDQPYqy6xJ4RHk+SGv1DdPTxgQDQCG63NMTaK2Kk72npIDR7g6h/mCKNLhFDXPALcS7kjqKRijf/PzRqnmCBQ=="
38
38
  }
39
39
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "0fbd8c0babdbfb88cae442db9cc57521f67ee345aa1872ed0cd04f464f992584"
14
+ "content": "2a6db4c53e8d2e492a44dc8c276b3f1ec9e00f20056225221aa142752a9dfb4b"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-x86_64-apple-darwin",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-x86_64-apple-darwin",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -17,15 +17,15 @@
17
17
  "platform": "darwin",
18
18
  "arch": "x64",
19
19
  "libc": null,
20
- "artifactSha256": "f2b7904e0e93116b6042e9f67e33ca60f2f054a48ae9f365327209473010a38a",
20
+ "artifactSha256": "be5de1bbcd0f9b4989aa82c5b51f0a7425c3eb1fa9dfd772cc362f476db433f5",
21
21
  "sbom": {
22
22
  "format": "CycloneDX-1.5",
23
23
  "path": "planu-core.darwin-x64.node.sbom.json",
24
- "sha256": "40bb12bea402c3d433ca284cbb80c122ffcacb68defa17409da65583f9fe0b73"
24
+ "sha256": "38a7cc4b3d77c4414c58fd23af446914e84db52dd937e13b763da35b1962ab1e"
25
25
  },
26
26
  "provenance": {
27
27
  "builder": "scripts/build-rust-local.sh",
28
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
28
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
29
29
  "sourceTreeDirty": false,
30
30
  "sourceDateEpoch": 1,
31
31
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -34,6 +34,6 @@
34
34
  "signature": {
35
35
  "algorithm": "Ed25519",
36
36
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
37
- "value": "wDC/VmuwtNH7DIZHFeD9JAuHp08V8yDMSOgopr83IQIBnQkWBsqS9lQ2Zchxh7JNiTEfICvSQF7lMxA9Dk6UAQ=="
37
+ "value": "7X8ph1LL1pB4hXlWcXD109sTeHzyRTa94ozW52TdCg6y1LIH1kNwuz4rU0g0KK4CLpnanRpeLpu7NODRGiP2AQ=="
38
38
  }
39
39
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "f2b7904e0e93116b6042e9f67e33ca60f2f054a48ae9f365327209473010a38a"
14
+ "content": "be5de1bbcd0f9b4989aa82c5b51f0a7425c3eb1fa9dfd772cc362f476db433f5"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-aarch64-unknown-linux-gnu",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-aarch64-unknown-linux-gnu",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "arm64",
18
18
  "libc": "glibc",
19
- "artifactSha256": "01ea24db6f97e97faeef630279a6ee999acb4a7562771459a2423a7598304253",
19
+ "artifactSha256": "01d8ce2cb696744dc58095e8ff7fb2be6afcbccf467b0cd0eb538b6be530e210",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-arm64-gnu.node.sbom.json",
23
- "sha256": "02f9bcacd4b15056f3b945e103ed330d231fc9ac0f1a377dd1ec326e60b2b1dc"
23
+ "sha256": "5eb69bed870789d764c02f63f0371c51872314e22a888c60af9842c35f536a4b"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
27
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "LwJ+lJNVWcGsacrsWvGRbBS6WQTC7iMOAg3yfb0AZtdsHLBvAyG1700kh/1E1CbKEUR73CbUmU2TzEzVkesUAw=="
36
+ "value": "EJcdFkXbSIapzhu002fZ/X4QRUCpbJjwj4AYsg0G2BiERBQjMavAeiXqVNLr75Jt84NRmcOqXhidCvaEV0snCQ=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "01ea24db6f97e97faeef630279a6ee999acb4a7562771459a2423a7598304253"
14
+ "content": "01d8ce2cb696744dc58095e8ff7fb2be6afcbccf467b0cd0eb538b6be530e210"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-aarch64-unknown-linux-musl",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-aarch64-unknown-linux-musl",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "arm64",
18
18
  "libc": "musl",
19
- "artifactSha256": "77906aaa7ef35468fae4f48928d7fe427f6b42134f6ba27b7b10852a904a9063",
19
+ "artifactSha256": "9b0683851a369169cda5c0e4141df24a59f94da03fdf7b8fc1230da65e38eb36",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-arm64-musl.node.sbom.json",
23
- "sha256": "03b4827b05fb040843d38ede98208022adce12d8c584a6f4a909eab71d18b439"
23
+ "sha256": "463cb73768477635328836a2a13d34038a2c7aeb83de4d17212274135af77e9e"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
27
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "cK02P++x9IPGghpCbgzJx1roAidXZtDiELr8LDUtD6JfRGvOf6l3VwX+Lvd+cSz/dLK1cosRIjoeZFovhbTnDg=="
36
+ "value": "LGOiA63xus7b3/9w0/8qTyrKDhkRV0y3EMRV7F5NHxj8oDGqLz81viGJvBU+iri4Xhh0Ku03hxoD377vhE4TBQ=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "77906aaa7ef35468fae4f48928d7fe427f6b42134f6ba27b7b10852a904a9063"
14
+ "content": "9b0683851a369169cda5c0e4141df24a59f94da03fdf7b8fc1230da65e38eb36"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-x86_64-unknown-linux-gnu",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-x86_64-unknown-linux-gnu",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "x64",
18
18
  "libc": "glibc",
19
- "artifactSha256": "ef2493926703d865ea8673d8357a238058a32a64325050d3880518d8884f25f2",
19
+ "artifactSha256": "2d8c9186e43c03eb451a9772c3dac7931039e5326997364bc2bcc5aa38726b48",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-x64-gnu.node.sbom.json",
23
- "sha256": "c9934e8615cac53687cc3de64ba400212a12aa8653cb320f9435c131fde66026"
23
+ "sha256": "a27023f31bf2290b52d4e344d1859f3bbb49f5a19ce9998800987440ce834964"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
27
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "AfBRj+G23Cdv1PqCjbZHpYdDXB5LW1JcgIbwcMstlFPhrv8ln9HTsyVOCE1PVl1ZeKMGP2YsTqgQj+XJb7tfBw=="
36
+ "value": "FEn6GiPlQVBmOEc8zw6SxFzCxNF9OfYAakAa36oR0r5fbCQBFBgfbuuKGdLar7rdnaU3vcgiexCzHJIuYdwFCg=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "ef2493926703d865ea8673d8357a238058a32a64325050d3880518d8884f25f2"
14
+ "content": "2d8c9186e43c03eb451a9772c3dac7931039e5326997364bc2bcc5aa38726b48"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-x86_64-unknown-linux-musl",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-x86_64-unknown-linux-musl",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "linux",
17
17
  "arch": "x64",
18
18
  "libc": "musl",
19
- "artifactSha256": "be770ad8fff0c70d1adf1599af7b60a9e0055e133890476e08e04c88c3f28c69",
19
+ "artifactSha256": "50547de5b23c0671447358eebc95d3841a2ce278236f5000655182ffb998d547",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.linux-x64-musl.node.sbom.json",
23
- "sha256": "efd3cf900ecd753f0bee1eba0c6ecc8bd21d4f405e5d346f0fbe58972cf65da4"
23
+ "sha256": "a074e28e92546729d7347be57236bd8a0678ea1396fd756770915db6efecfa18"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
27
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "tiDSdVl5hTKNRg88FbcmEwUh4rXFh7OGfMzb9fV3BqwBAwSA/XktD4rXe4YZyBy75Kebo11zW+gp3DMnf+KECA=="
36
+ "value": "qvTXfNiZUTxm+mdb2A7/P6wjxEhdCr9WIVZkzDDOoxu4k4NgFeBmrSGKdRays0RSEivQ9f23gnvhS1myJ+DIAQ=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "be770ad8fff0c70d1adf1599af7b60a9e0055e133890476e08e04c88c3f28c69"
14
+ "content": "50547de5b23c0671447358eebc95d3841a2ce278236f5000655182ffb998d547"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-aarch64-pc-windows-msvc",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-aarch64-pc-windows-msvc",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "win32",
17
17
  "arch": "arm64",
18
18
  "libc": null,
19
- "artifactSha256": "583b397fb748d8ce083c4b7d412800789e8b08d6cfcdf51dbb00ca8f16b790c3",
19
+ "artifactSha256": "151f0906eac861f554ce19ede7347d8267048f67aeb71b5f2521c7eaf8fbc034",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.win32-arm64-msvc.node.sbom.json",
23
- "sha256": "ed94ce39f49489d682bc80cce95eb3ef04bafd64ed7fd7d8ea6def8b12c9a458"
23
+ "sha256": "2acb3f0615fee3f77ca7be0110f41c245b760d81811c0299a57aac2f7aa15fc9"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
27
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "gaOwCaYMRwh0TEujBu7Rabl255fJETQEAo4i+NFVpamCPo5YWa2sknnxsbm/ZM4iX9YLD1W5S1MeYFUcwVrIAg=="
36
+ "value": "qMuwIDaEU/3JJj3BuHx/uv8ZCDScOhZGUSHuIKSS64I31OESsH7QuSd6+7QYT9THm5hb0nxuol3BtyyQ3AX3Dg=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "583b397fb748d8ce083c4b7d412800789e8b08d6cfcdf51dbb00ca8f16b790c3"
14
+ "content": "151f0906eac861f554ce19ede7347d8267048f67aeb71b5f2521c7eaf8fbc034"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "cliVersion": "5.3.15",
4
- "engineVersion": "5.3.15",
5
- "buildId": "1-5.3.15-x86_64-pc-windows-msvc",
3
+ "cliVersion": "5.3.16",
4
+ "engineVersion": "5.3.16",
5
+ "buildId": "1-5.3.16-x86_64-pc-windows-msvc",
6
6
  "protocolAbi": 1,
7
7
  "capabilityAbi": 2,
8
8
  "nodeApiAbi": 4,
@@ -16,15 +16,15 @@
16
16
  "platform": "win32",
17
17
  "arch": "x64",
18
18
  "libc": null,
19
- "artifactSha256": "612e2d929b12f1011da69ddcc598cbd251123294b52cfe7c04cd921e18d46ad7",
19
+ "artifactSha256": "b002099edf523c6aa0ca6f658a19514ff0a20ccf154bd88e4e205d49d9135590",
20
20
  "sbom": {
21
21
  "format": "CycloneDX-1.5",
22
22
  "path": "planu-core.win32-x64-msvc.node.sbom.json",
23
- "sha256": "b6f3af124aeadba537605434bc406ac76f0bd798244edfbe31566b5011989207"
23
+ "sha256": "c1b0d2ac50e19c7133dccb297d19f3a317125bfd88e87508ae0743acba413c5f"
24
24
  },
25
25
  "provenance": {
26
26
  "builder": "scripts/build-rust-local.sh",
27
- "sourceCommit": "14948c630bfb7a355391c10563934cc79e8bb167",
27
+ "sourceCommit": "0cde0ff71b6b07e6bb7c059c7f03a7a58910e221",
28
28
  "sourceTreeDirty": false,
29
29
  "sourceDateEpoch": 1,
30
30
  "rustToolchain": "rustc 1.95.0 (59807616e 2026-04-14)",
@@ -33,6 +33,6 @@
33
33
  "signature": {
34
34
  "algorithm": "Ed25519",
35
35
  "keyId": "e2ee765cb5ad9fbdc433ece332bce26d746d5e350b3acde0721a4c8c6337ff7b",
36
- "value": "2W3TfOtk1zUPWqx2o2a9DmJPT1rSEBL0kLVOrZfggV/B/hEWC5KgLZs0Cy9w6kE5QeWTeGQZ1spl12XVCdAZDQ=="
36
+ "value": "wQa0ygTquWBg4jFcHX8+sVqOETbJGfViThB7wyshllPFfdCF8s8chApggB+dgjCJd6qz2JaI1aJyifFFsIF9BQ=="
37
37
  }
38
38
  }
@@ -4,14 +4,14 @@
4
4
  "version": 1,
5
5
  "metadata": {
6
6
  "component": {
7
- "bom-ref": "pkg:cargo/planu-core@5.3.15",
7
+ "bom-ref": "pkg:cargo/planu-core@5.3.16",
8
8
  "type": "library",
9
9
  "name": "planu-core",
10
- "version": "5.3.15",
10
+ "version": "5.3.16",
11
11
  "hashes": [
12
12
  {
13
13
  "alg": "SHA-256",
14
- "content": "612e2d929b12f1011da69ddcc598cbd251123294b52cfe7c04cd921e18d46ad7"
14
+ "content": "b002099edf523c6aa0ca6f658a19514ff0a20ccf154bd88e4e205d49d9135590"
15
15
  }
16
16
  ]
17
17
  }
@@ -1106,7 +1106,7 @@
1106
1106
  "dependsOn": []
1107
1107
  },
1108
1108
  {
1109
- "ref": "pkg:cargo/planu-core@5.3.15",
1109
+ "ref": "pkg:cargo/planu-core@5.3.16",
1110
1110
  "dependsOn": [
1111
1111
  "pkg:cargo/core-foundation@0.10.1",
1112
1112
  "pkg:cargo/hmac@0.12.1",
@@ -29,6 +29,13 @@ export declare function injectValidationWatchEventForTests(input: {
29
29
  }): ValidationWatchEventClassification | undefined;
30
30
  /** Pause or observe authoritative verification turns without changing watcher topology. */
31
31
  export declare function setValidationFreshnessVerificationHookForTests(hook: (() => Promise<void>) | undefined): void;
32
+ /**
33
+ * Mark one exact epoch's receipt publication as in flight. A same-identity
34
+ * resubmission that arrives while the window is open waits for the returned
35
+ * closer before superseding this epoch, instead of closing it mid-publication.
36
+ * Returns a no-op closer once the epoch is no longer the active one.
37
+ */
38
+ export declare function beginValidationFreshnessPublicationWindow(lease: Pick<ValidationFreshnessLease, 'projectId' | 'specId' | 'epoch'>): () => void;
32
39
  /** Start both watchers before snapshotting and invalidate any older receipt immediately. */
33
40
  export declare function beginValidationFreshnessBarrier(input: {
34
41
  projectId: string;
@@ -71,6 +71,19 @@ let verificationHookForTests;
71
71
  function key(projectId, specId) {
72
72
  return `${projectId}:${specId}`;
73
73
  }
74
+ function noop() {
75
+ /* no-op */
76
+ }
77
+ function raceWithTimeout(promise, timeoutMs) {
78
+ return new Promise((resolve) => {
79
+ const timer = setTimeout(resolve, timeoutMs);
80
+ timer.unref();
81
+ void promise.finally(() => {
82
+ clearTimeout(timer);
83
+ resolve();
84
+ });
85
+ });
86
+ }
74
87
  async function invalidateDurably(projectId, specId) {
75
88
  const env_1 = { stack: [], error: void 0, hasError: false };
76
89
  try {
@@ -380,6 +393,29 @@ async function evidenceFingerprint(path) {
380
393
  }
381
394
  return hash.digest('hex');
382
395
  }
396
+ /**
397
+ * Mark one exact epoch's receipt publication as in flight. A same-identity
398
+ * resubmission that arrives while the window is open waits for the returned
399
+ * closer before superseding this epoch, instead of closing it mid-publication.
400
+ * Returns a no-op closer once the epoch is no longer the active one.
401
+ */
402
+ export function beginValidationFreshnessPublicationWindow(lease) {
403
+ const state = active.get(key(lease.projectId, lease.specId));
404
+ if (state?.epoch !== lease.epoch) {
405
+ return noop;
406
+ }
407
+ let release;
408
+ const window = new Promise((resolve) => {
409
+ release = resolve;
410
+ });
411
+ state.publicationInFlight = window;
412
+ return () => {
413
+ release();
414
+ if (state.publicationInFlight === window) {
415
+ state.publicationInFlight = undefined;
416
+ }
417
+ };
418
+ }
383
419
  /** Start both watchers before snapshotting and invalidate any older receipt immediately. */
384
420
  // eslint-disable-next-line max-lines-per-function -- serialized setup and rollback form one auditable ownership boundary
385
421
  export async function beginValidationFreshnessBarrier(input) {
@@ -395,11 +431,16 @@ export async function beginValidationFreshnessBarrier(input) {
395
431
  try {
396
432
  const previous = active.get(identity);
397
433
  if (previous) {
398
- await closeValidationFreshnessLease({
399
- projectId: input.projectId,
400
- specId: input.specId,
401
- epoch: previous.epoch,
402
- });
434
+ if (previous.publicationInFlight) {
435
+ await raceWithTimeout(previous.publicationInFlight, getRuntimePolicy().validation.receiptPublicationDeadlineMs + 1000);
436
+ }
437
+ if (active.get(identity) === previous) {
438
+ await closeValidationFreshnessLease({
439
+ projectId: input.projectId,
440
+ specId: input.specId,
441
+ epoch: previous.epoch,
442
+ });
443
+ }
403
444
  }
404
445
  await mkdir(input.handoffPath, { recursive: true, mode: 0o700 });
405
446
  const initialEvidenceFingerprint = await evidenceFingerprint(input.handoffPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planu/cli",
3
- "version": "5.3.15",
3
+ "version": "5.3.16",
4
4
  "description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -35,14 +35,14 @@
35
35
  "packageName": "@planu/core"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@planu/core-darwin-arm64": "5.3.15",
39
- "@planu/core-darwin-x64": "5.3.15",
40
- "@planu/core-linux-arm64-gnu": "5.3.15",
41
- "@planu/core-linux-arm64-musl": "5.3.15",
42
- "@planu/core-linux-x64-gnu": "5.3.15",
43
- "@planu/core-linux-x64-musl": "5.3.15",
44
- "@planu/core-win32-arm64-msvc": "5.3.15",
45
- "@planu/core-win32-x64-msvc": "5.3.15"
38
+ "@planu/core-darwin-arm64": "5.3.16",
39
+ "@planu/core-darwin-x64": "5.3.16",
40
+ "@planu/core-linux-arm64-gnu": "5.3.16",
41
+ "@planu/core-linux-arm64-musl": "5.3.16",
42
+ "@planu/core-linux-x64-gnu": "5.3.16",
43
+ "@planu/core-linux-x64-musl": "5.3.16",
44
+ "@planu/core-win32-arm64-msvc": "5.3.16",
45
+ "@planu/core-win32-x64-msvc": "5.3.16"
46
46
  },
47
47
  "engines": {
48
48
  "node": ">=24.0.0"
package/planu-native.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dev.planu.native",
3
3
  "displayName": "Planu Native Lightweight Surface",
4
- "version": "5.3.15",
4
+ "version": "5.3.16",
5
5
  "packageName": "@planu/cli",
6
6
  "modes": {
7
7
  "lightweight": {
package/planu-plugin.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dev.planu.cli",
3
3
  "displayName": "Planu — Spec Driven Development",
4
4
  "description": "Manage software specs, estimations, and autonomous SDD workflows. Language-agnostic MCP server for Claude Code.",
5
- "version": "5.3.15",
5
+ "version": "5.3.16",
6
6
  "icon": "assets/plugin/icon.svg",
7
7
  "command": ["npx", "@planu/cli@latest"],
8
8
  "packageName": "@planu/cli",