@skill-harness/adapters 0.13.0 → 0.15.0

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.
@@ -28,7 +28,7 @@ export declare function captureArchivedWorkCandidates(root: string, manifestId:
28
28
  acceptance: string;
29
29
  };
30
30
  /** Derive binding/coverage/acceptance fields from the real pinned P01 projection, not caller replacements. */
31
- export declare function captureArchivedWorkSignals(root: string, manifestId: string, context: WorkFrozen<WorkProjectionContext>, facts: WorkSignalFacts): {
31
+ export declare function captureArchivedWorkSignals(root: string, manifestId: string, context: WorkFrozen<WorkProjectionContext>, suppliedFacts: WorkSignalFacts | null): {
32
32
  linkageManifestId: string;
33
33
  version: string;
34
34
  bindingProfile: string;
@@ -36,6 +36,7 @@ export declare function captureArchivedWorkSignals(root: string, manifestId: str
36
36
  workSha256: string;
37
37
  producerCommit: string;
38
38
  projectionManifestId: string;
39
+ runtimeFactsManifestId: string;
39
40
  observationId: string;
40
41
  caseBatchId: string;
41
42
  candidateIds: string[];
@@ -32,11 +32,11 @@ export function captureArchivedWorkCandidates(root, manifestId, context, options
32
32
  return { ...linkage, linkageManifestId: stored.manifestId };
33
33
  }
34
34
  /** Derive binding/coverage/acceptance fields from the real pinned P01 projection, not caller replacements. */
35
- export function captureArchivedWorkSignals(root, manifestId, context, facts) {
35
+ export function captureArchivedWorkSignals(root, manifestId, context, suppliedFacts) {
36
36
  const read = readArchivedWork(root, manifestId, context);
37
37
  if (read.state !== "available" || read.projection.errors.length || read.projection.scopeState !== "valid" || !read.projection.selectedSnapshot || !read.projection.runtime)
38
38
  throw new Error("work source/selection incomplete; cannot capture signals");
39
- const work = read.projection, snapshotDigest = read.projection.selectedSnapshot.digest;
39
+ const work = read.projection, runtime = read.projection.runtime, snapshotDigest = read.projection.selectedSnapshot.digest;
40
40
  const snapshot = { snapshotDigest, scopeValid: true,
41
41
  obligations: work.obligations.map(o => ({ id: o.binding.obligation.id, digest: o.binding.obligation.digest,
42
42
  intentDigest: o.binding.intent.digest, policyDigest: o.binding.policy.digest, artifactDigest: o.binding.artifact?.digest ?? null,
@@ -44,6 +44,25 @@ export function captureArchivedWorkSignals(root, manifestId, context, facts) {
44
44
  coverage: [o.artifactCoverage.state, o.evidenceCoverage.state].includes("conflicted") ? "conflicted"
45
45
  : [o.artifactCoverage.state, o.evidenceCoverage.state].includes("unavailable") ? "unavailable"
46
46
  : o.artifactCoverage.state === "available" && o.evidenceCoverage.state === "available" && o.problems.every(p => p.code === "TRUSTED_REJECTION") ? "available" : "unknown" })) };
47
+ // Null means derive only facts the retained projection itself establishes. Empty arrays are explicit:
48
+ // runtime failure is not an objective violation, and absence cannot invent a deadline, expected wait,
49
+ // prior acceptance or authority. Coverage candidates still follow from the projected obligation.
50
+ const facts = suppliedFacts ?? { scopeDigest: snapshotDigest, version: "observed-work-v1", population: "retained-work",
51
+ expectedWaits: [], checkpoints: [], violations: [], priorAccepted: [] };
52
+ const runtimeFacts = {
53
+ version: "observed-work-runtime-v1", snapshotDigest, sourceManifestId: manifestId,
54
+ attempts: runtime.attempts.map(attempt => {
55
+ const eventDigests = runtime.occurrences.filter(occurrence => occurrence.payload.executionId === attempt.executionId &&
56
+ occurrence.payload.provenance === "observed" && ["completed", "failed", "cancelled"].includes(occurrence.payload.state))
57
+ .map(occurrence => occurrence.event.digest).sort();
58
+ return { executionId: attempt.executionId, state: attempt.state, resolution: attempt.resolution,
59
+ completionEvidence: { state: eventDigests.length ? "available" : "unavailable", eventDigests } };
60
+ }).sort((a, b) => a.executionId < b.executionId ? -1 : a.executionId > b.executionId ? 1 : 0),
61
+ obligations: snapshot.obligations.map(obligation => ({ obligationDigest: obligation.digest, acceptance: obligation.acceptance, coverage: obligation.coverage }))
62
+ .sort((a, b) => a.obligationDigest < b.obligationDigest ? -1 : a.obligationDigest > b.obligationDigest ? 1 : 0),
63
+ unavailable: ["checkpoint-evidence", "expected-wait-evidence", "prior-acceptance-history"],
64
+ };
65
+ const retainedRuntimeFacts = retainArchiveSource(root, { sourceId: `observed-runtime-${read.sourceSha256}`, parser: { id: "observed-work-runtime-facts", version: "1" }, retention: "exact", bytes: Buffer.from(JSON.stringify(runtimeFacts)) });
47
66
  // All four digests here are REVISION identities, not artifact content digests.
48
67
  // Duplicate obligation bindings cannot be collapsed by this input profile; its validator refuses them.
49
68
  const observation = retainWorkSignalObservation(root, snapshot, facts);
@@ -51,7 +70,7 @@ export function captureArchivedWorkSignals(root, manifestId, context, facts) {
51
70
  const cases = captureWorkSignalCases(root, observation.manifestId);
52
71
  const linkage = { version: "archived-work-signals-v1", bindingProfile: "work-v4-revision-digests-v1", workManifestId: manifestId,
53
72
  workSha256: read.sourceSha256, producerCommit: read.producerCommit, projectionManifestId: projection.manifestId,
54
- observationId: observation.manifestId, caseBatchId: cases.batchId, candidateIds: cases.candidateIds,
73
+ runtimeFactsManifestId: retainedRuntimeFacts.manifestId, observationId: observation.manifestId, caseBatchId: cases.batchId, candidateIds: cases.candidateIds,
55
74
  authorityBasis: "independently-supplied-host-context", acceptance: "not-assessed" };
56
75
  const stored = retainArchiveSource(root, { sourceId: `work-signal-link-${read.sourceSha256}`, parser: { id: "archived-work-signals", version: "1" }, retention: "exact", bytes: Buffer.from(JSON.stringify(linkage)) });
57
76
  return { ...linkage, linkageManifestId: stored.manifestId };
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export * from "./work-case-review.js";
18
18
  export * from "./archive-read-capability.js";
19
19
  export * from "./archived-work.js";
20
20
  export * from "./blind-intervention.js";
21
+ export * from "./learning-lifecycle.js";
21
22
  export * from "./archive-observer.js";
22
23
  export * from "./weekly-investigation.js";
23
24
  export * from "./intervention-run.js";
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ export * from "./work-case-review.js";
30
30
  export * from "./archive-read-capability.js";
31
31
  export * from "./archived-work.js";
32
32
  export * from "./blind-intervention.js";
33
+ export * from "./learning-lifecycle.js";
33
34
  export * from "./archive-observer.js";
34
35
  export * from "./weekly-investigation.js";
35
36
  export * from "./intervention-run.js";
@@ -0,0 +1,28 @@
1
+ export interface LearningLifecycleInput {
2
+ caseManifestId: string;
3
+ hypothesisManifestId: string;
4
+ comparisonManifestId: string;
5
+ choiceManifestId: string | null;
6
+ adoptionManifestId: string | null;
7
+ rollbackManifestId: string | null;
8
+ outcomeManifestIds: string[];
9
+ predecessorManifestId: string | null;
10
+ }
11
+ export type LearningLifecycleState = 'awaiting-human-choice' | 'decision-recorded' | 'adopted-awaiting-outcome' | 'rolled-back' | 'outcome-observed';
12
+ export interface LearningLifecycleView {
13
+ version: 'retained-learning-lifecycle-v1';
14
+ state: LearningLifecycleState;
15
+ predecessorManifestId: string | null;
16
+ links: {
17
+ case: string;
18
+ hypothesis: string;
19
+ comparison: string;
20
+ choice: string | null;
21
+ adoption: string | null;
22
+ rollback: string | null;
23
+ outcomes: string[];
24
+ };
25
+ }
26
+ /** Retains navigation only. Linked artifacts retain their own semantics and authority. */
27
+ export declare function retainLearningLifecycle(root: string, input: LearningLifecycleInput): string;
28
+ export declare function readLearningLifecycle(root: string, manifestId: string): LearningLifecycleView;
@@ -0,0 +1,66 @@
1
+ import { readArchiveSource, retainArchiveSource } from './evidence-archive.js';
2
+ const SHA = /^[a-f0-9]{64}$/;
3
+ const closed = (value, names) => !!value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).sort().join() === names.sort().join();
4
+ function available(root, id, parser) {
5
+ if (!SHA.test(id))
6
+ throw Error('invalid retained learning link');
7
+ const source = readArchiveSource(root, id);
8
+ if (source.status !== 'available' || source.reference.retention !== 'exact' || parser && source.reference.parser.id !== parser)
9
+ throw Error('retained learning link unavailable');
10
+ return source;
11
+ }
12
+ function validate(root, input, depth = 0) {
13
+ if (depth > 64)
14
+ throw Error('learning lifecycle predecessor bound exceeded');
15
+ if (!closed(input, ['caseManifestId', 'hypothesisManifestId', 'comparisonManifestId', 'choiceManifestId', 'adoptionManifestId', 'rollbackManifestId', 'outcomeManifestIds', 'predecessorManifestId']) || !Array.isArray(input.outcomeManifestIds) || input.outcomeManifestIds.length > 64 || new Set(input.outcomeManifestIds).size !== input.outcomeManifestIds.length)
16
+ throw Error('invalid bounded learning lifecycle');
17
+ for (const id of [input.caseManifestId, input.hypothesisManifestId, input.comparisonManifestId])
18
+ available(root, id);
19
+ for (const id of [input.choiceManifestId, input.adoptionManifestId, input.rollbackManifestId, ...input.outcomeManifestIds])
20
+ if (id !== null)
21
+ available(root, id);
22
+ if (input.adoptionManifestId && !input.choiceManifestId)
23
+ throw Error('quality choice required before adoption');
24
+ if (input.rollbackManifestId && !input.adoptionManifestId)
25
+ throw Error('adoption required before rollback');
26
+ if (input.outcomeManifestIds.length && !input.adoptionManifestId)
27
+ throw Error('adoption required before outcomes');
28
+ if (input.predecessorManifestId) {
29
+ const prior = read(root, input.predecessorManifestId, depth + 1);
30
+ if (prior.links.case !== input.caseManifestId || prior.links.hypothesis !== input.hypothesisManifestId || prior.links.comparison !== input.comparisonManifestId)
31
+ throw Error('learning lifecycle identity changed');
32
+ for (const key of ['choice', 'adoption', 'rollback'])
33
+ if (prior.links[key] !== null && prior.links[key] !== ({ choice: input.choiceManifestId, adoption: input.adoptionManifestId, rollback: input.rollbackManifestId })[key])
34
+ throw Error('learning lifecycle cannot regress or replace decisions');
35
+ if (prior.links.outcomes.some((id, index) => input.outcomeManifestIds[index] !== id))
36
+ throw Error('learning lifecycle cannot regress or replace outcomes');
37
+ }
38
+ const state = input.outcomeManifestIds.length ? 'outcome-observed' : input.rollbackManifestId ? 'rolled-back' : input.adoptionManifestId ? 'adopted-awaiting-outcome' : input.choiceManifestId ? 'decision-recorded' : 'awaiting-human-choice';
39
+ return { version: 'retained-learning-lifecycle-v1', state, predecessorManifestId: input.predecessorManifestId, links: { case: input.caseManifestId, hypothesis: input.hypothesisManifestId, comparison: input.comparisonManifestId, choice: input.choiceManifestId, adoption: input.adoptionManifestId, rollback: input.rollbackManifestId, outcomes: [...input.outcomeManifestIds] } };
40
+ }
41
+ /** Retains navigation only. Linked artifacts retain their own semantics and authority. */
42
+ export function retainLearningLifecycle(root, input) {
43
+ const view = validate(root, input);
44
+ return retainArchiveSource(root, { sourceId: `learning-${view.links.comparison.slice(0, 24)}`, parser: { id: 'learning-lifecycle', version: '1' }, retention: 'exact', bytes: Buffer.from(JSON.stringify(view)) }).manifestId;
45
+ }
46
+ function read(root, manifestId, depth) {
47
+ if (depth > 64)
48
+ throw Error('learning lifecycle predecessor bound exceeded');
49
+ const source = available(root, manifestId, 'learning-lifecycle');
50
+ let value;
51
+ try {
52
+ value = JSON.parse(source.bytes.toString('utf8'));
53
+ }
54
+ catch {
55
+ throw Error('invalid retained learning lifecycle');
56
+ }
57
+ if (!closed(value, ['version', 'state', 'predecessorManifestId', 'links']) || value.version !== 'retained-learning-lifecycle-v1' || !closed(value.links, ['case', 'hypothesis', 'comparison', 'choice', 'adoption', 'rollback', 'outcomes']))
58
+ throw Error('invalid retained learning lifecycle');
59
+ const links = value.links;
60
+ const rebuilt = validate(root, { caseManifestId: links.case, hypothesisManifestId: links.hypothesis, comparisonManifestId: links.comparison, choiceManifestId: links.choice, adoptionManifestId: links.adoption, rollbackManifestId: links.rollback, outcomeManifestIds: links.outcomes, predecessorManifestId: value.predecessorManifestId }, depth);
61
+ if (rebuilt.state !== value.state || JSON.stringify(rebuilt) !== source.bytes.toString('utf8'))
62
+ throw Error('retained learning lifecycle changed');
63
+ return Object.freeze({ ...rebuilt, links: Object.freeze({ ...rebuilt.links, outcomes: Object.freeze(rebuilt.links.outcomes) }) });
64
+ }
65
+ export function readLearningLifecycle(root, manifestId) { return read(root, manifestId, 0); }
66
+ //# sourceMappingURL=learning-lifecycle.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skill-harness/adapters",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "skill-harness harness adapters — pi runner + claude-code judge routing (internal API)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "prepack": "node ../../scripts/release-pack.mjs --guard"
41
41
  },
42
42
  "dependencies": {
43
- "@skill-harness/core": "0.13.0",
43
+ "@skill-harness/core": "0.15.0",
44
44
  "typebox": "1.3.4"
45
45
  }
46
46
  }