@haaaiawd/second-nature 0.2.12 → 0.2.13

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.
Files changed (59) hide show
  1. package/index.js +96 -6
  2. package/openclaw.plugin.json +1 -1
  3. package/package.json +1 -1
  4. package/runtime/cli/commands/index.js +85 -11
  5. package/runtime/cli/host-capability/host-discovery-port.d.ts +85 -0
  6. package/runtime/cli/host-capability/host-discovery-port.js +137 -0
  7. package/runtime/cli/ops/heartbeat-surface.d.ts +3 -3
  8. package/runtime/cli/ops/heartbeat-surface.js +6 -5
  9. package/runtime/cli/ops/ops-router.d.ts +6 -2
  10. package/runtime/cli/ops/ops-router.js +1273 -1145
  11. package/runtime/connectors/base/normalized-evidence-content.d.ts +4 -0
  12. package/runtime/connectors/base/normalized-evidence-content.js +21 -2
  13. package/runtime/connectors/evidence-normalizer.js +32 -1
  14. package/runtime/core/second-nature/action/action-closure-recorder.d.ts +2 -0
  15. package/runtime/core/second-nature/action/action-closure-recorder.js +49 -34
  16. package/runtime/core/second-nature/action/action-proposal-builder.js +3 -2
  17. package/runtime/core/second-nature/action/policy-bound-dispatch.d.ts +2 -0
  18. package/runtime/core/second-nature/action/policy-bound-dispatch.js +7 -3
  19. package/runtime/core/second-nature/control-plane/cycle-finalizer.d.ts +82 -0
  20. package/runtime/core/second-nature/control-plane/cycle-finalizer.js +187 -0
  21. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +13 -9
  22. package/runtime/core/second-nature/control-plane/real-runtime-spine.js +1 -1
  23. package/runtime/core/second-nature/guidance/guidance-proposal-consumer.d.ts +2 -1
  24. package/runtime/core/second-nature/guidance/guidance-proposal-consumer.js +4 -2
  25. package/runtime/core/second-nature/perception/judgment-engine.js +8 -4
  26. package/runtime/core/second-nature/perception/perception-builder.js +14 -2
  27. package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +30 -3
  28. package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.d.ts +5 -1
  29. package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.js +68 -29
  30. package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +2 -1
  31. package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +2 -1
  32. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +1 -0
  33. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +33 -0
  34. package/runtime/observability/causal-loop-health.d.ts +2 -1
  35. package/runtime/observability/causal-loop-health.js +7 -0
  36. package/runtime/observability/loop-stage-event-sink.js +6 -1
  37. package/runtime/observability/loop-status.d.ts +2 -0
  38. package/runtime/observability/loop-status.js +14 -1
  39. package/runtime/observability/services/heartbeat-digest-assembler.d.ts +3 -0
  40. package/runtime/observability/services/heartbeat-digest-assembler.js +9 -0
  41. package/runtime/shared/degraded-status-classifier.d.ts +16 -0
  42. package/runtime/shared/degraded-status-classifier.js +68 -0
  43. package/runtime/shared/evidence-level-classifier.d.ts +61 -0
  44. package/runtime/shared/evidence-level-classifier.js +116 -0
  45. package/runtime/shared/provenance-tier.d.ts +37 -0
  46. package/runtime/shared/provenance-tier.js +97 -0
  47. package/runtime/shared/setup-ack.d.ts +54 -0
  48. package/runtime/shared/setup-ack.js +108 -0
  49. package/runtime/shared/source-ref-compat.js +5 -2
  50. package/runtime/shared/types/v8-contracts.d.ts +13 -2
  51. package/runtime/storage/db/index.js +71 -28
  52. package/runtime/storage/db/migrations/v8-005-single-status-schema.js +2 -2
  53. package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.d.ts +9 -0
  54. package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.js +15 -0
  55. package/runtime/storage/db/schema/v8-entities.d.ts +76 -0
  56. package/runtime/storage/db/schema/v8-entities.js +4 -0
  57. package/runtime/storage/services/write-validation-gate.js +1 -1
  58. package/runtime/storage/v8-state-stores.d.ts +7 -2
  59. package/runtime/storage/v8-state-stores.js +37 -19
