software-defence-factory 0.9.0 → 0.9.1

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/docs/npm.md CHANGED
@@ -71,6 +71,43 @@ rebuilding the shared tag cannot remove an existing installation's pinned image.
71
71
  These retained images are recovery data; remove them only after confirming no
72
72
  installation or retained attempt needs them.
73
73
 
74
+ ## Protected evidence compatibility
75
+
76
+ 0.9.1 recognizes version-1 execution profiles emitted by native **0.8.0,
77
+ 0.9.0 and 0.9.1**. This is an exact allowlist in
78
+ `factory/execution-profile.mjs`, independent of the installed package version;
79
+ it is not a semver range or an automatic promise for later releases. Unknown
80
+ runtime strings, unknown profile formats and incomplete legacy acceptance
81
+ remain unverified or blocked. New profiles still record the actual emitting
82
+ runtime version. Updates never relabel profiles, rewrite policy hashes,
83
+ re-approve jobs or retry old work.
84
+
85
+ Compatibility only permits interpreting the protected evidence. Publication
86
+ still requires exact protected-file/run-record equality, successful linked
87
+ phases with supported role/model provenance, the unchanged effective policy
88
+ hash, retained source and candidate/patch identity, passing checks, independent
89
+ review and bound approval. Synthetic or missing provenance cannot authorize
90
+ publication. Enabled browser verification still requires current tool, story,
91
+ candidate, attempt and retained artifact evidence; enabling or changing that
92
+ policy invalidates prior evidence. CLI, API and dashboard consume the same
93
+ delivery capability.
94
+
95
+ Before extending this list, inspect the released profile/evidence writers and
96
+ exercise both capability derivation and publication validation. Remove support
97
+ for an older writer whenever a trust-relevant format, isolation, provenance or
98
+ validation change makes its guarantees insufficient for current policy; bump
99
+ the evidence format when its meaning changes. A package version bump alone is
100
+ neither a reason to discard evidence nor proof of compatibility. Retain
101
+ unsupported records unchanged and obtain fresh applicable evidence through the
102
+ normal workflow; never repair them by editing private records or hashes.
103
+
104
+ After native Verify/Review, the lead must qualify the installed patch read-only
105
+ against actual retained production evidence. This source change and its
106
+ controlled fixtures do not supply that proof. An already delivered PR keeps
107
+ read-only receipt reconciliation. New publication still requires the current
108
+ remote target to equal the original accepted base; checkpoint continuation and
109
+ target mismatch remain the separate #42 limit.
110
+
74
111
  ## Release flow
75
112
 
76
113
  `.github/workflows/ci.yml` tests pull requests and pushes on Node 22 and 24,
package/docs/recovery.md CHANGED
@@ -126,6 +126,16 @@ proof of the model/provider that served inference. Changing the installation's
126
126
  profile affects subsequent attempts, never the recorded history. A changed
127
127
  check/review policy still invalidates acceptance of earlier proof.
128
128
 
129
+ Protected delivery evidence uses an explicit [runtime compatibility
130
+ contract](npm.md#protected-evidence-compatibility): version-1 profiles from
131
+ 0.8.0, 0.9.0 and 0.9.1 can be interpreted by 0.9.1 while every existing trust
132
+ and current-policy guard still applies. An upgrade alone does not require
133
+ re-approval. Preserve original runtime versions, hashes and phase records;
134
+ do not normalize old configuration or manufacture provenance to unblock a job.
135
+ Unknown formats/versions remain blocked. Published PR receipt reconciliation
136
+ stays read-only, and compatibility does not relax the original-base/remote-target
137
+ guard or add checkpoint continuation.
138
+
129
139
  Attempts made before this metadata existed display **Not recorded
130
140
  (legacy/unknown)**. Do not copy today's profile onto them. For an investigation,
131
141
  compare the retained per-attempt measurement, logs, candidate/check/review hashes
@@ -3,9 +3,8 @@ import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, wr
3
3
  import { join, resolve } from 'node:path';
4
4
  import { spawnSync } from 'node:child_process';
5
5
  import { isDeepStrictEqual } from 'node:util';
6
- import { VERSION } from './updates.mjs';
7
6
  import { configAt, digest } from './lib.mjs';
8
- import { effectiveExecutionConfig } from './execution-profile.mjs';
7
+ import { effectiveExecutionConfig, isSupportedExecutionProfile } from './execution-profile.mjs';
9
8
  import { publicSourceAdmission } from './source-admission.mjs';
10
9
  import { QueueError } from './queue.mjs';
11
10
  import { readProjectLinks } from './project-links.mjs';
@@ -221,8 +220,8 @@ function trustedExecutionProfile(state, job, run, phase, expectedPolicy) {
221
220
  && ['explicit', 'provider_default'].includes(profile.modelSelection)
222
221
  && (profile.modelSelection === 'explicit' ? typeof profile.requestedModel === 'string' : profile.requestedModel === null)
223
222
  : profile.executor === 'deterministic' && profile.modelSelection === 'not_applicable' && profile.requestedModel === null;
224
- return profile.version === 1 && profile.phase === phase && executorMatches
225
- && profile.runtimeVersion === VERSION && profile.policyHash === expectedPolicy
223
+ return isSupportedExecutionProfile(profile) && profile.phase === phase && executorMatches
224
+ && profile.policyHash === expectedPolicy
226
225
  && isDeepStrictEqual(run.execution, profile);
227
226
  } catch { return false; }
228
227
  }
