selftune 0.2.8 → 0.2.9
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/apps/local-dashboard/dist/assets/{index-CRtLkBTi.css → index-Bs3Y4ixf.css} +1 -1
- package/apps/local-dashboard/dist/assets/index-C4UYGWKr.js +15 -0
- package/apps/local-dashboard/dist/index.html +2 -2
- package/cli/selftune/activation-rules.ts +36 -18
- package/cli/selftune/agent-guidance.ts +16 -16
- package/cli/selftune/alpha-identity.ts +1 -2
- package/cli/selftune/alpha-upload/flush.ts +2 -2
- package/cli/selftune/alpha-upload/stage-canonical.ts +12 -3
- package/cli/selftune/contribute/bundle.ts +1 -0
- package/cli/selftune/dashboard-contract.ts +1 -1
- package/cli/selftune/dashboard-server.ts +10 -52
- package/cli/selftune/eval/hooks-to-evals.ts +12 -6
- package/cli/selftune/grading/auto-grade.ts +1 -0
- package/cli/selftune/grading/grade-session.ts +1 -0
- package/cli/selftune/hooks/auto-activate.ts +5 -0
- package/cli/selftune/hooks/evolution-guard.ts +11 -14
- package/cli/selftune/init.ts +80 -71
- package/cli/selftune/localdb/direct-write.ts +54 -12
- package/cli/selftune/localdb/queries.ts +157 -0
- package/cli/selftune/localdb/schema.ts +44 -1
- package/cli/selftune/monitoring/watch.ts +1 -0
- package/cli/selftune/normalization.ts +3 -0
- package/cli/selftune/observability.ts +13 -7
- package/cli/selftune/orchestrate.ts +15 -37
- package/cli/selftune/repair/skill-usage.ts +6 -3
- package/cli/selftune/sync.ts +1 -0
- package/cli/selftune/types.ts +2 -2
- package/package.json +2 -1
- package/packages/telemetry-contract/package.json +2 -2
- package/packages/telemetry-contract/src/index.ts +0 -1
- package/packages/telemetry-contract/src/schemas.ts +5 -24
- package/skill/SKILL.md +3 -3
- package/skill/Workflows/Dashboard.md +8 -13
- package/skill/Workflows/Doctor.md +7 -7
- package/skill/Workflows/Initialize.md +35 -62
- package/skill/references/logs.md +6 -0
- package/apps/local-dashboard/dist/assets/index-Bk9vSHHd.js +0 -15
|
@@ -16,12 +16,12 @@ import { parseArgs } from "node:util";
|
|
|
16
16
|
|
|
17
17
|
import { readAlphaIdentity } from "./alpha-identity.js";
|
|
18
18
|
import type { UploadCycleSummary } from "./alpha-upload/index.js";
|
|
19
|
-
import { ORCHESTRATE_LOCK, SELFTUNE_CONFIG_PATH
|
|
19
|
+
import { ORCHESTRATE_LOCK, SELFTUNE_CONFIG_PATH } from "./constants.js";
|
|
20
20
|
import type { OrchestrateRunReport, OrchestrateRunSkillAction } from "./dashboard-contract.js";
|
|
21
21
|
import type { EvolveResult } from "./evolution/evolve.js";
|
|
22
22
|
import { readGradingResultsForSkill } from "./grading/results.js";
|
|
23
23
|
import { getDb } from "./localdb/db.js";
|
|
24
|
-
import { writeOrchestrateRunToDb } from "./localdb/direct-write.js";
|
|
24
|
+
import { updateSignalConsumed, writeOrchestrateRunToDb } from "./localdb/direct-write.js";
|
|
25
25
|
import {
|
|
26
26
|
queryEvolutionAudit,
|
|
27
27
|
queryImprovementSignals,
|
|
@@ -36,13 +36,13 @@ import { computeStatus } from "./status.js";
|
|
|
36
36
|
import type { SyncResult } from "./sync.js";
|
|
37
37
|
import { createDefaultSyncOptions, syncSources } from "./sync.js";
|
|
38
38
|
import type {
|
|
39
|
+
AlphaIdentity,
|
|
39
40
|
EvolutionAuditEntry,
|
|
40
41
|
ImprovementSignalRecord,
|
|
41
42
|
QueryLogRecord,
|
|
42
43
|
SessionTelemetryRecord,
|
|
43
44
|
SkillUsageRecord,
|
|
44
45
|
} from "./types.js";
|
|
45
|
-
import { readJsonl } from "./utils/jsonl.js";
|
|
46
46
|
import { detectAgent } from "./utils/llm-call.js";
|
|
47
47
|
import { getSelftuneVersion, readConfiguredAgentType } from "./utils/selftune-meta.js";
|
|
48
48
|
import {
|
|
@@ -123,42 +123,17 @@ export function groupSignalsBySkill(signals: ImprovementSignalRecord[]): Map<str
|
|
|
123
123
|
return map;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
export function markSignalsConsumed(
|
|
127
|
-
signals: ImprovementSignalRecord[],
|
|
128
|
-
runId: string,
|
|
129
|
-
signalLogPath: string = SIGNAL_LOG,
|
|
130
|
-
): void {
|
|
126
|
+
export function markSignalsConsumed(signals: ImprovementSignalRecord[], runId: string): void {
|
|
131
127
|
try {
|
|
132
128
|
if (signals.length === 0) return;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const now = new Date().toISOString();
|
|
140
|
-
const updated = allRecords.map((record) => {
|
|
141
|
-
const key = `${record.timestamp}|${record.session_id}`;
|
|
142
|
-
if (pendingKeys.has(key) && !record.consumed) {
|
|
143
|
-
return {
|
|
144
|
-
...record,
|
|
145
|
-
consumed: true,
|
|
146
|
-
consumed_at: now,
|
|
147
|
-
consumed_by_run: runId,
|
|
148
|
-
};
|
|
129
|
+
for (const signal of signals) {
|
|
130
|
+
const ok = updateSignalConsumed(signal.session_id, signal.query, signal.signal_type, runId);
|
|
131
|
+
if (!ok) {
|
|
132
|
+
console.error(
|
|
133
|
+
`[orchestrate] failed to mark signal consumed: session_id=${signal.session_id}, signal_type=${signal.signal_type}`,
|
|
134
|
+
);
|
|
149
135
|
}
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Re-read to capture any signals appended between our read and write
|
|
154
|
-
const freshRecords = readJsonl<ImprovementSignalRecord>(signalLogPath);
|
|
155
|
-
const existingKeys = new Set(updated.map((r) => `${r.timestamp}|${r.session_id}`));
|
|
156
|
-
const newlyAppended = freshRecords.filter(
|
|
157
|
-
(r) => !existingKeys.has(`${r.timestamp}|${r.session_id}`),
|
|
158
|
-
);
|
|
159
|
-
const merged = [...updated, ...newlyAppended];
|
|
160
|
-
|
|
161
|
-
writeFileSync(signalLogPath, `${merged.map((r) => JSON.stringify(r)).join("\n")}\n`);
|
|
136
|
+
}
|
|
162
137
|
} catch {
|
|
163
138
|
// Silent on errors
|
|
164
139
|
}
|
|
@@ -412,6 +387,7 @@ export interface OrchestrateDeps {
|
|
|
412
387
|
resolveSkillPath?: (skillName: string) => string | undefined;
|
|
413
388
|
readGradingResults?: (skillName: string) => ReturnType<typeof readGradingResultsForSkill>;
|
|
414
389
|
readSignals?: () => ImprovementSignalRecord[];
|
|
390
|
+
readAlphaIdentity?: () => AlphaIdentity | null;
|
|
415
391
|
}
|
|
416
392
|
|
|
417
393
|
// ---------------------------------------------------------------------------
|
|
@@ -729,6 +705,8 @@ export async function orchestrate(
|
|
|
729
705
|
});
|
|
730
706
|
const _resolveSkillPath = deps.resolveSkillPath ?? defaultResolveSkillPath;
|
|
731
707
|
const _readGradingResults = deps.readGradingResults ?? readGradingResultsForSkill;
|
|
708
|
+
const _readAlphaIdentity =
|
|
709
|
+
deps.readAlphaIdentity ?? (() => readAlphaIdentity(SELFTUNE_CONFIG_PATH));
|
|
732
710
|
|
|
733
711
|
// Lazy-load evolve and watch to avoid circular imports
|
|
734
712
|
const _evolve = deps.evolve ?? (await import("./evolution/evolve.js")).evolve;
|
|
@@ -1001,7 +979,7 @@ export async function orchestrate(
|
|
|
1001
979
|
// -------------------------------------------------------------------------
|
|
1002
980
|
// Step 9: Alpha upload (fail-open — never blocks the orchestrate loop)
|
|
1003
981
|
// -------------------------------------------------------------------------
|
|
1004
|
-
const alphaIdentity =
|
|
982
|
+
const alphaIdentity = _readAlphaIdentity();
|
|
1005
983
|
if (alphaIdentity?.enrolled) {
|
|
1006
984
|
try {
|
|
1007
985
|
console.error("[orchestrate] Running alpha upload cycle...");
|
|
@@ -511,14 +511,17 @@ Options:
|
|
|
511
511
|
since,
|
|
512
512
|
);
|
|
513
513
|
const rolloutPaths = findRolloutFiles(values["codex-home"] ?? DEFAULT_CODEX_HOME, since);
|
|
514
|
+
// SQLite-first: default paths read from SQLite; JSONL only for custom --skill-log overrides
|
|
514
515
|
let rawSkillRecords: SkillUsageRecord[];
|
|
515
516
|
let queryRecords: QueryLogRecord[];
|
|
516
|
-
|
|
517
|
+
const skillLogPath = values["skill-log"] ?? SKILL_LOG;
|
|
518
|
+
if (skillLogPath === SKILL_LOG) {
|
|
517
519
|
const db = getDb();
|
|
518
520
|
rawSkillRecords = querySkillUsageRecords(db) as SkillUsageRecord[];
|
|
519
521
|
queryRecords = queryQueryLog(db) as QueryLogRecord[];
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
+
} else {
|
|
523
|
+
// test/custom-path fallback
|
|
524
|
+
rawSkillRecords = readJsonl<SkillUsageRecord>(skillLogPath);
|
|
522
525
|
queryRecords = readJsonl<QueryLogRecord>(QUERY_LOG);
|
|
523
526
|
}
|
|
524
527
|
const { repairedRecords, repairedSessionIds } = rebuildSkillUsageFromTranscripts(
|
package/cli/selftune/sync.ts
CHANGED
|
@@ -367,6 +367,7 @@ function rebuildSkillUsageOverlay(
|
|
|
367
367
|
rawSkillRecords = readJsonl<SkillUsageRecord>(options.skillLogPath);
|
|
368
368
|
}
|
|
369
369
|
} else {
|
|
370
|
+
// Intentional JSONL fallback: custom --skill-log path overrides SQLite reads
|
|
370
371
|
rawSkillRecords = readJsonl<SkillUsageRecord>(options.skillLogPath);
|
|
371
372
|
}
|
|
372
373
|
const { repairedRecords, repairedSessionIds } = rebuildSkillUsageFromTranscripts(
|
package/cli/selftune/types.ts
CHANGED
|
@@ -125,7 +125,7 @@ export type {
|
|
|
125
125
|
CanonicalSessionRecord,
|
|
126
126
|
CanonicalSkillInvocationRecord,
|
|
127
127
|
CanonicalSourceSessionKind,
|
|
128
|
-
} from "@selftune/telemetry-contract";
|
|
128
|
+
} from "@selftune/telemetry-contract/types";
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
130
|
// Canonical normalization types (local + cloud projection layer)
|
|
131
131
|
// ---------------------------------------------------------------------------
|
|
@@ -138,7 +138,7 @@ export {
|
|
|
138
138
|
CANONICAL_RECORD_KINDS,
|
|
139
139
|
CANONICAL_SCHEMA_VERSION,
|
|
140
140
|
CANONICAL_SOURCE_SESSION_KINDS,
|
|
141
|
-
} from "@selftune/telemetry-contract";
|
|
141
|
+
} from "@selftune/telemetry-contract/types";
|
|
142
142
|
|
|
143
143
|
// ---------------------------------------------------------------------------
|
|
144
144
|
// Transcript parsing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selftune",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Self-improving skills CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"test:fast": "bun test $(find tests -name '*.test.ts' ! -name 'evolve.test.ts' ! -name 'integration.test.ts' ! -name 'dashboard-server.test.ts' ! -path '*/blog-proof/*')",
|
|
61
61
|
"test:slow": "bun test tests/evolution/evolve.test.ts tests/evolution/integration.test.ts tests/monitoring/integration.test.ts tests/dashboard/dashboard-server.test.ts",
|
|
62
62
|
"build:dashboard": "cd apps/local-dashboard && bunx vite build",
|
|
63
|
+
"link:claude-workspace": "bash scripts/link-claude-workspace.sh",
|
|
63
64
|
"sync-version": "bun run scripts/sync-skill-version.ts",
|
|
64
65
|
"validate:subagents": "bun run scripts/validate-subagent-docs.ts",
|
|
65
66
|
"prepublishOnly": "bun run sync-version && bun run build:dashboard",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
},
|
|
14
14
|
"exports": {
|
|
15
15
|
".": "./index.ts",
|
|
16
|
+
"./schemas": "./src/schemas.ts",
|
|
16
17
|
"./types": "./src/types.ts",
|
|
17
18
|
"./validators": "./src/validators.ts",
|
|
18
|
-
"./schemas": "./src/schemas.ts",
|
|
19
19
|
"./fixtures": "./fixtures/index.ts"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"zod": "^3.
|
|
22
|
+
"zod": "^4.3.6"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zod validation schemas for all canonical telemetry record types
|
|
3
|
-
* and the PushPayloadV2 envelope.
|
|
4
|
-
*
|
|
5
|
-
* This is the single source of truth -- cloud consumers should import
|
|
6
|
-
* from @selftune/telemetry-contract/schemas instead of maintaining
|
|
7
|
-
* their own copies.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { z } from "zod";
|
|
11
2
|
import {
|
|
12
3
|
CANONICAL_CAPTURE_MODES,
|
|
@@ -19,8 +10,6 @@ import {
|
|
|
19
10
|
CANONICAL_SOURCE_SESSION_KINDS,
|
|
20
11
|
} from "./types.js";
|
|
21
12
|
|
|
22
|
-
// ---------- Shared enum schemas ----------
|
|
23
|
-
|
|
24
13
|
export const canonicalPlatformSchema = z.enum(CANONICAL_PLATFORMS);
|
|
25
14
|
export const captureModeSchema = z.enum(CANONICAL_CAPTURE_MODES);
|
|
26
15
|
export const sourceSessionKindSchema = z.enum(CANONICAL_SOURCE_SESSION_KINDS);
|
|
@@ -29,14 +18,12 @@ export const invocationModeSchema = z.enum(CANONICAL_INVOCATION_MODES);
|
|
|
29
18
|
export const completionStatusSchema = z.enum(CANONICAL_COMPLETION_STATUSES);
|
|
30
19
|
export const recordKindSchema = z.enum(CANONICAL_RECORD_KINDS);
|
|
31
20
|
|
|
32
|
-
// ---------- Shared structural schemas ----------
|
|
33
|
-
|
|
34
21
|
export const rawSourceRefSchema = z.object({
|
|
35
22
|
path: z.string().optional(),
|
|
36
23
|
line: z.number().int().nonnegative().optional(),
|
|
37
24
|
event_type: z.string().optional(),
|
|
38
25
|
raw_id: z.string().optional(),
|
|
39
|
-
metadata: z.record(z.unknown()).optional(),
|
|
26
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
40
27
|
});
|
|
41
28
|
|
|
42
29
|
export const canonicalRecordBaseSchema = z.object({
|
|
@@ -54,8 +41,6 @@ export const canonicalSessionRecordBaseSchema = canonicalRecordBaseSchema.extend
|
|
|
54
41
|
session_id: z.string().min(1),
|
|
55
42
|
});
|
|
56
43
|
|
|
57
|
-
// ---------- Canonical record schemas ----------
|
|
58
|
-
|
|
59
44
|
export const CanonicalSessionRecordSchema = canonicalSessionRecordBaseSchema.extend({
|
|
60
45
|
record_kind: z.literal("session"),
|
|
61
46
|
external_session_id: z.string().optional(),
|
|
@@ -115,7 +100,7 @@ export const CanonicalExecutionFactRecordSchema = canonicalSessionRecordBaseSche
|
|
|
115
100
|
execution_fact_id: z.string().min(1),
|
|
116
101
|
occurred_at: z.string().datetime(),
|
|
117
102
|
prompt_id: z.string().optional(),
|
|
118
|
-
tool_calls_json: z.record(z.number().finite()),
|
|
103
|
+
tool_calls_json: z.record(z.string(), z.number().finite()),
|
|
119
104
|
total_tool_calls: z.number().int().nonnegative(),
|
|
120
105
|
bash_commands_redacted: z.array(z.string()).optional(),
|
|
121
106
|
assistant_turns: z.number().int().nonnegative(),
|
|
@@ -151,8 +136,6 @@ export const CanonicalEvolutionEvidenceRecordSchema = z.object({
|
|
|
151
136
|
raw_source_ref: rawSourceRefSchema.optional(),
|
|
152
137
|
});
|
|
153
138
|
|
|
154
|
-
// ---------- Orchestrate run schemas ----------
|
|
155
|
-
|
|
156
139
|
export const OrchestrateRunSkillActionSchema = z.object({
|
|
157
140
|
skill: z.string().min(1),
|
|
158
141
|
action: z.enum(["evolve", "watch", "skip"]),
|
|
@@ -179,12 +162,12 @@ export const PushOrchestrateRunRecordSchema = z.object({
|
|
|
179
162
|
skill_actions: z.array(OrchestrateRunSkillActionSchema),
|
|
180
163
|
});
|
|
181
164
|
|
|
182
|
-
// ---------- Push V2 envelope ----------
|
|
183
|
-
|
|
184
165
|
export const PushPayloadV2Schema = z.object({
|
|
185
166
|
schema_version: z.literal("2.0"),
|
|
186
167
|
client_version: z.string().min(1),
|
|
187
|
-
|
|
168
|
+
// Queue-generated push IDs are typically UUIDs, but the wire contract only
|
|
169
|
+
// requires a stable non-empty idempotency key.
|
|
170
|
+
push_id: z.string().min(1),
|
|
188
171
|
normalizer_version: z.string().min(1),
|
|
189
172
|
canonical: z.object({
|
|
190
173
|
sessions: z.array(CanonicalSessionRecordSchema).min(0),
|
|
@@ -197,8 +180,6 @@ export const PushPayloadV2Schema = z.object({
|
|
|
197
180
|
}),
|
|
198
181
|
});
|
|
199
182
|
|
|
200
|
-
// ---------- Inferred types from Zod schemas ----------
|
|
201
|
-
|
|
202
183
|
export type PushPayloadV2 = z.infer<typeof PushPayloadV2Schema>;
|
|
203
184
|
export type ZodCanonicalSessionRecord = z.infer<typeof CanonicalSessionRecordSchema>;
|
|
204
185
|
export type ZodCanonicalPromptRecord = z.infer<typeof CanonicalPromptRecordSchema>;
|
package/skill/SKILL.md
CHANGED
|
@@ -12,7 +12,7 @@ description: >
|
|
|
12
12
|
even if they don't say "selftune" explicitly.
|
|
13
13
|
metadata:
|
|
14
14
|
author: selftune-dev
|
|
15
|
-
version: 0.2.
|
|
15
|
+
version: 0.2.9
|
|
16
16
|
category: developer-tools
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -104,8 +104,8 @@ selftune cron remove [--dry-run]
|
|
|
104
104
|
selftune telemetry [status|enable|disable]
|
|
105
105
|
selftune export [TABLE...] [--output/-o DIR] [--since DATE]
|
|
106
106
|
|
|
107
|
-
# Alpha enrollment (
|
|
108
|
-
selftune init --alpha --alpha-email <email>
|
|
107
|
+
# Alpha enrollment (device-code flow — browser opens automatically)
|
|
108
|
+
selftune init --alpha --alpha-email <email>
|
|
109
109
|
selftune alpha upload [--dry-run]
|
|
110
110
|
selftune status # shows cloud link state + upload readiness
|
|
111
111
|
```
|
|
@@ -11,14 +11,11 @@ selftune dashboard
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Starts a Bun HTTP server with a React SPA dashboard and opens it in the
|
|
14
|
-
default browser. The dashboard reads SQLite directly
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
buttons trigger selftune commands directly from the dashboard. Use
|
|
20
|
-
`selftune export` to generate JSONL from SQLite for debugging or
|
|
21
|
-
offline analysis.
|
|
14
|
+
default browser. The dashboard reads SQLite directly and uses WAL-based
|
|
15
|
+
invalidation to push live updates via Server-Sent Events (SSE).
|
|
16
|
+
TanStack Query polling (60s) acts as a fallback. Action buttons trigger
|
|
17
|
+
selftune commands directly from the dashboard. Use `selftune export` to
|
|
18
|
+
generate JSONL from SQLite for debugging or offline analysis.
|
|
22
19
|
|
|
23
20
|
## Options
|
|
24
21
|
|
|
@@ -56,11 +53,9 @@ override.
|
|
|
56
53
|
### Live Updates (SSE)
|
|
57
54
|
|
|
58
55
|
The dashboard connects to `/api/v2/events` via Server-Sent Events.
|
|
59
|
-
|
|
60
|
-
`update` event. The SPA invalidates all cached
|
|
61
|
-
immediate refetches
|
|
62
|
-
footer and Status page will warn when the server is still in this
|
|
63
|
-
legacy JSONL watcher mode.
|
|
56
|
+
The server watches the SQLite WAL file for changes and broadcasts an
|
|
57
|
+
`update` event when new data is written. The SPA invalidates all cached
|
|
58
|
+
queries, triggering immediate refetches (~1s latency).
|
|
64
59
|
|
|
65
60
|
TanStack Query polling (60s) acts as a fallback safety net in case the
|
|
66
61
|
SSE connection drops. Data also refreshes on window focus.
|
|
@@ -40,14 +40,14 @@ None. Doctor runs all checks unconditionally.
|
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"name": "dashboard_freshness_mode",
|
|
43
|
-
"status": "
|
|
44
|
-
"message": "Dashboard
|
|
43
|
+
"status": "pass",
|
|
44
|
+
"message": "Dashboard reads SQLite and watches WAL for live updates"
|
|
45
45
|
}
|
|
46
46
|
],
|
|
47
47
|
"summary": {
|
|
48
|
-
"pass":
|
|
48
|
+
"pass": 9,
|
|
49
49
|
"fail": 1,
|
|
50
|
-
"warn":
|
|
50
|
+
"warn": 0,
|
|
51
51
|
"total": 10
|
|
52
52
|
},
|
|
53
53
|
"healthy": false
|
|
@@ -183,9 +183,9 @@ for root cause analysis.
|
|
|
183
183
|
**Diagnostic steps:**
|
|
184
184
|
1. Check `selftune status` — look at "Alpha Upload" and "Cloud link" lines
|
|
185
185
|
2. If `doctor` includes a `cloud_link` or alpha queue warning, prefer `.checks[].guidance.next_command`
|
|
186
|
-
3. If "not enrolled" or "not linked": run `selftune init --alpha --alpha-email <email
|
|
187
|
-
4. If "enrolled (missing credential)": re-run `selftune init --alpha --alpha-email <email> --
|
|
188
|
-
5. If "api_key has invalid format":
|
|
186
|
+
3. If "not enrolled" or "not linked": run `selftune init --alpha --alpha-email <email>` (opens browser for device-code auth)
|
|
187
|
+
4. If "enrolled (missing credential)": re-run `selftune init --alpha --alpha-email <email> --force` (re-authenticates via browser)
|
|
188
|
+
5. If "api_key has invalid format": re-run init with `--alpha --force` to re-authenticate
|
|
189
189
|
|
|
190
190
|
**Resolution:** Follow the setup sequence in Initialize workflow → Alpha Enrollment section.
|
|
191
191
|
|
|
@@ -25,11 +25,10 @@ selftune init --no-alpha [--force]
|
|
|
25
25
|
| `--force` | Reinitialize even if config already exists | Off |
|
|
26
26
|
| `--enable-autonomy` | Enable autonomous scheduling during init | Off |
|
|
27
27
|
| `--schedule-format <fmt>` | Schedule format: `cron`, `launchd`, `systemd` | Auto-detected |
|
|
28
|
-
| `--alpha` | Enroll in the selftune alpha program | Off |
|
|
28
|
+
| `--alpha` | Enroll in the selftune alpha program (opens browser for device-code auth) | Off |
|
|
29
29
|
| `--no-alpha` | Unenroll from the alpha program (preserves user_id) | Off |
|
|
30
30
|
| `--alpha-email <email>` | Email for alpha enrollment (required with `--alpha`) | - |
|
|
31
31
|
| `--alpha-name <name>` | Display name for alpha enrollment | - |
|
|
32
|
-
| `--alpha-key <key>` | API key for cloud uploads (`st_live_*` format) | - |
|
|
33
32
|
|
|
34
33
|
## Output Format
|
|
35
34
|
|
|
@@ -46,9 +45,12 @@ Creates `~/.selftune/config.json`:
|
|
|
46
45
|
"alpha": {
|
|
47
46
|
"enrolled": true,
|
|
48
47
|
"user_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
48
|
+
"cloud_user_id": "cloud-uuid-...",
|
|
49
|
+
"cloud_org_id": "org-uuid-...",
|
|
49
50
|
"email": "user@example.com",
|
|
50
51
|
"display_name": "User Name",
|
|
51
|
-
"consent_timestamp": "2026-02-28T10:00:00Z"
|
|
52
|
+
"consent_timestamp": "2026-02-28T10:00:00Z",
|
|
53
|
+
"api_key": "<provisioned automatically via device-code flow>"
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
```
|
|
@@ -66,9 +68,12 @@ Creates `~/.selftune/config.json`:
|
|
|
66
68
|
| `alpha` | object? | Alpha program enrollment (present only if enrolled) |
|
|
67
69
|
| `alpha.enrolled` | boolean | Whether the user is currently enrolled |
|
|
68
70
|
| `alpha.user_id` | string | Stable UUID, generated once, preserved across reinits |
|
|
71
|
+
| `alpha.cloud_user_id` | string? | Cloud account UUID (set by device-code flow) |
|
|
72
|
+
| `alpha.cloud_org_id` | string? | Cloud organization UUID (set by device-code flow) |
|
|
69
73
|
| `alpha.email` | string? | Email provided at enrollment |
|
|
70
74
|
| `alpha.display_name` | string? | Optional display name |
|
|
71
75
|
| `alpha.consent_timestamp` | string | ISO 8601 timestamp of consent |
|
|
76
|
+
| `alpha.api_key` | string? | Upload credential (provisioned automatically by device-code flow) |
|
|
72
77
|
|
|
73
78
|
## Steps
|
|
74
79
|
|
|
@@ -195,79 +200,61 @@ Before running the alpha command:
|
|
|
195
200
|
The CLI stays non-interactive. The agent is responsible for collecting consent
|
|
196
201
|
and the required `--alpha-email` value before invoking the command.
|
|
197
202
|
|
|
198
|
-
## Alpha Enrollment (
|
|
203
|
+
## Alpha Enrollment (Device-Code Flow)
|
|
199
204
|
|
|
200
205
|
The alpha program sends canonical telemetry to the selftune cloud for analysis.
|
|
201
|
-
|
|
206
|
+
Enrollment uses a device-code flow — one command, one browser approval, fully automatic.
|
|
202
207
|
|
|
203
208
|
### Setup Sequence
|
|
204
209
|
|
|
205
210
|
1. **Check local config**: Run `selftune status` — look for the "Alpha Upload" section
|
|
206
|
-
2. **If not linked**:
|
|
207
|
-
> To join the selftune alpha program, you need to create an account at https://app.selftune.dev and issue an upload credential. This is a one-time step — afterwards everything runs locally through the CLI.
|
|
208
|
-
3. **User completes cloud enrollment**: Signs in, enrolls, copies the `st_live_*` credential
|
|
209
|
-
4. **Store credential locally**:
|
|
211
|
+
2. **If not linked**: Collect the user's email and run:
|
|
210
212
|
|
|
211
213
|
```bash
|
|
212
|
-
selftune init --alpha --alpha-email <user-email> --
|
|
214
|
+
selftune init --alpha --alpha-email <user-email> --force
|
|
213
215
|
```
|
|
214
216
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
3. **Browser opens automatically**: The CLI requests a device code, opens the verification URL in the browser with the code pre-filled, and polls for approval.
|
|
218
|
+
4. **User approves in browser**: One click to authorize.
|
|
219
|
+
5. **CLI receives credentials**: API key, cloud_user_id, and org_id are automatically provisioned and stored in `~/.selftune/config.json` with `0600` permissions.
|
|
220
|
+
6. **Verify readiness**: The init command prints a readiness check. If all checks pass, alpha upload is active.
|
|
221
|
+
The readiness JSON includes a `guidance` object with:
|
|
217
222
|
- `message`
|
|
218
223
|
- `next_command`
|
|
219
224
|
- `suggested_commands[]`
|
|
220
225
|
- `blocking`
|
|
221
|
-
|
|
222
|
-
- `
|
|
223
|
-
-
|
|
224
|
-
- `not enrolled` → re-run init with `--alpha --alpha-email <email> --alpha-key <key>`
|
|
226
|
+
7. **If readiness fails**: Run `selftune doctor` to diagnose. Common issues:
|
|
227
|
+
- `not enrolled` → re-run `selftune init --alpha --alpha-email <email> --force`
|
|
228
|
+
- Device-code expired → re-run the init command (codes expire after ~15 minutes)
|
|
225
229
|
|
|
226
230
|
### Key Principle
|
|
227
231
|
|
|
228
|
-
The cloud app is used **only** for
|
|
229
|
-
- Sign-in
|
|
230
|
-
- Alpha enrollment
|
|
231
|
-
- Upload credential issuance
|
|
232
|
-
|
|
233
|
-
All other selftune operations happen through the local CLI and this agent.
|
|
232
|
+
The cloud app is used **only** for the one-time browser approval during device-code auth. All other selftune operations happen through the local CLI and this agent.
|
|
234
233
|
|
|
235
234
|
### Enroll
|
|
236
235
|
|
|
237
236
|
```bash
|
|
238
237
|
selftune init --alpha --alpha-email user@example.com --alpha-name "User Name" --force
|
|
239
|
-
selftune init --alpha-key st_live_abc123... # after enrollment, store the API key
|
|
240
238
|
```
|
|
241
239
|
|
|
242
240
|
The `--alpha-email` flag is required. The command will:
|
|
243
241
|
1. Generate a stable UUID (preserved across reinits)
|
|
244
|
-
2.
|
|
245
|
-
3.
|
|
246
|
-
4.
|
|
247
|
-
5.
|
|
242
|
+
2. Request a device code from the cloud API
|
|
243
|
+
3. Open the browser to the verification URL
|
|
244
|
+
4. Poll until the user approves
|
|
245
|
+
5. Receive and store the API key, cloud_user_id, and org_id automatically
|
|
246
|
+
6. Write the alpha block to `~/.selftune/config.json` with `0600` permissions
|
|
247
|
+
7. Print an `alpha_enrolled` JSON message to stdout
|
|
248
|
+
8. Print the consent notice to stderr
|
|
248
249
|
|
|
249
250
|
The consent notice explicitly states that the friendly alpha cohort shares raw
|
|
250
251
|
prompt/query text in addition to skill/session/evolution metadata.
|
|
251
252
|
|
|
252
|
-
### API Key Provisioning
|
|
253
|
-
|
|
254
|
-
After enrollment, users need to configure an API key for cloud uploads:
|
|
255
|
-
|
|
256
|
-
1. Create a cloud account at the selftune web app
|
|
257
|
-
2. Generate an API key (format: `st_live_*`)
|
|
258
|
-
3. Store the key locally:
|
|
259
|
-
|
|
260
|
-
```bash
|
|
261
|
-
selftune init --alpha --alpha-email <email> --alpha-key st_live_abc123... --force
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
Without an API key, alpha enrollment is recorded locally but no uploads are attempted. When a key is stored, selftune tightens the local config file permissions to `0600`.
|
|
265
|
-
|
|
266
253
|
### Upload Behavior
|
|
267
254
|
|
|
268
|
-
Once enrolled
|
|
269
|
-
|
|
270
|
-
|
|
255
|
+
Once enrolled, `selftune orchestrate` automatically uploads new session,
|
|
256
|
+
invocation, and evolution data to the cloud API at the end of each run.
|
|
257
|
+
This upload step is fail-open -- errors never block the orchestrate loop.
|
|
271
258
|
Use `selftune alpha upload` for manual uploads or `selftune alpha upload --dry-run`
|
|
272
259
|
to preview what would be sent.
|
|
273
260
|
|
|
@@ -298,23 +285,9 @@ If `--alpha` is passed without `--alpha-email`, the CLI throws a JSON error:
|
|
|
298
285
|
}
|
|
299
286
|
```
|
|
300
287
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
{
|
|
305
|
-
"alpha_readiness": {
|
|
306
|
-
"ready": false,
|
|
307
|
-
"missing": ["api_key not set"],
|
|
308
|
-
"guidance": {
|
|
309
|
-
"code": "alpha_credential_required",
|
|
310
|
-
"message": "Alpha enrollment exists, but the local upload credential is missing or invalid.",
|
|
311
|
-
"next_command": "selftune init --alpha --alpha-email user@example.com --alpha-key <st_live_key> --force",
|
|
312
|
-
"suggested_commands": ["selftune status", "selftune doctor"],
|
|
313
|
-
"blocking": true
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
```
|
|
288
|
+
If the device-code flow fails (network error, timeout, user denied), the CLI throws
|
|
289
|
+
with a descriptive error message. The agent should relay this to the user and suggest
|
|
290
|
+
retrying with `selftune init --alpha --alpha-email <email> --force`.
|
|
318
291
|
|
|
319
292
|
## Common Patterns
|
|
320
293
|
|
|
@@ -326,7 +299,7 @@ When alpha readiness is evaluated after `selftune init --alpha`, the CLI emits:
|
|
|
326
299
|
**User wants alpha enrollment**
|
|
327
300
|
> Ask whether they want to opt into alpha data sharing. If yes, collect email
|
|
328
301
|
> and optional display name, then run `selftune init --alpha --alpha-email ...`.
|
|
329
|
-
>
|
|
302
|
+
> The browser opens automatically for approval. No manual key management needed.
|
|
330
303
|
|
|
331
304
|
**Hooks not capturing data**
|
|
332
305
|
> Run `selftune doctor` to check hook installation. Parse the JSON output
|
package/skill/references/logs.md
CHANGED
|
@@ -4,6 +4,12 @@ selftune writes raw legacy logs plus a canonical event log. This reference
|
|
|
4
4
|
describes each format in detail for the skill to use when parsing sessions,
|
|
5
5
|
audit trails, and cloud-ingest exports.
|
|
6
6
|
|
|
7
|
+
> **Note:** JSONL files are now backup/recovery only. SQLite (`~/.selftune/selftune.db`)
|
|
8
|
+
> is the sole operational store for all runtime reads. JSONL writes are retained for
|
|
9
|
+
> append-only durability, but all dashboard queries, hook reads, grading, monitoring,
|
|
10
|
+
> and upload staging read from SQLite. JSONL reads only occur when custom log paths
|
|
11
|
+
> are provided (e.g., `--telemetry-log`, `--skill-log`) for test isolation.
|
|
12
|
+
|
|
7
13
|
---
|
|
8
14
|
|
|
9
15
|
## ~/.claude/session_telemetry_log.jsonl
|