@@ -21,6 +21,7 @@
21
21
  * Test coverage: tests/unit/observability/loop-stage-event-sink.test.ts
22
22
  */
23
23
  import { writeLoopStageEvent } from "../storage/v8-state-stores.js";
24
+ import { classifyDegradedStatus } from "../shared/degraded-status-classifier.js";
24
25
  function validateEvent(event) {
25
26
  if (!event.cycleId || event.cycleId.trim().length === 0) {
26
27
  return { ok: false, reason: "cycle_id_required", field: "cycleId" };
@@ -100,7 +101,7 @@ export async function recordLoopStageEvent(db, event, options) {
100
101
  const validation = validateEvent(event);
101
102
  if (!validation.ok) {
102
103
  const degraded = {
103
- status: "degraded",
104
+ status: classifyDegradedStatus("stage_event_missing"),
104
105
  reason: "stage_event_missing",
105
106
  ownerStage: event.stage || "ingestion",
106
107
  sourceRefs: event.sourceRefs || [],
@@ -111,6 +112,8 @@ export async function recordLoopStageEvent(db, event, options) {
111
112
  }
112
113
  const now = options?.now ?? new Date().toISOString();
113
114
  const sourceRefs = event.sourceRefs ?? [];
115
+ const proofRefs = event.proofRefs ?? [];
116
+ const traceRefs = event.traceRefs ?? [];
114
117
  const { redacted: redactedRefs, redactionClass } = redactSourceRefs(sourceRefs);
115
118
  const record = {
116
119
  id: event.id ?? `evt_${now.replace(/[:.]/g, "")}_${event.cycleId}_${event.stage}`,
@@ -120,6 +123,8 @@ export async function recordLoopStageEvent(db, event, options) {
120
123
  status: event.status,
121
124
  reason: event.reason,
122
125
  sourceRefs: redactedRefs,
126
+ proofRefs,
127
+ traceRefs,
123
128
  redactionClass,
124
129
  occurredAt: event.occurredAt,
125
130
  expectedDownstreamByCycle: event.expectedDownstreamByCycle,
@@ -20,6 +20,7 @@
20
20
  */
21
21
  import type { StateDatabase } from "../storage/db/index.js";
22
22
  import type { DegradedOperationResult } from "../shared/types/v8-contracts.js";
23
+ import type { EvidenceLevel } from "../shared/types/v8-contracts.js";
23
24
  export interface RealRunHealthProjection {
24
25
  gatePassed: boolean;
25
26
  contractSmokeOnly: boolean;
@@ -47,6 +48,7 @@ export interface LoopStatusReadModel {
47
48
  connectorTerminalCount: number;
48
49
  nextAction: string;
49
50
  realRunHealth: RealRunHealthProjection;
51
+ evidenceLevel: EvidenceLevel;
50
52
  }
51
53
  export interface StageSummary {
52
54
  stage: string;
@@ -21,6 +21,7 @@
21
21
  import { assembleLoopStatus } from "./causal-loop-health.js";
22
22
  import { checkRealRunHealth } from "./living-loop-health-gate.js";
23
23
  import { readActionClosuresByDay, readConnectorCooldownState, } from "../storage/v8-state-stores.js";
24
+ import { classifyEvidenceLevel } from "../shared/evidence-level-classifier.js";
24
25
  // ───────────────────────────────────────────────────────────────
25
26
  // Helpers
26
27
  // ───────────────────────────────────────────────────────────────
@@ -163,7 +164,7 @@ export async function attributeDenials(db, options) {
163
164
  // ───────────────────────────────────────────────────────────────
164
165
  export async function readLoopStatus(db) {
165
166
  const health = await assembleLoopStatus(db, { limit: 50 });
166
- if ("status" in health && health.status === "degraded") {
167
+ if ("ownerStage" in health) {
167
168
  return {
168
169
  ok: false,
169
170
  degraded: health,
@@ -214,6 +215,17 @@ export async function readLoopStatus(db) {
214
215
  overallStatus = "healthy";
215
216
  stalledAt = undefined;
216
217
  }
218
+ // T-OBS.R.7: derive evidenceLevel from observed proof
219
+ const evidenceLevel = classifyEvidenceLevel({
220
+ hasCarrierEnvelope: true, // loop_status command envelope itself
221
+ hasContractSmoke: snapshot.lastCycleSequence > 0 && !realRunHealth.hasRealClosure,
222
+ hasStatePresent: snapshot.lastCycleSequence === 0 &&
223
+ (realRunHealth.hasQuietArtifact ||
224
+ realRunHealth.hasDreamArtifact ||
225
+ realRunHealth.hasProjectionFeedback),
226
+ hasCycleExecution: snapshot.lastCycleSequence > 0 && realRunHealth.hasRealClosure,
227
+ hasReadbackVerification: realRunHealth.gatePassed,
228
+ });
217
229
  const stageSummaries = snapshot.stages.map((s) => ({
218
230
  stage: s.stage,
219
231
  eventCount: s.eventCount,
@@ -240,6 +252,7 @@ export async function readLoopStatus(db) {
240
252
  connectorTerminalCount: attribution.connectorTerminalCount,
241
253
  nextAction,
242
254
  realRunHealth,
255
+ evidenceLevel,
243
256
  },
244
257
  };
245
258
  }
@@ -32,6 +32,7 @@
32
32
  */
33
33
  import type { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
34
34
  import type { StateDatabase } from "../../storage/db/index.js";
35
+ import type { EvidenceLevel } from "../../shared/types/v8-contracts.js";
35
36
  export interface ConnectorDaySummary {
36
37
  platformId: string;
37
38
  capability: string;
@@ -91,6 +92,8 @@ export interface HeartbeatDigest {
91
92
  healthSummary: HealthDaySummary;
92
93
  /** Real-run health gate result (T-OBS.R.3) */
93
94
  realRunHealth: RealRunHealthDigestProjection;
95
+ /** T-OBS.R.7: evidence level for this digest */
96
+ evidenceLevel: EvidenceLevel;
94
97
  /** Set when delivery succeeded */
95
98
  deliveredAt?: string;
96
99
  /** Proof of successful delivery (channel + message hash, no raw content) */
@@ -31,6 +31,7 @@
31
31
  * tests/integration/observability/digest-delivery.test.ts (T-OBS.C.4)
32
32
  */
33
33
  import { checkRealRunHealth } from "../living-loop-health-gate.js";
34
+ import { classifyEvidenceLevel } from "../../shared/evidence-level-classifier.js";
34
35
  // ─── Helpers ─────────────────────────────────────────────────────────────────
35
36
  function isSameDayUtc(isoTimestamp, dateStr) {
36
37
  // dateStr: "YYYY-MM-DD"
@@ -262,6 +263,13 @@ export async function generateHeartbeatDigest(date, deps) {
262
263
  };
263
264
  }
264
265
  }
266
+ const evidenceLevel = classifyEvidenceLevel({
267
+ hasCarrierEnvelope: true,
268
+ hasContractSmoke: !deps.db,
269
+ hasStatePresent: Boolean(deps.db) && !realRunHealth.gatePassed && !realRunHealth.hasRealClosure,
270
+ hasCycleExecution: realRunHealth.hasRealClosure,
271
+ hasReadbackVerification: realRunHealth.gatePassed,
272
+ });
265
273
  const digest = {
266
274
  date,
267
275
  generatedAt,
@@ -271,6 +279,7 @@ export async function generateHeartbeatDigest(date, deps) {
271
279
  quietDreamSummary,
272
280
  healthSummary,
273
281
  realRunHealth,
282
+ evidenceLevel,
274
283
  };
275
284
  // T-OBS.C.4: delivery hook — attempt delivery if adapter is provided
276
285
  if (deliveryAdapter) {
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Degraded Status Classifier (T-OBS.R.8)
3
+ *
4
+ * Core logic: map canonical V8ReasonCode values to precise operational states
5
+ * so that stage-level diagnostics never use the aggregate "degraded" string.
6
+ *
7
+ * Design authority:
8
+ * - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md §4.1`
9
+ *
10
+ * Dependencies: `src/shared/types/v8-contracts.js`
11
+ * Boundary: pure function; no I/O.
12
+ * Test coverage: tests/unit/shared/degraded-status-classifier.test.ts
13
+ */
14
+ import type { DegradedOperationResult, V8ReasonCode } from "./types/v8-contracts.js";
15
+ export type PreciseDegradedStatus = DegradedOperationResult["status"];
16
+ export declare function classifyDegradedStatus(reason: V8ReasonCode): PreciseDegradedStatus;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Degraded Status Classifier (T-OBS.R.8)
3
+ *
4
+ * Core logic: map canonical V8ReasonCode values to precise operational states
5
+ * so that stage-level diagnostics never use the aggregate "degraded" string.
6
+ *
7
+ * Design authority:
8
+ * - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md §4.1`
9
+ *
10
+ * Dependencies: `src/shared/types/v8-contracts.js`
11
+ * Boundary: pure function; no I/O.
12
+ * Test coverage: tests/unit/shared/degraded-status-classifier.test.ts
13
+ */
14
+ const EMPTY_REASONS = new Set([
15
+ "evidence_batch_empty",
16
+ "evidence_content_missing",
17
+ "ingestion_empty",
18
+ "ingestion_no_data",
19
+ "quiet_empty_input",
20
+ ]);
21
+ const BLOCKED_REASONS = new Set([
22
+ "source_refs_unresolved",
23
+ "proposal_missing_source_refs",
24
+ "judgment_missing_source_refs",
25
+ "quiet_redaction_blocked",
26
+ "dream_blocked_redaction",
27
+ "dream_blocked_no_content",
28
+ "dream_blocked_private_redacted",
29
+ "dream_blocked_credential",
30
+ "dream_blocked_validation_failed",
31
+ "dream_interval_active",
32
+ ]);
33
+ const UNSAFE_REASONS = new Set([
34
+ "policy_denied_high_risk",
35
+ "policy_denied_breaker_open",
36
+ "closure_idempotency_conflict",
37
+ ]);
38
+ const PARTIAL_REASONS = new Set([
39
+ "evidence_batch_truncated",
40
+ "perception_rules_only",
41
+ "dream_rules_only",
42
+ "dream_model_timeout",
43
+ "quiet_validation_failed",
44
+ "closure_downgraded_without_draft",
45
+ ]);
46
+ const UNAVAILABLE_REASONS = new Set([
47
+ "state_unreadable",
48
+ "quiet_state_unreadable",
49
+ "ingestion_state_unreadable",
50
+ "stage_event_missing",
51
+ "execution_unavailable",
52
+ "guidance_unavailable",
53
+ "dream_scheduler_unavailable",
54
+ "policy_denied_missing_permission",
55
+ "closure_failed",
56
+ "closure_unavailable",
57
+ ]);
58
+ export function classifyDegradedStatus(reason) {
59
+ if (EMPTY_REASONS.has(reason))
60
+ return "empty";
61
+ if (BLOCKED_REASONS.has(reason))
62
+ return "blocked";
63
+ if (UNSAFE_REASONS.has(reason))
64
+ return "unsafe";
65
+ if (PARTIAL_REASONS.has(reason))
66
+ return "partial";
67
+ return "unavailable";
68
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * EvidenceLevelClassifier — Monotonic evidence-level taxonomy for operator-facing surfaces.
3
+ *
4
+ * Core logic: classify how strongly a command/response is backed by runtime proof.
5
+ * Levels are ordered from weakest (carrier_ack) to strongest (durable_verified).
6
+ * A level may only stay the same or increase within one command; synthetic/carrier
7
+ * proofs can never be promoted to real_runtime or durable_verified.
8
+ *
9
+ * Design authority:
10
+ * - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md §4.2, §4.3`
11
+ * - `.anws/v8/04_SYSTEM_DESIGN/runtime-ops-system.md §2, §3.3`
12
+ * - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §6.1`
13
+ *
14
+ * Dependencies: `src/shared/types/v8-contracts.js` (EvidenceLevel)
15
+ * Boundary: Pure classification functions; no storage or side effects.
16
+ * Test coverage: tests/unit/shared/evidence-level-classifier.test.ts
17
+ */
18
+ import type { EvidenceLevel } from "./types/v8-contracts.js";
19
+ export type { EvidenceLevel };
20
+ export declare const EVIDENCE_LEVEL_ORDER: Record<EvidenceLevel, number>;
21
+ export interface EvidenceLevelProofInput {
22
+ /** Host/plugin/CLI produced an envelope but no Second Nature contract path ran. */
23
+ hasCarrierEnvelope?: boolean;
24
+ /** Static/fixture contract path ran without proving live state mutation. */
25
+ hasContractSmoke?: boolean;
26
+ /** Durable state was read or existing rows were observed. */
27
+ hasStatePresent?: boolean;
28
+ /** Current v8 living-loop command executed and produced stage + closure proof. */
29
+ hasCycleExecution?: boolean;
30
+ /** real_runtime proof was persisted and read back through normal read model. */
31
+ hasReadbackVerification?: boolean;
32
+ }
33
+ /**
34
+ * Classify evidence level from observed proof flags.
35
+ * Returns the strongest level whose required proof is present.
36
+ */
37
+ export declare function classifyEvidenceLevel(input: EvidenceLevelProofInput): EvidenceLevel;
38
+ /**
39
+ * Cap a candidate level by a maximum allowed level.
40
+ * Used to prevent carrier/smoke proofs from masquerading as real runtime health.
41
+ */
42
+ export declare function capEvidenceLevel(candidate: EvidenceLevel, cap: EvidenceLevel): EvidenceLevel;
43
+ /**
44
+ * Promote a current level to a target level only if target is strictly stronger
45
+ * and promotion is supported by supplied proof.
46
+ */
47
+ export declare function promoteEvidenceLevel(current: EvidenceLevel, target: EvidenceLevel, proof: EvidenceLevelProofInput): EvidenceLevel;
48
+ /**
49
+ * Aggregate multiple stage evidence levels into the minimum (weakest) level.
50
+ * A chain is only as strong as its weakest proven stage.
51
+ */
52
+ export declare function minEvidenceLevel(levels: EvidenceLevel[]): EvidenceLevel;
53
+ /**
54
+ * Convenience: the strongest level achievable from the input proof set,
55
+ * but never exceeding a hard cap.
56
+ */
57
+ export declare function classifyAndCapEvidenceLevel(input: EvidenceLevelProofInput, cap: EvidenceLevel): EvidenceLevel;
58
+ /**
59
+ * Map an evidence level to a human-readable operator note.
60
+ */
61
+ export declare function evidenceLevelDescription(level: EvidenceLevel): string;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * EvidenceLevelClassifier — Monotonic evidence-level taxonomy for operator-facing surfaces.
3
+ *
4
+ * Core logic: classify how strongly a command/response is backed by runtime proof.
5
+ * Levels are ordered from weakest (carrier_ack) to strongest (durable_verified).
6
+ * A level may only stay the same or increase within one command; synthetic/carrier
7
+ * proofs can never be promoted to real_runtime or durable_verified.
8
+ *
9
+ * Design authority:
10
+ * - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md §4.2, §4.3`
11
+ * - `.anws/v8/04_SYSTEM_DESIGN/runtime-ops-system.md §2, §3.3`
12
+ * - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §6.1`
13
+ *
14
+ * Dependencies: `src/shared/types/v8-contracts.js` (EvidenceLevel)
15
+ * Boundary: Pure classification functions; no storage or side effects.
16
+ * Test coverage: tests/unit/shared/evidence-level-classifier.test.ts
17
+ */
18
+ export const EVIDENCE_LEVEL_ORDER = {
19
+ carrier_ack: 0,
20
+ contract_smoke: 1,
21
+ state_present: 2,
22
+ real_runtime: 3,
23
+ durable_verified: 4,
24
+ };
25
+ const LEVELS_BY_ORDER = [
26
+ "carrier_ack",
27
+ "contract_smoke",
28
+ "state_present",
29
+ "real_runtime",
30
+ "durable_verified",
31
+ ];
32
+ /**
33
+ * Classify evidence level from observed proof flags.
34
+ * Returns the strongest level whose required proof is present.
35
+ */
36
+ export function classifyEvidenceLevel(input) {
37
+ if (input.hasReadbackVerification && input.hasCycleExecution) {
38
+ return "durable_verified";
39
+ }
40
+ if (input.hasCycleExecution) {
41
+ return "real_runtime";
42
+ }
43
+ if (input.hasStatePresent) {
44
+ return "state_present";
45
+ }
46
+ if (input.hasContractSmoke) {
47
+ return "contract_smoke";
48
+ }
49
+ if (input.hasCarrierEnvelope) {
50
+ return "carrier_ack";
51
+ }
52
+ // Default: carrier_ack when an envelope exists but no stronger proof was supplied.
53
+ return "carrier_ack";
54
+ }
55
+ /**
56
+ * Cap a candidate level by a maximum allowed level.
57
+ * Used to prevent carrier/smoke proofs from masquerading as real runtime health.
58
+ */
59
+ export function capEvidenceLevel(candidate, cap) {
60
+ if (EVIDENCE_LEVEL_ORDER[candidate] <= EVIDENCE_LEVEL_ORDER[cap]) {
61
+ return candidate;
62
+ }
63
+ return cap;
64
+ }
65
+ /**
66
+ * Promote a current level to a target level only if target is strictly stronger
67
+ * and promotion is supported by supplied proof.
68
+ */
69
+ export function promoteEvidenceLevel(current, target, proof) {
70
+ const classified = classifyEvidenceLevel(proof);
71
+ if (EVIDENCE_LEVEL_ORDER[classified] >= EVIDENCE_LEVEL_ORDER[target]) {
72
+ return capEvidenceLevel(target, classified);
73
+ }
74
+ return current;
75
+ }
76
+ /**
77
+ * Aggregate multiple stage evidence levels into the minimum (weakest) level.
78
+ * A chain is only as strong as its weakest proven stage.
79
+ */
80
+ export function minEvidenceLevel(levels) {
81
+ if (levels.length === 0)
82
+ return "carrier_ack";
83
+ let min = levels[0];
84
+ for (const level of levels) {
85
+ if (EVIDENCE_LEVEL_ORDER[level] < EVIDENCE_LEVEL_ORDER[min]) {
86
+ min = level;
87
+ }
88
+ }
89
+ return min;
90
+ }
91
+ /**
92
+ * Convenience: the strongest level achievable from the input proof set,
93
+ * but never exceeding a hard cap.
94
+ */
95
+ export function classifyAndCapEvidenceLevel(input, cap) {
96
+ return capEvidenceLevel(classifyEvidenceLevel(input), cap);
97
+ }
98
+ /**
99
+ * Map an evidence level to a human-readable operator note.
100
+ */
101
+ export function evidenceLevelDescription(level) {
102
+ switch (level) {
103
+ case "carrier_ack":
104
+ return "Host/plugin returned an envelope but no Second Nature contract path ran.";
105
+ case "contract_smoke":
106
+ return "Static or fixture contract path passed without live state mutation.";
107
+ case "state_present":
108
+ return "Durable state exists or was read, but no current cycle executed.";
109
+ case "real_runtime":
110
+ return "Current v8 living-loop command executed and produced stage/closure proof.";
111
+ case "durable_verified":
112
+ return "Real runtime proof persisted and read back through the normal read model.";
113
+ default:
114
+ return "Unknown evidence level.";
115
+ }
116
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Provenance Tier Validator (T-SH.R.6)
3
+ *
4
+ * Core logic: enforce that real domain evidence lives in `sourceRefs`,
5
+ * runtime/policy/setup/host/packaging proofs live in `proofRefs`, and
6
+ * observability/audit/stage-event traces live in `traceRefs`.
7
+ *
8
+ * Design authority:
9
+ * - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md §2.2`
10
+ *
11
+ * Dependencies: `src/shared/types/v8-contracts.js`
12
+ * Boundary: pure functions; no I/O.
13
+ * Test coverage: tests/unit/shared/provenance-tier.test.ts
14
+ */
15
+ import type { SourceRef, ProvenanceBundle } from "./types/v8-contracts.js";
16
+ export interface ProvenanceValidationError {
17
+ ok: false;
18
+ field: "sourceRefs" | "proofRefs" | "traceRefs";
19
+ ref: SourceRef;
20
+ reason: string;
21
+ }
22
+ export type ProvenanceValidationResult = {
23
+ ok: true;
24
+ bundle: ProvenanceBundle;
25
+ } | {
26
+ ok: false;
27
+ errors: ProvenanceValidationError[];
28
+ };
29
+ export declare function validateProvenanceTiers(bundle: ProvenanceBundle): ProvenanceValidationResult;
30
+ export declare function buildClosureProvenance(input: {
31
+ sourceRefs?: SourceRef[];
32
+ proofRefs?: SourceRef[];
33
+ traceRefs?: SourceRef[];
34
+ }): ProvenanceBundle;
35
+ export declare function cycleTraceRef(cycleId: string): SourceRef;
36
+ export declare function closureTraceRef(closureId: string): SourceRef;
37
+ export declare function decisionProofRef(decisionId: string): SourceRef;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Provenance Tier Validator (T-SH.R.6)
3
+ *
4
+ * Core logic: enforce that real domain evidence lives in `sourceRefs`,
5
+ * runtime/policy/setup/host/packaging proofs live in `proofRefs`, and
6
+ * observability/audit/stage-event traces live in `traceRefs`.
7
+ *
8
+ * Design authority:
9
+ * - `.anws/v8/04_SYSTEM_DESIGN/shared-v8-contracts.md §2.2`
10
+ *
11
+ * Dependencies: `src/shared/types/v8-contracts.js`
12
+ * Boundary: pure functions; no I/O.
13
+ * Test coverage: tests/unit/shared/provenance-tier.test.ts
14
+ */
15
+ const PROOF_FAMILIES = new Set([
16
+ "action_closure",
17
+ "audit",
18
+ "connector_result",
19
+ ]);
20
+ const TRACE_FAMILIES = new Set([
21
+ "audit",
22
+ ]);
23
+ function isSyntheticProof(ref) {
24
+ return (ref.uri.startsWith("sn://closure/") ||
25
+ ref.uri.startsWith("sn://decision/") ||
26
+ ref.uri.startsWith("sn://policy/") ||
27
+ ref.uri.startsWith("sn://execution/") ||
28
+ ref.uri.startsWith("sn://cycle/") ||
29
+ ref.uri.startsWith("sn://host/") ||
30
+ ref.uri.startsWith("sn://setup/") ||
31
+ PROOF_FAMILIES.has(ref.family));
32
+ }
33
+ function isTraceRef(ref) {
34
+ return (ref.uri.startsWith("sn://cycle/") ||
35
+ ref.uri.startsWith("sn://event/") ||
36
+ TRACE_FAMILIES.has(ref.family));
37
+ }
38
+ export function validateProvenanceTiers(bundle) {
39
+ const errors = [];
40
+ for (const ref of bundle.sourceRefs) {
41
+ if (isSyntheticProof(ref)) {
42
+ errors.push({
43
+ ok: false,
44
+ field: "sourceRefs",
45
+ ref,
46
+ reason: "Synthetic proof/trace ref must not live in sourceRefs",
47
+ });
48
+ }
49
+ }
50
+ for (const ref of bundle.proofRefs) {
51
+ if (isTraceRef(ref)) {
52
+ errors.push({
53
+ ok: false,
54
+ field: "proofRefs",
55
+ ref,
56
+ reason: "Trace ref must live in traceRefs, not proofRefs",
57
+ });
58
+ }
59
+ }
60
+ return errors.length === 0
61
+ ? { ok: true, bundle }
62
+ : { ok: false, errors };
63
+ }
64
+ export function buildClosureProvenance(input) {
65
+ return {
66
+ sourceRefs: input.sourceRefs ?? [],
67
+ proofRefs: input.proofRefs ?? [],
68
+ traceRefs: input.traceRefs ?? [],
69
+ };
70
+ }
71
+ export function cycleTraceRef(cycleId) {
72
+ return {
73
+ uri: `sn://cycle/${cycleId}`,
74
+ family: "audit",
75
+ id: cycleId,
76
+ redactionClass: "none",
77
+ resolveStatus: "resolvable",
78
+ };
79
+ }
80
+ export function closureTraceRef(closureId) {
81
+ return {
82
+ uri: `sn://closure/${closureId}`,
83
+ family: "action_closure",
84
+ id: closureId,
85
+ redactionClass: "none",
86
+ resolveStatus: "resolvable",
87
+ };
88
+ }
89
+ export function decisionProofRef(decisionId) {
90
+ return {
91
+ uri: `sn://decision/${decisionId}`,
92
+ family: "action_closure",
93
+ id: decisionId,
94
+ redactionClass: "none",
95
+ resolveStatus: "resolvable",
96
+ };
97
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Setup Ack Truth Contract (T-ROS.R.8)
3
+ *
4
+ * Core logic: define the canonical SetupAck schema and a validator that rejects
5
+ * `placedIn: "unspecified"`, missing fields, unknown writers, and hand-written
6
+ * files that do not satisfy the schema.
7
+ *
8
+ * Design authority:
9
+ * - `.anws/v8/04_SYSTEM_DESIGN/runtime-ops-system.md §3.2`
10
+ *
11
+ * Dependencies: none (plain validation to keep plugin load lightweight)
12
+ * Boundary: Pure validation; no I/O.
13
+ * Test coverage: tests/unit/shared/setup-ack-validator.test.ts
14
+ */
15
+ export declare const SETUP_ACK_SCHEMA_VERSION = 1;
16
+ export type SetupAckPlacement = "workspace_guide" | "host_skill_registry" | "agent_profile" | "manual_operator_instruction";
17
+ export type SetupAckWriter = "setup_ack_command" | "host_setup_bridge";
18
+ export interface SetupAck {
19
+ schemaVersion: typeof SETUP_ACK_SCHEMA_VERSION;
20
+ acknowledgedAt: string;
21
+ placedIn: SetupAckPlacement;
22
+ placementProofRef: string;
23
+ writer: SetupAckWriter;
24
+ hostName?: string;
25
+ hostVersion?: string;
26
+ acceptedBy?: string;
27
+ note?: string;
28
+ }
29
+ export interface SetupAckValidationResult {
30
+ ok: true;
31
+ ack: SetupAck;
32
+ }
33
+ export interface SetupAckValidationError {
34
+ ok: false;
35
+ field: string;
36
+ reason: string;
37
+ repairAction: string;
38
+ }
39
+ export type ValidateSetupAckResult = SetupAckValidationResult | {
40
+ ok: false;
41
+ errors: SetupAckValidationError[];
42
+ };
43
+ export declare function validateSetupAck(raw: Record<string, unknown>): ValidateSetupAckResult;
44
+ /**
45
+ * Check whether a raw marker object can be considered a complete ack.
46
+ * Hand-written files are treated as incomplete until verified.
47
+ */
48
+ export declare function isSetupAckComplete(raw: Record<string, unknown>): {
49
+ complete: true;
50
+ ack: SetupAck;
51
+ } | {
52
+ complete: false;
53
+ errors: SetupAckValidationError[];
54
+ };