@@ -284,7 +283,7 @@ function acceptanceSummary(job, state, config, sourceAdmission) {
284
283
  { build: buildRun, verify: checkRun, review: reviewRun, handoff: handoffRun }, candidate, checks, review);
285
284
  if (!phaseEvidence.trusted) return { state: 'blocked', reason: phaseEvidence.reason };
286
285
  const validRun = (run, command) => run?.command === command && run.state === 'succeeded' && run.outcome === 'complete'
287
- && run.execution?.runtimeVersion === VERSION && run.execution?.policyHash === expectedPolicy;
286
+ && isSupportedExecutionProfile(run.execution) && run.execution.policyHash === expectedPolicy;
288
287
  const bound = handoff.id === accepted.handoff_run_id && validRun(buildRun, 'build')
289
288
  && validRun(checkRun, 'verify') && validRun(reviewRun, 'review') && validRun(handoff, 'handoff')
290
289
  && job.source_admission?.status === 'retained' && accepted.head !== accepted.base && candidate.parent === accepted.base
@@ -508,7 +507,7 @@ export class DeliveryService {
508
507
  const byID = id => job.runs.find(run => run.id === id);
509
508
  const buildRun = byID(accepted.build_run_id), checkRun = byID(accepted.checks_run_id), reviewRun = byID(accepted.review_run_id);
510
509
  const validRun = (run, command) => run?.command === command && run.state === 'succeeded' && run.outcome === 'complete'
511
- && run.execution?.runtimeVersion === VERSION && run.execution?.policyHash === expectedPolicy;
510
+ && isSupportedExecutionProfile(run.execution) && run.execution.policyHash === expectedPolicy;
512
511
  if (!validRun(buildRun, 'build') || !validRun(checkRun, 'verify') || !validRun(reviewRun, 'review') || !validRun(handoff, 'handoff'))
513
512
  throw new QueueError('Build, checks, review and approval do not match the current Factory policy.');
514
513
  const phaseEvidence = trustedPhaseEvidence(this.state, job, expectedPolicy,
@@ -6,6 +6,17 @@ import { usageFields } from './usage.mjs';
6
6
  import { effectiveInferenceProvider } from './model-environment.mjs';
7
7
  import { expectedWebStories, webPolicyHash } from './web-verification.mjs';
8
8
 
9
+ // Audited v1 protected evidence writers: 0.8.0 (380f749), 0.9.0 (cdaadef),
10
+ // and this patch. Deliberately independent of VERSION: a release bump is not
11
+ // evidence compatibility. Re-audit this list for every trust-relevant writer,
12
+ // isolation or validation change; remove versions whose guarantees no longer
13
+ // satisfy current policy. See docs/npm.md. This predicate alone grants no trust.
14
+ const SUPPORTED_EXECUTION_RUNTIMES_V1 = new Set(['0.8.0', '0.9.0', '0.9.1']);
15
+
16
+ export function isSupportedExecutionProfile(profile) {
17
+ return profile?.version === 1 && SUPPORTED_EXECUTION_RUNTIMES_V1.has(profile.runtimeVersion);
18
+ }
19
+
9
20
  export function withRequestedModel(configuration, requestedModel) {
10
21
  const config = structuredClone(configuration);
11
22
  if (requestedModel && requestedModel !== config.model) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "software-defence-factory",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Scoped software delivery and defence investigations with isolated jobs, evidence and review",
5
5
  "type": "module",
6
6
  "license": "MIT",