@haaaiawd/second-nature 0.2.1 → 0.2.4
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/runtime/cli/index.js +5 -1
- package/runtime/cli/ops/heartbeat-surface.d.ts +23 -0
- package/runtime/cli/ops/heartbeat-surface.js +73 -1
- package/runtime/cli/ops/manual-run-dispatcher.d.ts +2 -0
- package/runtime/cli/ops/manual-run-dispatcher.js +10 -0
- package/runtime/cli/ops/ops-router.js +117 -31
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +3 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.js +2 -0
- package/runtime/connectors/base/contract.d.ts +10 -0
- package/runtime/connectors/base/policy-bound-write-dispatch.d.ts +29 -0
- package/runtime/connectors/base/policy-bound-write-dispatch.js +127 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +336 -25
- package/runtime/core/second-nature/control-plane/real-runtime-spine.d.ts +33 -0
- package/runtime/core/second-nature/control-plane/real-runtime-spine.js +41 -0
- package/runtime/core/second-nature/guidance/impulse-context-reader.d.ts +44 -0
- package/runtime/core/second-nature/guidance/impulse-context-reader.js +84 -0
- package/runtime/core/second-nature/guidance/impulse-context-writer.d.ts +39 -0
- package/runtime/core/second-nature/guidance/impulse-context-writer.js +70 -0
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +6 -1
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +11 -0
- package/runtime/core/second-nature/perception/judgment-engine.d.ts +2 -0
- package/runtime/core/second-nature/perception/judgment-engine.js +11 -1
- package/runtime/core/second-nature/perception/perception-builder.d.ts +6 -2
- package/runtime/core/second-nature/perception/perception-builder.js +18 -7
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +3 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +42 -1
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.d.ts +43 -0
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +157 -0
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +17 -16
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +3 -0
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +4 -0
- package/runtime/observability/living-loop-health-gate.d.ts +45 -0
- package/runtime/observability/living-loop-health-gate.js +94 -0
- package/runtime/observability/loop-status.d.ts +11 -0
- package/runtime/observability/loop-status.js +49 -3
- package/runtime/observability/services/audit-closure-recorders.d.ts +31 -0
- package/runtime/observability/services/audit-closure-recorders.js +87 -0
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +12 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +22 -3
- package/runtime/shared/types/v8-contracts.d.ts +2 -2
- package/runtime/storage/db/index.js +34 -0
- package/runtime/storage/db/migrations/index.js +4 -0
- package/runtime/storage/db/migrations/v8-001-living-perception-loop.js +119 -119
- package/runtime/storage/db/migrations/v8-002-perception-contract-alignment.d.ts +12 -0
- package/runtime/storage/db/migrations/v8-002-perception-contract-alignment.js +14 -0
- package/runtime/storage/db/migrations/v8-003-quiet-closure-refs.d.ts +10 -0
- package/runtime/storage/db/migrations/v8-003-quiet-closure-refs.js +12 -0
- package/runtime/storage/db/schema/v8-entities.d.ts +586 -0
- package/runtime/storage/db/schema/v8-entities.js +39 -0
- package/runtime/storage/v8-state-stores.d.ts +32 -2
- package/runtime/storage/v8-state-stores.js +121 -2
|
@@ -68,6 +68,8 @@ export async function buildQuietDailyReview(db, options) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
const sourceRefs = closures.map(buildSourceRefFromClosure);
|
|
71
|
+
// T-DQ.R.4: first-class closure refs — identical to sourceRefs here, but explicitly typed
|
|
72
|
+
const closureRefs = closures.map(buildSourceRefFromClosure);
|
|
71
73
|
// Collect memory-review candidates from closure payloads
|
|
72
74
|
const memoryCandidates = [];
|
|
73
75
|
for (const closure of closures) {
|
|
@@ -96,6 +98,7 @@ export async function buildQuietDailyReview(db, options) {
|
|
|
96
98
|
closureCount: closures.length,
|
|
97
99
|
memoryCandidateCount: memoryCandidates.length,
|
|
98
100
|
sourceRefs,
|
|
101
|
+
closureRefs,
|
|
99
102
|
redactionClass: "none",
|
|
100
103
|
lifecycleStatus: "pending",
|
|
101
104
|
payloadJson: JSON.stringify({
|
|
@@ -115,6 +118,7 @@ export async function buildQuietDailyReview(db, options) {
|
|
|
115
118
|
closureCount: closures.length,
|
|
116
119
|
memoryCandidateCount: memoryCandidates.length,
|
|
117
120
|
sourceRefs,
|
|
121
|
+
closureRefs,
|
|
118
122
|
reviewSummary,
|
|
119
123
|
importanceSignals,
|
|
120
124
|
createdAt: now,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LivingLoopHealthGate — Distinguish contract-smoke from real runtime activity.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Check for persisted ActionClosureRecord, QuietDailyReview,
|
|
5
|
+
* and DreamConsolidationRun to determine if the living loop has real
|
|
6
|
+
* evidence or is only passing contract smoke tests.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §4.2`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/control-plane-system.md §4`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readActionClosuresByDay, readDailyRhythmStateByDay)
|
|
14
|
+
*
|
|
15
|
+
* Boundary:
|
|
16
|
+
* - Read-only diagnostic; does not modify state.
|
|
17
|
+
* - Reports explicit absence reasons instead of silent zeros.
|
|
18
|
+
*/
|
|
19
|
+
import type { StateDatabase } from "../storage/db/index.js";
|
|
20
|
+
import type { DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
21
|
+
export interface RealRunHealthGate {
|
|
22
|
+
/** Has at least one real ActionClosureRecord */
|
|
23
|
+
hasRealClosure: boolean;
|
|
24
|
+
/** Has a completed QuietDailyReview */
|
|
25
|
+
hasQuietArtifact: boolean;
|
|
26
|
+
/** Has a scheduled or completed DreamConsolidationRun */
|
|
27
|
+
hasDreamArtifact: boolean;
|
|
28
|
+
/** True if only contract smoke (cycle traces) but no real artifacts */
|
|
29
|
+
contractSmokeOnly: boolean;
|
|
30
|
+
/** True if closure exists but no runtime-produced cycle trace backs it */
|
|
31
|
+
seededStateDetected: boolean;
|
|
32
|
+
/** True only when real runtime activity is proven (not seeded, not smoke-only) */
|
|
33
|
+
gatePassed: boolean;
|
|
34
|
+
/** Explicit missing stage reason */
|
|
35
|
+
missingStage?: "closure" | "quiet" | "dream" | "none";
|
|
36
|
+
missingReason?: string;
|
|
37
|
+
}
|
|
38
|
+
export type RealRunHealthResult = {
|
|
39
|
+
ok: true;
|
|
40
|
+
gate: RealRunHealthGate;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
degraded: DegradedOperationResult;
|
|
44
|
+
};
|
|
45
|
+
export declare function checkRealRunHealth(db: StateDatabase, day?: string): Promise<RealRunHealthResult>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LivingLoopHealthGate — Distinguish contract-smoke from real runtime activity.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Check for persisted ActionClosureRecord, QuietDailyReview,
|
|
5
|
+
* and DreamConsolidationRun to determine if the living loop has real
|
|
6
|
+
* evidence or is only passing contract smoke tests.
|
|
7
|
+
*
|
|
8
|
+
* Design authority:
|
|
9
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/observability-health-system.md §4.2`
|
|
10
|
+
* - `.anws/v8/04_SYSTEM_DESIGN/control-plane-system.md §4`
|
|
11
|
+
*
|
|
12
|
+
* Dependencies:
|
|
13
|
+
* - `src/storage/v8-state-stores.js` (readActionClosuresByDay, readDailyRhythmStateByDay)
|
|
14
|
+
*
|
|
15
|
+
* Boundary:
|
|
16
|
+
* - Read-only diagnostic; does not modify state.
|
|
17
|
+
* - Reports explicit absence reasons instead of silent zeros.
|
|
18
|
+
*/
|
|
19
|
+
import { readActionClosuresByDay, readDailyRhythmStateByDay, readHeartbeatCycleTraces, } from "../storage/v8-state-stores.js";
|
|
20
|
+
// ───────────────────────────────────────────────────────────────
|
|
21
|
+
// Public API
|
|
22
|
+
// ───────────────────────────────────────────────────────────────
|
|
23
|
+
export async function checkRealRunHealth(db, day) {
|
|
24
|
+
const targetDay = day ?? new Date().toISOString().slice(0, 10);
|
|
25
|
+
// Check closures
|
|
26
|
+
const closureResult = await readActionClosuresByDay(db, targetDay);
|
|
27
|
+
if (closureResult.degraded) {
|
|
28
|
+
return { ok: false, degraded: closureResult.degraded };
|
|
29
|
+
}
|
|
30
|
+
const hasRealClosure = closureResult.rows.length > 0;
|
|
31
|
+
// Check if closures are runtime-produced (backed by cycle trace + stage events)
|
|
32
|
+
let seededStateDetected = false;
|
|
33
|
+
if (hasRealClosure) {
|
|
34
|
+
const traces = await readHeartbeatCycleTraces(db, 1000);
|
|
35
|
+
if (traces.degraded) {
|
|
36
|
+
return { ok: false, degraded: traces.degraded };
|
|
37
|
+
}
|
|
38
|
+
for (const closure of closureResult.rows) {
|
|
39
|
+
const hasCycleTrace = traces.rows.some((t) => t.id === closure.cycleId);
|
|
40
|
+
if (!hasCycleTrace) {
|
|
41
|
+
seededStateDetected = true;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Check daily rhythm state for Quiet/Dream
|
|
47
|
+
const rhythmResult = await readDailyRhythmStateByDay(db, targetDay);
|
|
48
|
+
if (rhythmResult.degraded) {
|
|
49
|
+
return { ok: false, degraded: rhythmResult.degraded };
|
|
50
|
+
}
|
|
51
|
+
const rhythm = rhythmResult.row;
|
|
52
|
+
const hasQuietArtifact = rhythm?.quietStatus === "completed";
|
|
53
|
+
const hasDreamArtifact = rhythm?.dreamStatus === "scheduled" || rhythm?.dreamStatus === "completed";
|
|
54
|
+
// Determine if only contract smoke
|
|
55
|
+
const contractSmokeOnly = !hasRealClosure && !hasQuietArtifact && !hasDreamArtifact;
|
|
56
|
+
// Gate passes only when all real runtime stages have evidence
|
|
57
|
+
const gatePassed = !contractSmokeOnly && !seededStateDetected && hasRealClosure && hasQuietArtifact && hasDreamArtifact;
|
|
58
|
+
// Identify missing stage
|
|
59
|
+
let missingStage;
|
|
60
|
+
let missingReason;
|
|
61
|
+
if (!hasRealClosure) {
|
|
62
|
+
missingStage = "closure";
|
|
63
|
+
missingReason = "No ActionClosureRecord for today. Heartbeat may be running contract smoke without real action closure.";
|
|
64
|
+
}
|
|
65
|
+
else if (seededStateDetected) {
|
|
66
|
+
missingStage = "closure";
|
|
67
|
+
missingReason = "ActionClosureRecord exists but lacks runtime-produced cycle trace. Seeded state detected — not valid runtime proof.";
|
|
68
|
+
}
|
|
69
|
+
else if (!hasQuietArtifact) {
|
|
70
|
+
missingStage = "quiet";
|
|
71
|
+
missingReason = "ActionClosureRecord exists but no QuietDailyReview. Daily review may be due or skipped.";
|
|
72
|
+
}
|
|
73
|
+
else if (!hasDreamArtifact) {
|
|
74
|
+
missingStage = "dream";
|
|
75
|
+
missingReason = "QuietDailyReview completed but no DreamConsolidationRun. Dream scheduler may be unavailable.";
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
missingStage = "none";
|
|
79
|
+
missingReason = "All living-loop stages have real artifacts.";
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
ok: true,
|
|
83
|
+
gate: {
|
|
84
|
+
hasRealClosure,
|
|
85
|
+
hasQuietArtifact,
|
|
86
|
+
hasDreamArtifact,
|
|
87
|
+
contractSmokeOnly,
|
|
88
|
+
seededStateDetected,
|
|
89
|
+
gatePassed,
|
|
90
|
+
missingStage,
|
|
91
|
+
missingReason,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -20,6 +20,16 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { StateDatabase } from "../storage/db/index.js";
|
|
22
22
|
import type { DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
23
|
+
export interface RealRunHealthProjection {
|
|
24
|
+
gatePassed: boolean;
|
|
25
|
+
contractSmokeOnly: boolean;
|
|
26
|
+
seededStateDetected: boolean;
|
|
27
|
+
hasRealClosure: boolean;
|
|
28
|
+
hasQuietArtifact: boolean;
|
|
29
|
+
hasDreamArtifact: boolean;
|
|
30
|
+
missingStage?: string;
|
|
31
|
+
missingReason?: string;
|
|
32
|
+
}
|
|
23
33
|
export interface LoopStatusReadModel {
|
|
24
34
|
ok: true;
|
|
25
35
|
overallStatus: string;
|
|
@@ -29,6 +39,7 @@ export interface LoopStatusReadModel {
|
|
|
29
39
|
stageSummaries: StageSummary[];
|
|
30
40
|
policyDeniedCount: number;
|
|
31
41
|
nextAction: string;
|
|
42
|
+
realRunHealth: RealRunHealthProjection;
|
|
32
43
|
}
|
|
33
44
|
export interface StageSummary {
|
|
34
45
|
stage: string;
|
|
@@ -19,10 +19,15 @@
|
|
|
19
19
|
* Test coverage: tests/unit/observability/loop-status.test.ts
|
|
20
20
|
*/
|
|
21
21
|
import { assembleLoopStatus } from "./causal-loop-health.js";
|
|
22
|
+
import { checkRealRunHealth } from "./living-loop-health-gate.js";
|
|
22
23
|
// ───────────────────────────────────────────────────────────────
|
|
23
24
|
// Helpers
|
|
24
25
|
// ───────────────────────────────────────────────────────────────
|
|
25
|
-
function computeNextAction(overallStatus, stalledAt) {
|
|
26
|
+
function computeNextAction(overallStatus, stalledAt, realRunMissingStage, realRunMissingReason) {
|
|
27
|
+
// Real-run health takes precedence over generic causal health
|
|
28
|
+
if (realRunMissingStage && realRunMissingStage !== "none") {
|
|
29
|
+
return `Real-run health degraded: ${realRunMissingReason ?? `missing stage: ${realRunMissingStage}`}. Run a real heartbeat cycle or verify daily rhythm state.`;
|
|
30
|
+
}
|
|
26
31
|
if (overallStatus === "healthy") {
|
|
27
32
|
return "No operator action required. Loop is progressing normally.";
|
|
28
33
|
}
|
|
@@ -60,6 +65,46 @@ export async function readLoopStatus(db) {
|
|
|
60
65
|
};
|
|
61
66
|
}
|
|
62
67
|
const snapshot = health;
|
|
68
|
+
// T-OBS.R.3: Consume real-run health gate
|
|
69
|
+
const realRunResult = await checkRealRunHealth(db);
|
|
70
|
+
let realRunHealth;
|
|
71
|
+
if (realRunResult.ok) {
|
|
72
|
+
realRunHealth = {
|
|
73
|
+
gatePassed: realRunResult.gate.gatePassed,
|
|
74
|
+
contractSmokeOnly: realRunResult.gate.contractSmokeOnly,
|
|
75
|
+
seededStateDetected: realRunResult.gate.seededStateDetected,
|
|
76
|
+
hasRealClosure: realRunResult.gate.hasRealClosure,
|
|
77
|
+
hasQuietArtifact: realRunResult.gate.hasQuietArtifact,
|
|
78
|
+
hasDreamArtifact: realRunResult.gate.hasDreamArtifact,
|
|
79
|
+
missingStage: realRunResult.gate.missingStage,
|
|
80
|
+
missingReason: realRunResult.gate.missingReason,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
realRunHealth = {
|
|
85
|
+
gatePassed: false,
|
|
86
|
+
contractSmokeOnly: false,
|
|
87
|
+
seededStateDetected: false,
|
|
88
|
+
hasRealClosure: false,
|
|
89
|
+
hasQuietArtifact: false,
|
|
90
|
+
hasDreamArtifact: false,
|
|
91
|
+
missingReason: "Real-run health check degraded",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Override overallStatus based on real-run health parity
|
|
95
|
+
let overallStatus = snapshot.overallStatus;
|
|
96
|
+
let stalledAt = snapshot.stalledAt;
|
|
97
|
+
if (!realRunHealth.gatePassed) {
|
|
98
|
+
// Real-run gate fails → cannot report healthy
|
|
99
|
+
if (overallStatus === "healthy") {
|
|
100
|
+
overallStatus = "degraded";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Real-run gate passes → all stages have evidence, ignore staged-event-only stall
|
|
105
|
+
overallStatus = "healthy";
|
|
106
|
+
stalledAt = undefined;
|
|
107
|
+
}
|
|
63
108
|
const stageSummaries = snapshot.stages.map((s) => ({
|
|
64
109
|
stage: s.stage,
|
|
65
110
|
eventCount: s.eventCount,
|
|
@@ -68,18 +113,19 @@ export async function readLoopStatus(db) {
|
|
|
68
113
|
}));
|
|
69
114
|
// Policy denied count is a placeholder; real implementation would query action closures
|
|
70
115
|
const policyDeniedCount = 0;
|
|
71
|
-
const nextAction = computeNextAction(
|
|
116
|
+
const nextAction = computeNextAction(overallStatus, snapshot.stalledAt, realRunHealth.missingStage, realRunHealth.missingReason);
|
|
72
117
|
return {
|
|
73
118
|
ok: true,
|
|
74
119
|
status: {
|
|
75
120
|
ok: true,
|
|
76
|
-
overallStatus
|
|
121
|
+
overallStatus,
|
|
77
122
|
stalledAt: snapshot.stalledAt,
|
|
78
123
|
lastCycleSequence: snapshot.lastCycleSequence,
|
|
79
124
|
lastHeartbeatAt: snapshot.lastHeartbeatAt,
|
|
80
125
|
stageSummaries,
|
|
81
126
|
policyDeniedCount,
|
|
82
127
|
nextAction,
|
|
128
|
+
realRunHealth,
|
|
83
129
|
},
|
|
84
130
|
};
|
|
85
131
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ConnectorResult } from "../../connectors/base/contract.js";
|
|
2
|
+
import type { QuietArtifactAck } from "../../storage/quiet/quiet-artifact-writer.js";
|
|
3
|
+
import type { QuietArtifactWrite } from "../../storage/quiet/quiet-artifact-types.js";
|
|
4
|
+
import type { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
|
|
5
|
+
export type ConnectorAttemptAuditOutcome = "success" | "failure" | "circuit_open" | "blocked";
|
|
6
|
+
export interface RecordConnectorAttemptAuditInput {
|
|
7
|
+
auditStore?: AppendOnlyAuditStore;
|
|
8
|
+
platformId: string;
|
|
9
|
+
capability: string;
|
|
10
|
+
result: ConnectorResult<unknown>;
|
|
11
|
+
triggerSource: "manual_run" | "heartbeat";
|
|
12
|
+
decisionId?: string;
|
|
13
|
+
intentId?: string;
|
|
14
|
+
createdAt?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RecordQuietArtifactAuditInput {
|
|
17
|
+
auditStore?: AppendOnlyAuditStore;
|
|
18
|
+
day: string;
|
|
19
|
+
kind: QuietArtifactWrite["kind"];
|
|
20
|
+
status: "completed" | "empty" | "blocked" | "failed";
|
|
21
|
+
reasons: string[];
|
|
22
|
+
artifactAck?: QuietArtifactAck;
|
|
23
|
+
persistedRelativePath?: string;
|
|
24
|
+
createdAt?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function recordConnectorAttemptAudit(input: RecordConnectorAttemptAuditInput): {
|
|
27
|
+
eventId?: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function recordQuietArtifactAudit(input: RecordQuietArtifactAuditInput): {
|
|
30
|
+
eventId?: string;
|
|
31
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit-backed closure recorders for connector attempts and Quiet artifacts.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: write small redacted audit envelopes for runtime actions that
|
|
5
|
+
* heartbeat_digest depends on. Payloads contain outcome and source linkage only;
|
|
6
|
+
* raw connector payloads and credentials are deliberately excluded.
|
|
7
|
+
*
|
|
8
|
+
* Dependencies: AppendOnlyAuditStore and buildAuditEnvelope hash-chain helpers.
|
|
9
|
+
* Boundary: does not execute connectors, write Quiet artifacts, or mutate state.
|
|
10
|
+
*
|
|
11
|
+
* Test coverage: tests/unit/ops/manual-run-dispatcher.test.ts,
|
|
12
|
+
* tests/unit/observability/heartbeat-digest-assembler.test.ts.
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
import { buildAuditEnvelope } from "../audit/audit-envelope.js";
|
|
16
|
+
function mapConnectorOutcome(result) {
|
|
17
|
+
if (result.status === "success")
|
|
18
|
+
return "success";
|
|
19
|
+
if (result.failureClass === "cooldown_blocked")
|
|
20
|
+
return "blocked";
|
|
21
|
+
if (result.failureClass === "platform_unavailable")
|
|
22
|
+
return "circuit_open";
|
|
23
|
+
return "failure";
|
|
24
|
+
}
|
|
25
|
+
export function recordConnectorAttemptAudit(input) {
|
|
26
|
+
if (!input.auditStore)
|
|
27
|
+
return {};
|
|
28
|
+
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
29
|
+
const family = "connector.attempt";
|
|
30
|
+
const envelope = buildAuditEnvelope({
|
|
31
|
+
family,
|
|
32
|
+
plane: "telemetry",
|
|
33
|
+
traceId: `connector_attempt:${input.triggerSource}:${randomUUID()}`,
|
|
34
|
+
sequence: input.auditStore.list().length + 1,
|
|
35
|
+
previousHash: input.auditStore.lastRecordHash(family),
|
|
36
|
+
createdAt,
|
|
37
|
+
payload: {
|
|
38
|
+
platformId: input.platformId,
|
|
39
|
+
capability: input.capability,
|
|
40
|
+
outcome: mapConnectorOutcome(input.result),
|
|
41
|
+
triggerSource: input.triggerSource,
|
|
42
|
+
status: input.result.status,
|
|
43
|
+
failureClass: input.result.failureClass,
|
|
44
|
+
channel: input.result.metadata.channel,
|
|
45
|
+
latencyMs: input.result.metadata.latencyMs,
|
|
46
|
+
degraded: Boolean(input.result.metadata.degraded),
|
|
47
|
+
decisionId: input.decisionId,
|
|
48
|
+
intentId: input.intentId,
|
|
49
|
+
createdAt,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
input.auditStore.append(envelope);
|
|
53
|
+
return { eventId: envelope.eventId };
|
|
54
|
+
}
|
|
55
|
+
export function recordQuietArtifactAudit(input) {
|
|
56
|
+
if (!input.auditStore)
|
|
57
|
+
return {};
|
|
58
|
+
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
59
|
+
const family = "source_coverage";
|
|
60
|
+
const sourceRefs = input.artifactAck ? [input.artifactAck.artifactRef] : [];
|
|
61
|
+
const envelope = buildAuditEnvelope({
|
|
62
|
+
family,
|
|
63
|
+
plane: "source_coverage",
|
|
64
|
+
traceId: `quiet_artifact:${input.day}:${randomUUID()}`,
|
|
65
|
+
sequence: input.auditStore.list().length + 1,
|
|
66
|
+
previousHash: input.auditStore.lastRecordHash(family),
|
|
67
|
+
createdAt,
|
|
68
|
+
payload: {
|
|
69
|
+
auditId: `quiet-audit:${input.day}:${randomUUID()}`,
|
|
70
|
+
traceId: `quiet_trace:${input.day}`,
|
|
71
|
+
subjectType: "quiet_artifact",
|
|
72
|
+
subjectRef: input.artifactAck?.artifactRef.uri ?? `quiet:${input.day}:${input.kind}`,
|
|
73
|
+
day: input.day,
|
|
74
|
+
kind: input.kind,
|
|
75
|
+
status: input.status,
|
|
76
|
+
usedSourceRefs: sourceRefs,
|
|
77
|
+
unresolvedRefs: [],
|
|
78
|
+
coverageRatio: input.artifactAck?.sourceCoverage.coverageRatio ?? 0,
|
|
79
|
+
unsupportedClaims: input.artifactAck?.sourceCoverage.unsupportedClaims ?? [],
|
|
80
|
+
persistedRelativePath: input.persistedRelativePath,
|
|
81
|
+
reasonCodes: input.reasons,
|
|
82
|
+
createdAt,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
input.auditStore.append(envelope);
|
|
86
|
+
return { eventId: envelope.eventId };
|
|
87
|
+
}
|
|
@@ -68,6 +68,16 @@ export interface DeliveryProofRef {
|
|
|
68
68
|
channelId: string;
|
|
69
69
|
messageHash: string;
|
|
70
70
|
}
|
|
71
|
+
export interface RealRunHealthDigestProjection {
|
|
72
|
+
gatePassed: boolean;
|
|
73
|
+
contractSmokeOnly: boolean;
|
|
74
|
+
seededStateDetected: boolean;
|
|
75
|
+
hasRealClosure: boolean;
|
|
76
|
+
hasQuietArtifact: boolean;
|
|
77
|
+
hasDreamArtifact: boolean;
|
|
78
|
+
missingStage?: string;
|
|
79
|
+
missingReason?: string;
|
|
80
|
+
}
|
|
71
81
|
export interface HeartbeatDigest {
|
|
72
82
|
date: string;
|
|
73
83
|
generatedAt: string;
|
|
@@ -76,6 +86,8 @@ export interface HeartbeatDigest {
|
|
|
76
86
|
goalSummary: GoalDaySummary;
|
|
77
87
|
quietDreamSummary: QuietDreamDaySummary;
|
|
78
88
|
healthSummary: HealthDaySummary;
|
|
89
|
+
/** Real-run health gate result (T-OBS.R.3) */
|
|
90
|
+
realRunHealth: RealRunHealthDigestProjection;
|
|
79
91
|
/** Set when delivery succeeded */
|
|
80
92
|
deliveredAt?: string;
|
|
81
93
|
/** Proof of successful delivery (channel + message hash, no raw content) */
|
|
@@ -97,10 +97,21 @@ function aggregateHealthSummary(events, dateStr) {
|
|
|
97
97
|
}
|
|
98
98
|
function aggregateQuietDreamFromAudit(events, dateStr) {
|
|
99
99
|
const dreamEvents = filterByDate(events, "dream.trace", dateStr);
|
|
100
|
+
const quietEvents = filterByDate(events, "source_coverage", dateStr)
|
|
101
|
+
.filter((ev) => ev.payload.subjectType === "quiet_artifact");
|
|
102
|
+
let quietRuns = 0;
|
|
103
|
+
let quietSucceeded = 0;
|
|
100
104
|
let dreamRuns = 0;
|
|
101
105
|
let dreamAccepted = 0;
|
|
102
106
|
let dreamSkipped = 0;
|
|
103
107
|
const dreamSkipReasons = [];
|
|
108
|
+
for (const ev of quietEvents) {
|
|
109
|
+
const payload = ev.payload;
|
|
110
|
+
quietRuns++;
|
|
111
|
+
if (payload.status === "completed" || payload.status === "empty") {
|
|
112
|
+
quietSucceeded++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
104
115
|
for (const ev of dreamEvents) {
|
|
105
116
|
const payload = ev.payload;
|
|
106
117
|
if (payload.event === "dream_started")
|
|
@@ -113,10 +124,9 @@ function aggregateQuietDreamFromAudit(events, dateStr) {
|
|
|
113
124
|
dreamSkipReasons.push(payload.skipReason);
|
|
114
125
|
}
|
|
115
126
|
}
|
|
116
|
-
// Quiet stats not yet in audit (narrative.trace not fully wired) — default to 0
|
|
117
127
|
return {
|
|
118
|
-
quietRuns
|
|
119
|
-
quietSucceeded
|
|
128
|
+
quietRuns,
|
|
129
|
+
quietSucceeded,
|
|
120
130
|
dreamRuns,
|
|
121
131
|
dreamAccepted,
|
|
122
132
|
dreamSkipped,
|
|
@@ -217,6 +227,15 @@ export async function generateHeartbeatDigest(date, deps) {
|
|
|
217
227
|
goalSummary,
|
|
218
228
|
quietDreamSummary,
|
|
219
229
|
healthSummary,
|
|
230
|
+
realRunHealth: {
|
|
231
|
+
gatePassed: false,
|
|
232
|
+
contractSmokeOnly: true,
|
|
233
|
+
seededStateDetected: false,
|
|
234
|
+
hasRealClosure: false,
|
|
235
|
+
hasQuietArtifact: false,
|
|
236
|
+
hasDreamArtifact: false,
|
|
237
|
+
missingReason: "Real-run health not evaluated — call checkRealRunHealth before digest generation",
|
|
238
|
+
},
|
|
220
239
|
};
|
|
221
240
|
// T-OBS.C.4: delivery hook — attempt delivery if adapter is provided
|
|
222
241
|
if (deliveryAdapter) {
|
|
@@ -25,7 +25,7 @@ export interface ActionKindMetadata {
|
|
|
25
25
|
allowedDowngrades: PlatformNeutralActionKind[];
|
|
26
26
|
}
|
|
27
27
|
export type ConnectorCapabilitySideEffect = "external_read" | "external_write" | "local_state" | "unknown";
|
|
28
|
-
export type SourceRefFamily = "evidence" | "perception" | "judgment" | "action_closure" | "quiet_review" | "dream_run" | "memory_projection" | "tool_experience" | "connector_result" | "audit";
|
|
28
|
+
export type SourceRefFamily = "evidence" | "perception" | "judgment" | "action_closure" | "quiet_review" | "dream_run" | "memory_projection" | "projection" | "tool_experience" | "connector_result" | "audit";
|
|
29
29
|
export type RedactionClass = "none" | "redacted" | "blocked";
|
|
30
30
|
export type SensitivityClass = "public_technical" | "public_general" | "private_context" | "sensitive";
|
|
31
31
|
export type SourceResolveStatus = "resolvable" | "missing" | "redacted" | "permission_denied";
|
|
@@ -82,5 +82,5 @@ export interface DegradedOperationResult {
|
|
|
82
82
|
operatorNextAction: string;
|
|
83
83
|
retryable: boolean;
|
|
84
84
|
}
|
|
85
|
-
export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "dream_scheduled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "evidence_batch_empty" | "evidence_batch_truncated" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
|
|
85
|
+
export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "dream_scheduled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "projection_topic_matched" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "evidence_batch_empty" | "evidence_batch_truncated" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
|
|
86
86
|
export declare const ACTION_KIND_REGISTRY: Readonly<Record<PlatformNeutralActionKind, ActionKindMetadata>>;
|
|
@@ -197,6 +197,7 @@ const STATE_SCHEMA_SQL = `
|
|
|
197
197
|
entities_json TEXT,
|
|
198
198
|
novelty TEXT,
|
|
199
199
|
relevance REAL,
|
|
200
|
+
relevance_class TEXT,
|
|
200
201
|
summary TEXT,
|
|
201
202
|
risk_flags_json TEXT,
|
|
202
203
|
confidence REAL,
|
|
@@ -241,6 +242,7 @@ const STATE_SCHEMA_SQL = `
|
|
|
241
242
|
closure_count INTEGER NOT NULL DEFAULT 0,
|
|
242
243
|
memory_candidate_count INTEGER NOT NULL DEFAULT 0,
|
|
243
244
|
source_refs_json TEXT NOT NULL,
|
|
245
|
+
closure_refs_json TEXT,
|
|
244
246
|
redaction_class TEXT NOT NULL DEFAULT 'none',
|
|
245
247
|
payload_json TEXT,
|
|
246
248
|
lifecycle_status TEXT NOT NULL DEFAULT 'pending'
|
|
@@ -295,6 +297,38 @@ const STATE_SCHEMA_SQL = `
|
|
|
295
297
|
payload_json TEXT,
|
|
296
298
|
lifecycle_status TEXT NOT NULL DEFAULT 'started'
|
|
297
299
|
);
|
|
300
|
+
CREATE TABLE IF NOT EXISTS impulse_context_artifact (
|
|
301
|
+
id TEXT PRIMARY KEY,
|
|
302
|
+
created_at TEXT NOT NULL,
|
|
303
|
+
updated_at TEXT NOT NULL,
|
|
304
|
+
scene_type TEXT NOT NULL,
|
|
305
|
+
capability_intent TEXT,
|
|
306
|
+
platform_id TEXT,
|
|
307
|
+
capability_class TEXT,
|
|
308
|
+
impulse_source TEXT NOT NULL,
|
|
309
|
+
impulse_text TEXT,
|
|
310
|
+
atmosphere_text TEXT,
|
|
311
|
+
expression_boundary_constraints_json TEXT,
|
|
312
|
+
expression_boundary_style TEXT,
|
|
313
|
+
freshness_version INTEGER NOT NULL DEFAULT 1,
|
|
314
|
+
source_refs_json TEXT NOT NULL,
|
|
315
|
+
redaction_class TEXT NOT NULL DEFAULT 'none',
|
|
316
|
+
payload_json TEXT,
|
|
317
|
+
lifecycle_status TEXT NOT NULL DEFAULT 'active'
|
|
318
|
+
);
|
|
319
|
+
CREATE TABLE IF NOT EXISTS daily_rhythm_state (
|
|
320
|
+
id TEXT PRIMARY KEY,
|
|
321
|
+
day TEXT NOT NULL,
|
|
322
|
+
quiet_status TEXT NOT NULL DEFAULT 'not_due',
|
|
323
|
+
dream_status TEXT NOT NULL DEFAULT 'not_due',
|
|
324
|
+
quiet_reason TEXT,
|
|
325
|
+
dream_reason TEXT,
|
|
326
|
+
quiet_completed_at TEXT,
|
|
327
|
+
dream_completed_at TEXT,
|
|
328
|
+
source_refs_json TEXT NOT NULL,
|
|
329
|
+
payload_json TEXT,
|
|
330
|
+
updated_at TEXT NOT NULL
|
|
331
|
+
);
|
|
298
332
|
`;
|
|
299
333
|
function resolveDbPath(filename) {
|
|
300
334
|
if (path.isAbsolute(filename) || filename === ":memory:") {
|
|
@@ -6,10 +6,14 @@ import { V7_002_EFFECT_COMMIT_LEDGER } from "./v7-002-effect-commit-ledger.js";
|
|
|
6
6
|
import { V7_003_CIRCUIT_BREAKER } from "./v7-003-circuit-breaker.js";
|
|
7
7
|
import { V7_004_BEHAVIOR_PROMOTION } from "./v7-004-behavior-promotion.js";
|
|
8
8
|
import { V8_001_LIVING_PERCEPTION_LOOP } from "./v8-001-living-perception-loop.js";
|
|
9
|
+
import { V8_002_PERCEPTION_CONTRACT_ALIGNMENT } from "./v8-002-perception-contract-alignment.js";
|
|
10
|
+
import { V8_003_QUIET_CLOSURE_REFS } from "./v8-003-quiet-closure-refs.js";
|
|
9
11
|
export const ALL_MIGRATIONS = [
|
|
10
12
|
V7_001_FOUNDATION,
|
|
11
13
|
V7_002_EFFECT_COMMIT_LEDGER,
|
|
12
14
|
V7_003_CIRCUIT_BREAKER,
|
|
13
15
|
V7_004_BEHAVIOR_PROMOTION,
|
|
14
16
|
V8_001_LIVING_PERCEPTION_LOOP,
|
|
17
|
+
V8_002_PERCEPTION_CONTRACT_ALIGNMENT,
|
|
18
|
+
V8_003_QUIET_CLOSURE_REFS,
|
|
15
19
|
];
|