@etiquekit/etq 1.0.13 → 1.0.15
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/AGENTS.md +26 -19
- package/LICENSE +3 -2
- package/NOTICE +4 -6
- package/QuickStart.md +46 -35
- package/README.md +57 -55
- package/bin/etiquette +5 -1
- package/bin/etiquette-core +5 -1
- package/docs/ARCHITECTURE.md +17 -20
- package/docs/CONCEPTS.md +5 -5
- package/docs/CORE_PROFILE.md +15 -7
- package/docs/LANE_PROVISIONING.md +2 -7
- package/docs/README.md +14 -13
- package/docs/RELEASE_SURFACE_AUDIT.md +6 -7
- package/docs/SEAT_DISCIPLINE.md +91 -55
- package/docs/SEAT_PROVISIONING.md +19 -16
- package/docs/TEAM_HANDOFF.md +21 -21
- package/docs/WORKTREE_QOL.md +0 -7
- package/docs/contracts/ledger-entry/README.md +11 -11
- package/docs/contracts/ledger-entry/ledger-entry.v0.2.md +2 -2
- package/docs/contracts/ledger-entry/ledger-entry.v0.md +8 -8
- package/lib/etiquette-core.js +313 -0
- package/lib/etiquette.js +1151 -0
- package/package.json +13 -12
- package/templates/etiquette-vanilla-v0/README.md +3 -2
- package/templates/etiquette-vanilla-v0/source/control-seat/README.md +6 -0
- package/templates/etiquette-vanilla-v0/source/control-seat/bin/access-assurance-check +55 -0
- package/templates/etiquette-vanilla-v0/source/control-seat/bin/seat-doctor +5 -0
- package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/access/ACCESS-ASSURANCE.md +39 -0
- package/templates/etiquette-vanilla-v0/source/control-seat/docs/work/access/workspace-secure-profile.v0.json +53 -0
- package/templates/etiquette-vanilla-v0/validate-vanilla.sh +5 -0
- package/templates/hosted-receiver/README.md +41 -0
- package/templates/hosted-receiver/w1-github-webhook-receiver.mjs +129 -0
- package/templates/seat-packs-v0/README.md +2 -3
- package/templates/seat-packs-v0/source/claude-code-seat/README.md +3 -3
- package/templates/seat-packs-v0/source/codex-seat/README.md +3 -3
- package/templates/seat-packs-v0/source/gemini-seat/README.md +3 -3
- package/templates/seat-packs-v0/source/ollama-seat/README.md +3 -3
- package/templates/seat-packs-v0/source/openrouter-seat/README.md +3 -3
- package/docs/CONCEPT_STATUS.md +0 -66
- package/packages/control/src/authority/lease.ts +0 -261
- package/packages/control/src/authority/node-delegation.ts +0 -257
- package/packages/control/src/authority/rig-conductor.ts +0 -632
- package/packages/control/src/cli/argv.ts +0 -155
- package/packages/control/src/cli/commands/console.ts +0 -200
- package/packages/control/src/cli/commands/dispatch-core.ts +0 -89
- package/packages/control/src/cli/commands/dispatch.ts +0 -279
- package/packages/control/src/cli/commands/harness.ts +0 -89
- package/packages/control/src/cli/commands/hook.ts +0 -50
- package/packages/control/src/cli/commands/ledger.ts +0 -91
- package/packages/control/src/cli/commands/local-workflow.ts +0 -4686
- package/packages/control/src/cli/commands/memory.ts +0 -445
- package/packages/control/src/cli/commands/release.ts +0 -108
- package/packages/control/src/cli/commands/rubric.ts +0 -103
- package/packages/control/src/cli/commands/seat.ts +0 -179
- package/packages/control/src/cli/commands/session.ts +0 -127
- package/packages/control/src/cli/commands/supervision.ts +0 -246
- package/packages/control/src/cli/commands/sync.ts +0 -119
- package/packages/control/src/cli/commands/workflow.ts +0 -86
- package/packages/control/src/cli/commands/workspace.ts +0 -50
- package/packages/control/src/cli/core-usage.ts +0 -34
- package/packages/control/src/cli/prompt.ts +0 -67
- package/packages/control/src/cli/supervision-deps.ts +0 -44
- package/packages/control/src/cli/usage.ts +0 -241
- package/packages/control/src/cli.ts +0 -207
- package/packages/control/src/core-cli.ts +0 -50
- package/packages/control/src/dispatch/decision.ts +0 -202
- package/packages/control/src/dispatch/projection.ts +0 -293
- package/packages/control/src/dispatch/record.ts +0 -153
- package/packages/control/src/engagement/project.ts +0 -170
- package/packages/control/src/fs.ts +0 -19
- package/packages/control/src/harness/pruning.ts +0 -406
- package/packages/control/src/hooks/dispatcher.ts +0 -117
- package/packages/control/src/hooks/outbox.ts +0 -86
- package/packages/control/src/hooks/sanitize.ts +0 -6
- package/packages/control/src/hooks/types.ts +0 -34
- package/packages/control/src/index.ts +0 -384
- package/packages/control/src/ledger/entry.ts +0 -303
- package/packages/control/src/ledger/indexer.ts +0 -542
- package/packages/control/src/memory/context.ts +0 -149
- package/packages/control/src/memory/drain-import.ts +0 -207
- package/packages/control/src/memory/indexer.ts +0 -284
- package/packages/control/src/memory/query.ts +0 -75
- package/packages/control/src/memory/sanitize.ts +0 -50
- package/packages/control/src/memory/sharded-drain-import.ts +0 -212
- package/packages/control/src/memory/status.ts +0 -211
- package/packages/control/src/memory/store-lifecycle.ts +0 -509
- package/packages/control/src/memory/store.ts +0 -284
- package/packages/control/src/memory/types.ts +0 -146
- package/packages/control/src/parity/surfaces.ts +0 -748
- package/packages/control/src/project.ts +0 -141
- package/packages/control/src/projection/local-ledger-view.ts +0 -373
- package/packages/control/src/projection/return-enforcement.ts +0 -48
- package/packages/control/src/projection/timeline-preview.ts +0 -539
- package/packages/control/src/projection/timeline.ts +0 -708
- package/packages/control/src/release/readiness.ts +0 -831
- package/packages/control/src/rubric/loader.ts +0 -326
- package/packages/control/src/rubric/promotion.ts +0 -54
- package/packages/control/src/rubric/runner.ts +0 -159
- package/packages/control/src/rubric/types.ts +0 -158
- package/packages/control/src/seat/owner-card.ts +0 -388
- package/packages/control/src/seat/readiness.ts +0 -834
- package/packages/control/src/session/runbook.ts +0 -431
- package/packages/control/src/shared/sanitize.ts +0 -49
- package/packages/control/src/supervision/action-classes.ts +0 -192
- package/packages/control/src/supervision/command-apply.ts +0 -378
- package/packages/control/src/supervision/errors.ts +0 -14
- package/packages/control/src/supervision/event-replay.ts +0 -155
- package/packages/control/src/supervision/events.ts +0 -109
- package/packages/control/src/supervision/index.ts +0 -16
- package/packages/control/src/supervision/manifest.ts +0 -127
- package/packages/control/src/supervision/paths.ts +0 -49
- package/packages/control/src/supervision/projection-adapter.ts +0 -274
- package/packages/control/src/supervision/projection.ts +0 -75
- package/packages/control/src/supervision/rebuild.ts +0 -99
- package/packages/control/src/supervision/session-open.ts +0 -131
- package/packages/control/src/supervision/session-read.ts +0 -99
- package/packages/control/src/supervision/sqlite-impl.ts +0 -71
- package/packages/control/src/supervision/sqlite.ts +0 -121
- package/packages/control/src/supervision/store-rows.ts +0 -371
- package/packages/control/src/supervision/turn-close.ts +0 -154
- package/packages/control/src/supervision/turn-open.ts +0 -284
- package/packages/control/src/sync/event-log-merge-driver.ts +0 -263
- package/packages/control/src/sync/join-plan.ts +0 -375
- package/packages/control/src/sync/outbox.ts +0 -492
- package/packages/control/src/workflow/evaluator.ts +0 -140
- package/packages/control/src/workflow/loader.ts +0 -200
- package/packages/control/src/workflow/types.ts +0 -90
- package/packages/control/src/workspace/authority.ts +0 -499
- package/packages/protocol/src/guards.ts +0 -119
- package/packages/protocol/src/huddle-board.ts +0 -198
- package/packages/protocol/src/huddle.ts +0 -295
- package/packages/protocol/src/incident.ts +0 -251
- package/packages/protocol/src/index.ts +0 -8
- package/packages/protocol/src/interfaces.ts +0 -107
- package/packages/protocol/src/packet-profile.ts +0 -195
- package/packages/protocol/src/state.ts +0 -81
- package/packages/protocol/src/types.ts +0 -434
- package/release/lineage.v0.json +0 -14
- package/scripts/release-candidate-verify.sh +0 -175
- package/scripts/release-checksum.sh +0 -25
- package/scripts/release-pack-canary.sh +0 -97
- package/scripts/release-scan.sh +0 -249
- package/scripts/release-sign.sh +0 -34
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
export const INCIDENT_SEVERITIES = ['sev0', 'sev1', 'sev2', 'sev3', 'sev4'] as const;
|
|
4
|
-
export const INCIDENT_STATUSES = ['detected', 'acknowledged', 'mitigating', 'resolved', 'closed'] as const;
|
|
5
|
-
export const INCIDENT_BUSINESS_CONTEXT_STATUSES = ['absent', 'partial', 'provided'] as const;
|
|
6
|
-
export const INCIDENT_ROOT_CAUSE_STATUSES = ['unknown', 'suspected', 'confirmed'] as const;
|
|
7
|
-
export const INCIDENT_FOLLOW_UP_STATUSES = ['open', 'deferred', 'done'] as const;
|
|
8
|
-
|
|
9
|
-
export type IncidentSeverity = (typeof INCIDENT_SEVERITIES)[number];
|
|
10
|
-
export type IncidentStatus = (typeof INCIDENT_STATUSES)[number];
|
|
11
|
-
export type IncidentBusinessContextStatus = (typeof INCIDENT_BUSINESS_CONTEXT_STATUSES)[number];
|
|
12
|
-
export type IncidentRootCauseStatus = (typeof INCIDENT_ROOT_CAUSE_STATUSES)[number];
|
|
13
|
-
export type IncidentFollowUpStatus = (typeof INCIDENT_FOLLOW_UP_STATUSES)[number];
|
|
14
|
-
|
|
15
|
-
const nonEmptyString = z.string().trim().min(1);
|
|
16
|
-
const stringList = z.array(nonEmptyString);
|
|
17
|
-
|
|
18
|
-
function looksLikeChatMemoryPath(value: string): boolean {
|
|
19
|
-
const normalized = value.toLowerCase();
|
|
20
|
-
return (
|
|
21
|
-
normalized.includes('chat-history') ||
|
|
22
|
-
normalized.includes('chat_history') ||
|
|
23
|
-
normalized.includes('chatlog') ||
|
|
24
|
-
normalized.includes('chat-memory') ||
|
|
25
|
-
normalized.includes('conversation-log') ||
|
|
26
|
-
normalized.includes('transcript')
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function addEvidenceRefIssues(refs: string[], path: (string | number)[], ctx: z.RefinementCtx): void {
|
|
31
|
-
for (const [index, ref] of refs.entries()) {
|
|
32
|
-
if (looksLikeChatMemoryPath(ref)) {
|
|
33
|
-
ctx.addIssue({
|
|
34
|
-
code: 'custom',
|
|
35
|
-
path: [...path, index],
|
|
36
|
-
message: 'incident evidence_refs must point to durable proof, not chat logs or transcripts',
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const IncidentBusinessContextV1Schema = z.object({
|
|
43
|
-
customer_impact: nonEmptyString.optional(),
|
|
44
|
-
sla_tier: nonEmptyString.optional(),
|
|
45
|
-
revenue_exposure: nonEmptyString.optional(),
|
|
46
|
-
compliance_implication: nonEmptyString.optional(),
|
|
47
|
-
}).default({});
|
|
48
|
-
|
|
49
|
-
const IncidentTimelineEventV1Schema = z.object({
|
|
50
|
-
at: nonEmptyString,
|
|
51
|
-
actor: nonEmptyString,
|
|
52
|
-
action: nonEmptyString,
|
|
53
|
-
evidence_refs: z.array(nonEmptyString).default([]),
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const IncidentDecisionV1Schema = z.object({
|
|
57
|
-
at: nonEmptyString,
|
|
58
|
-
actor: nonEmptyString,
|
|
59
|
-
decision: nonEmptyString,
|
|
60
|
-
rationale: nonEmptyString,
|
|
61
|
-
evidence_refs: z.array(nonEmptyString).default([]),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const IncidentFollowUpV1Schema = z.object({
|
|
65
|
-
id: nonEmptyString,
|
|
66
|
-
title: nonEmptyString,
|
|
67
|
-
owner: nonEmptyString,
|
|
68
|
-
status: z.enum(INCIDENT_FOLLOW_UP_STATUSES),
|
|
69
|
-
evidence_refs: z.array(nonEmptyString).default([]),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
export const IncidentReceiptV1Schema = z.object({
|
|
73
|
-
schema_version: z.literal('incident-receipt.v1'),
|
|
74
|
-
incident_id: nonEmptyString,
|
|
75
|
-
title: nonEmptyString,
|
|
76
|
-
severity: z.enum(INCIDENT_SEVERITIES),
|
|
77
|
-
severity_rationale: nonEmptyString.optional(),
|
|
78
|
-
status: z.enum(INCIDENT_STATUSES),
|
|
79
|
-
incident_class: nonEmptyString.optional(),
|
|
80
|
-
started_at: nonEmptyString,
|
|
81
|
-
detected_at: nonEmptyString,
|
|
82
|
-
acknowledged_at: nonEmptyString.optional(),
|
|
83
|
-
resolved_at: nonEmptyString.optional(),
|
|
84
|
-
acknowledged_by: nonEmptyString.optional(),
|
|
85
|
-
resolved_by: nonEmptyString.optional(),
|
|
86
|
-
detection_source: nonEmptyString.optional(),
|
|
87
|
-
notification_chain: z.array(nonEmptyString).default([]),
|
|
88
|
-
affected_systems: stringList.min(1),
|
|
89
|
-
symptoms: stringList.min(1),
|
|
90
|
-
timeline: z.array(IncidentTimelineEventV1Schema).min(1),
|
|
91
|
-
decisions: z.array(IncidentDecisionV1Schema).default([]),
|
|
92
|
-
evidence_refs: stringList.min(1),
|
|
93
|
-
mitigation: nonEmptyString.optional(),
|
|
94
|
-
rollback_or_fix_refs: z.array(nonEmptyString).default([]),
|
|
95
|
-
validation_refs: z.array(nonEmptyString).default([]),
|
|
96
|
-
follow_ups: z.array(IncidentFollowUpV1Schema).default([]),
|
|
97
|
-
business_context_status: z.enum(INCIDENT_BUSINESS_CONTEXT_STATUSES),
|
|
98
|
-
business_context: IncidentBusinessContextV1Schema,
|
|
99
|
-
root_cause_status: z.enum(INCIDENT_ROOT_CAUSE_STATUSES).default('unknown'),
|
|
100
|
-
root_cause_evidence_refs: z.array(nonEmptyString).default([]),
|
|
101
|
-
runbook_executed_ref: nonEmptyString.optional(),
|
|
102
|
-
}).superRefine((receipt, ctx) => {
|
|
103
|
-
addEvidenceRefIssues(receipt.evidence_refs, ['evidence_refs'], ctx);
|
|
104
|
-
addEvidenceRefIssues(receipt.rollback_or_fix_refs, ['rollback_or_fix_refs'], ctx);
|
|
105
|
-
addEvidenceRefIssues(receipt.validation_refs, ['validation_refs'], ctx);
|
|
106
|
-
addEvidenceRefIssues(receipt.root_cause_evidence_refs, ['root_cause_evidence_refs'], ctx);
|
|
107
|
-
|
|
108
|
-
for (const [index, event] of receipt.timeline.entries()) {
|
|
109
|
-
addEvidenceRefIssues(event.evidence_refs, ['timeline', index, 'evidence_refs'], ctx);
|
|
110
|
-
}
|
|
111
|
-
for (const [index, decision] of receipt.decisions.entries()) {
|
|
112
|
-
addEvidenceRefIssues(decision.evidence_refs, ['decisions', index, 'evidence_refs'], ctx);
|
|
113
|
-
}
|
|
114
|
-
for (const [index, followUp] of receipt.follow_ups.entries()) {
|
|
115
|
-
addEvidenceRefIssues(followUp.evidence_refs, ['follow_ups', index, 'evidence_refs'], ctx);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (receipt.runbook_executed_ref && looksLikeChatMemoryPath(receipt.runbook_executed_ref)) {
|
|
119
|
-
ctx.addIssue({
|
|
120
|
-
code: 'custom',
|
|
121
|
-
path: ['runbook_executed_ref'],
|
|
122
|
-
message: 'runbook_executed_ref must point to a durable runbook receipt or proof ref',
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (['resolved', 'closed'].includes(receipt.status)) {
|
|
127
|
-
if (!receipt.resolved_at) {
|
|
128
|
-
ctx.addIssue({
|
|
129
|
-
code: 'custom',
|
|
130
|
-
path: ['resolved_at'],
|
|
131
|
-
message: 'resolved or closed incidents must include resolved_at',
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
if (!receipt.resolved_by) {
|
|
135
|
-
ctx.addIssue({
|
|
136
|
-
code: 'custom',
|
|
137
|
-
path: ['resolved_by'],
|
|
138
|
-
message: 'resolved or closed incidents must include resolved_by',
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (receipt.root_cause_status === 'confirmed' && receipt.root_cause_evidence_refs.length === 0) {
|
|
144
|
-
ctx.addIssue({
|
|
145
|
-
code: 'custom',
|
|
146
|
-
path: ['root_cause_evidence_refs'],
|
|
147
|
-
message: 'confirmed root cause requires at least one root_cause_evidence_ref',
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const context = receipt.business_context;
|
|
152
|
-
const contextValues = [
|
|
153
|
-
context.customer_impact,
|
|
154
|
-
context.sla_tier,
|
|
155
|
-
context.revenue_exposure,
|
|
156
|
-
context.compliance_implication,
|
|
157
|
-
].filter(Boolean);
|
|
158
|
-
|
|
159
|
-
if (receipt.business_context_status === 'absent' && contextValues.length > 0) {
|
|
160
|
-
ctx.addIssue({
|
|
161
|
-
code: 'custom',
|
|
162
|
-
path: ['business_context_status'],
|
|
163
|
-
message: 'business_context_status absent cannot carry incident business context fields',
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (receipt.business_context_status === 'provided' && contextValues.length < 4) {
|
|
168
|
-
ctx.addIssue({
|
|
169
|
-
code: 'custom',
|
|
170
|
-
path: ['business_context'],
|
|
171
|
-
message: 'provided business context requires customer_impact, sla_tier, revenue_exposure, and compliance_implication',
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
export type IncidentReceiptV1 = z.infer<typeof IncidentReceiptV1Schema>;
|
|
177
|
-
|
|
178
|
-
export const IncidentSummaryV1Schema = z.object({
|
|
179
|
-
schema_version: z.literal('incident-summary.v1'),
|
|
180
|
-
incident_id: nonEmptyString,
|
|
181
|
-
status: z.enum(INCIDENT_STATUSES),
|
|
182
|
-
severity: z.enum(INCIDENT_SEVERITIES),
|
|
183
|
-
started_at: nonEmptyString,
|
|
184
|
-
resolved_at: nonEmptyString.optional(),
|
|
185
|
-
duration_minutes: z.number().int().min(0).optional(),
|
|
186
|
-
affected_systems: stringList.min(1),
|
|
187
|
-
primary_symptom: nonEmptyString,
|
|
188
|
-
mitigation_summary: nonEmptyString.optional(),
|
|
189
|
-
evidence_refs: stringList.min(1),
|
|
190
|
-
follow_up_count: z.number().int().min(0),
|
|
191
|
-
business_context_status: z.enum(INCIDENT_BUSINESS_CONTEXT_STATUSES),
|
|
192
|
-
root_cause_status: z.enum(INCIDENT_ROOT_CAUSE_STATUSES),
|
|
193
|
-
}).superRefine((summary, ctx) => {
|
|
194
|
-
addEvidenceRefIssues(summary.evidence_refs, ['evidence_refs'], ctx);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
export type IncidentSummaryV1 = z.infer<typeof IncidentSummaryV1Schema>;
|
|
198
|
-
|
|
199
|
-
export function parseIncidentReceiptV1(value: unknown): IncidentReceiptV1 {
|
|
200
|
-
return IncidentReceiptV1Schema.parse(value);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export function validateIncidentReceiptV1(value: unknown): void {
|
|
204
|
-
IncidentReceiptV1Schema.parse(value);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function parseIncidentSummaryV1(value: unknown): IncidentSummaryV1 {
|
|
208
|
-
return IncidentSummaryV1Schema.parse(value);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function validateIncidentSummaryV1(value: unknown): void {
|
|
212
|
-
IncidentSummaryV1Schema.parse(value);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function durationMinutes(startedAt: string, resolvedAt?: string): number | undefined {
|
|
216
|
-
if (!resolvedAt) return undefined;
|
|
217
|
-
|
|
218
|
-
const startMs = Date.parse(startedAt);
|
|
219
|
-
const resolvedMs = Date.parse(resolvedAt);
|
|
220
|
-
if (!Number.isFinite(startMs) || !Number.isFinite(resolvedMs) || resolvedMs < startMs) {
|
|
221
|
-
return undefined;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return Math.round((resolvedMs - startMs) / 60000);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function toIncidentSummaryV1(receipt: IncidentReceiptV1): IncidentSummaryV1 {
|
|
228
|
-
const parsed = IncidentReceiptV1Schema.parse(receipt);
|
|
229
|
-
|
|
230
|
-
return IncidentSummaryV1Schema.parse({
|
|
231
|
-
schema_version: 'incident-summary.v1',
|
|
232
|
-
incident_id: parsed.incident_id,
|
|
233
|
-
status: parsed.status,
|
|
234
|
-
severity: parsed.severity,
|
|
235
|
-
started_at: parsed.started_at,
|
|
236
|
-
resolved_at: parsed.resolved_at,
|
|
237
|
-
duration_minutes: durationMinutes(parsed.started_at, parsed.resolved_at),
|
|
238
|
-
affected_systems: parsed.affected_systems,
|
|
239
|
-
primary_symptom: parsed.symptoms[0],
|
|
240
|
-
mitigation_summary: parsed.mitigation,
|
|
241
|
-
evidence_refs: [
|
|
242
|
-
...parsed.evidence_refs,
|
|
243
|
-
...parsed.rollback_or_fix_refs,
|
|
244
|
-
...parsed.validation_refs,
|
|
245
|
-
...parsed.root_cause_evidence_refs,
|
|
246
|
-
],
|
|
247
|
-
follow_up_count: parsed.follow_ups.length,
|
|
248
|
-
business_context_status: parsed.business_context_status,
|
|
249
|
-
root_cause_status: parsed.root_cause_status,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
EvidenceBundle,
|
|
3
|
-
ProjectManifest,
|
|
4
|
-
Slice,
|
|
5
|
-
SliceStatusSnapshot,
|
|
6
|
-
ScrutinyKind,
|
|
7
|
-
SliceTask,
|
|
8
|
-
ScrutinyState,
|
|
9
|
-
TaskPacketSpec,
|
|
10
|
-
TaskState,
|
|
11
|
-
Verdict,
|
|
12
|
-
SupervisionSession,
|
|
13
|
-
SupervisionTurn,
|
|
14
|
-
SupervisionCommandRecord,
|
|
15
|
-
SupervisionEvent,
|
|
16
|
-
SupervisionProjection,
|
|
17
|
-
} from './types';
|
|
18
|
-
|
|
19
|
-
export interface ScrutinyLink {
|
|
20
|
-
scrutinyId: string;
|
|
21
|
-
path: string;
|
|
22
|
-
state: ScrutinyState;
|
|
23
|
-
kind?: ScrutinyKind;
|
|
24
|
-
blockedBy?: string[];
|
|
25
|
-
dependsOn?: string[];
|
|
26
|
-
readinessQuestion?: string;
|
|
27
|
-
readyForRevisit?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ScrutinyAdapter {
|
|
31
|
-
openOrLinkSlice(project: ProjectManifest, slice: Slice): Promise<ScrutinyLink>;
|
|
32
|
-
readSliceState(project: ProjectManifest, scrutinyId: string): Promise<ScrutinyLink>;
|
|
33
|
-
appendEvidence(project: ProjectManifest, scrutinyId: string, evidence: EvidenceBundle): Promise<void>;
|
|
34
|
-
renderArtifacts(project: ProjectManifest): Promise<void>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface HandshakeAdapter {
|
|
38
|
-
ensureStore(project: ProjectManifest): Promise<void>;
|
|
39
|
-
newPacket(spec: TaskPacketSpec): Promise<{ packetPath: string; taskFile: string }>;
|
|
40
|
-
respondPacket(project: ProjectManifest, packetId: string, status: 'ACCEPTED' | 'COUNTER', counterRound?: number): Promise<void>;
|
|
41
|
-
lockPacket(project: ProjectManifest, packetId: string): Promise<void>;
|
|
42
|
-
provisionPacket(project: ProjectManifest, packetId: string): Promise<void>;
|
|
43
|
-
syncTasks(project: ProjectManifest, args?: string[]): Promise<string>;
|
|
44
|
-
claimTask(project: ProjectManifest, taskId: string, owner: string): Promise<void>;
|
|
45
|
-
resolveTask(project: ProjectManifest, taskId: string, owner: string, note?: string): Promise<void>;
|
|
46
|
-
watchInbox(project: ProjectManifest): Promise<string[]>;
|
|
47
|
-
readPacketStates(project: ProjectManifest): Promise<Record<string, 'PROPOSED' | 'ACCEPTED' | 'LOCKED' | 'PROVISIONED'>>;
|
|
48
|
-
readTaskStates(project: ProjectManifest): Promise<Record<string, TaskState>>;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface LogixAdapter {
|
|
52
|
-
createDecisionContext(project: ProjectManifest, slice: Slice): Promise<string>;
|
|
53
|
-
requestVerdict(project: ProjectManifest, slice: Slice, contextId: string): Promise<string>;
|
|
54
|
-
recordVerdict(project: ProjectManifest, verdict: Verdict): Promise<string>;
|
|
55
|
-
recordWaiver(project: ProjectManifest, sliceId: string, note: string): Promise<string>;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface AgentRuntimeAdapter {
|
|
59
|
-
spawnWorker(project: ProjectManifest, task: SliceTask): Promise<string>;
|
|
60
|
-
bindWorktree(project: ProjectManifest, task: SliceTask, worktreePath: string, owner: string): Promise<string>;
|
|
61
|
-
captureReceipt(project: ProjectManifest, taskId: string, summary: string, evidencePaths: string[]): Promise<string>;
|
|
62
|
-
reportTaskState(project: ProjectManifest, taskId: string, state: TaskState, summary: string): Promise<string>;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export interface ControlDeps {
|
|
66
|
-
scrutiny: ScrutinyAdapter;
|
|
67
|
-
handshake: HandshakeAdapter;
|
|
68
|
-
logix: LogixAdapter;
|
|
69
|
-
agent: AgentRuntimeAdapter;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface ControlDeps {
|
|
73
|
-
scrutiny: ScrutinyAdapter;
|
|
74
|
-
handshake: HandshakeAdapter;
|
|
75
|
-
logix: LogixAdapter;
|
|
76
|
-
agent: AgentRuntimeAdapter;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ============================================================================
|
|
80
|
-
// Supervision (Phase 1)
|
|
81
|
-
// Authority: etiquette/internal/docs/supervision/PHASE1.md
|
|
82
|
-
// Doctrine: llm-substrate/governance/shared-live-supervision.md
|
|
83
|
-
// ============================================================================
|
|
84
|
-
|
|
85
|
-
export interface SupervisionSqliteHandle {
|
|
86
|
-
run(sql: string, params?: unknown[]): void;
|
|
87
|
-
all<T = unknown>(sql: string, params?: unknown[]): T[];
|
|
88
|
-
get<T = unknown>(sql: string, params?: unknown[]): T | undefined;
|
|
89
|
-
close(): void;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export interface SupervisionProjectionAdapter {
|
|
93
|
-
renderStatusBoard(projection: SupervisionProjection): Promise<void>;
|
|
94
|
-
renderSeatChannel(projection: SupervisionProjection, seatId: string): Promise<void>;
|
|
95
|
-
writeLatestProjection(projectionPath: string, projection: SupervisionProjection): Promise<void>;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export interface SupervisionDeps {
|
|
99
|
-
now(): Date;
|
|
100
|
-
randomId(): string;
|
|
101
|
-
readFile(path: string): Promise<string>;
|
|
102
|
-
writeFile(path: string, contents: string): Promise<void>;
|
|
103
|
-
appendFile(path: string, contents: string): Promise<void>;
|
|
104
|
-
mkdirp(path: string): Promise<void>;
|
|
105
|
-
openSqlite(path: string): SupervisionSqliteHandle;
|
|
106
|
-
projectionAdapter: SupervisionProjectionAdapter;
|
|
107
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { stringify as stringifyYaml } from 'yaml';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
const nonEmptyString = z.string().trim().min(1);
|
|
5
|
-
const profileKey = z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*$/);
|
|
6
|
-
const PacketProfileScalarV0Schema = z.union([z.string(), z.number(), z.boolean()]);
|
|
7
|
-
const PacketProfileValueV0Schema = z.union([
|
|
8
|
-
PacketProfileScalarV0Schema,
|
|
9
|
-
z.array(PacketProfileScalarV0Schema),
|
|
10
|
-
]);
|
|
11
|
-
|
|
12
|
-
const PacketProfileFieldV0Schema = z.object({
|
|
13
|
-
key: profileKey,
|
|
14
|
-
required: z.boolean().default(false),
|
|
15
|
-
default: PacketProfileValueV0Schema.optional(),
|
|
16
|
-
derive: nonEmptyString.optional(),
|
|
17
|
-
}).strict().superRefine((field, ctx) => {
|
|
18
|
-
if (field.default !== undefined && field.derive !== undefined) {
|
|
19
|
-
ctx.addIssue({
|
|
20
|
-
code: 'custom',
|
|
21
|
-
message: 'packet profile field cannot declare both default and derive',
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const PacketProfileSectionV0Schema = z.object({
|
|
27
|
-
key: profileKey,
|
|
28
|
-
heading: nonEmptyString,
|
|
29
|
-
required: z.boolean().default(false),
|
|
30
|
-
default: nonEmptyString.optional(),
|
|
31
|
-
derive: nonEmptyString.optional(),
|
|
32
|
-
}).strict().superRefine((section, ctx) => {
|
|
33
|
-
if (section.default !== undefined && section.derive !== undefined) {
|
|
34
|
-
ctx.addIssue({
|
|
35
|
-
code: 'custom',
|
|
36
|
-
message: 'packet profile section cannot declare both default and derive',
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export const PacketProfileV0Schema = z.object({
|
|
42
|
-
schema: z.literal('packet-profile.v0'),
|
|
43
|
-
format: z.literal('markdown-frontmatter'),
|
|
44
|
-
filename: z.object({
|
|
45
|
-
pattern: nonEmptyString,
|
|
46
|
-
extension: z.string().regex(/^\.[A-Za-z0-9]+$/).default('.md'),
|
|
47
|
-
}).strict(),
|
|
48
|
-
frontmatter: z.object({
|
|
49
|
-
fields: z.array(PacketProfileFieldV0Schema).min(1),
|
|
50
|
-
}).strict(),
|
|
51
|
-
title: z.object({
|
|
52
|
-
derive: nonEmptyString,
|
|
53
|
-
}).strict().optional(),
|
|
54
|
-
sections: z.array(PacketProfileSectionV0Schema).min(1),
|
|
55
|
-
}).strict().superRefine((profile, ctx) => {
|
|
56
|
-
const fieldKeys = new Set<string>();
|
|
57
|
-
for (const [index, field] of profile.frontmatter.fields.entries()) {
|
|
58
|
-
if (fieldKeys.has(field.key)) {
|
|
59
|
-
ctx.addIssue({
|
|
60
|
-
code: 'custom',
|
|
61
|
-
path: ['frontmatter', 'fields', index, 'key'],
|
|
62
|
-
message: `duplicate packet profile field: ${field.key}`,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
fieldKeys.add(field.key);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const sectionKeys = new Set<string>();
|
|
69
|
-
for (const [index, section] of profile.sections.entries()) {
|
|
70
|
-
if (sectionKeys.has(section.key)) {
|
|
71
|
-
ctx.addIssue({
|
|
72
|
-
code: 'custom',
|
|
73
|
-
path: ['sections', index, 'key'],
|
|
74
|
-
message: `duplicate packet profile section: ${section.key}`,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
sectionKeys.add(section.key);
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export type PacketProfileV0 = z.infer<typeof PacketProfileV0Schema>;
|
|
82
|
-
export type PacketProfileValueV0 = z.infer<typeof PacketProfileValueV0Schema>;
|
|
83
|
-
|
|
84
|
-
export interface RenderedPacketProfileV0 {
|
|
85
|
-
filename: string;
|
|
86
|
-
markdown: string;
|
|
87
|
-
frontmatter: Record<string, PacketProfileValueV0>;
|
|
88
|
-
sections: Array<{ key: string; heading: string; body: string }>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function isMissing(value: PacketProfileValueV0 | undefined): boolean {
|
|
92
|
-
return value === undefined || value === '';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function renderTemplate(
|
|
96
|
-
template: string,
|
|
97
|
-
values: Record<string, PacketProfileValueV0>,
|
|
98
|
-
): string {
|
|
99
|
-
const rendered = template.replace(/\{\{\s*([A-Za-z][A-Za-z0-9_-]*)\s*\}\}/g, (_match, key: string) => {
|
|
100
|
-
const value = values[key];
|
|
101
|
-
if (isMissing(value)) {
|
|
102
|
-
throw new Error(`packet profile template value is missing: ${key}`);
|
|
103
|
-
}
|
|
104
|
-
return Array.isArray(value) ? value.join(',') : String(value);
|
|
105
|
-
});
|
|
106
|
-
if (rendered.includes('{{') || rendered.includes('}}')) {
|
|
107
|
-
throw new Error(`invalid packet profile template: ${template}`);
|
|
108
|
-
}
|
|
109
|
-
return rendered;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function valueFor(
|
|
113
|
-
key: string,
|
|
114
|
-
input: Record<string, PacketProfileValueV0>,
|
|
115
|
-
context: Record<string, PacketProfileValueV0>,
|
|
116
|
-
fallback?: PacketProfileValueV0,
|
|
117
|
-
derive?: string,
|
|
118
|
-
): PacketProfileValueV0 | undefined {
|
|
119
|
-
if (derive !== undefined) return renderTemplate(derive, context);
|
|
120
|
-
if (input[key] !== undefined) return input[key];
|
|
121
|
-
if (fallback !== undefined) return fallback;
|
|
122
|
-
return undefined;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function packetFilename(
|
|
126
|
-
profile: PacketProfileV0,
|
|
127
|
-
context: Record<string, PacketProfileValueV0>,
|
|
128
|
-
): string {
|
|
129
|
-
const renderedStem = renderTemplate(profile.filename.pattern, context);
|
|
130
|
-
const stem = renderedStem
|
|
131
|
-
.replace(/[^A-Za-z0-9._-]+/g, '-')
|
|
132
|
-
.replace(/^-+|-+$/g, '');
|
|
133
|
-
if (!stem || stem === '.' || stem === '..') {
|
|
134
|
-
throw new Error(`packet profile produced an unsafe filename: ${renderedStem}`);
|
|
135
|
-
}
|
|
136
|
-
const filename = stem.endsWith(profile.filename.extension)
|
|
137
|
-
? stem
|
|
138
|
-
: `${stem}${profile.filename.extension}`;
|
|
139
|
-
if (!/^[A-Za-z0-9._-]+$/.test(filename) || filename === '.' || filename === '..') {
|
|
140
|
-
throw new Error(`packet profile produced an unsafe filename: ${filename}`);
|
|
141
|
-
}
|
|
142
|
-
return filename;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function parsePacketProfileV0(value: unknown): PacketProfileV0 {
|
|
146
|
-
return PacketProfileV0Schema.parse(value);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function renderPacketProfileV0(
|
|
150
|
-
profileInput: PacketProfileV0,
|
|
151
|
-
input: Record<string, PacketProfileValueV0>,
|
|
152
|
-
): RenderedPacketProfileV0 {
|
|
153
|
-
const profile = PacketProfileV0Schema.parse(profileInput);
|
|
154
|
-
const context: Record<string, PacketProfileValueV0> = { ...input };
|
|
155
|
-
const frontmatter: Record<string, PacketProfileValueV0> = {};
|
|
156
|
-
|
|
157
|
-
for (const field of profile.frontmatter.fields) {
|
|
158
|
-
const value = valueFor(field.key, input, context, field.default, field.derive);
|
|
159
|
-
if (field.required && isMissing(value)) {
|
|
160
|
-
throw new Error(`required packet profile field is missing: ${field.key}`);
|
|
161
|
-
}
|
|
162
|
-
if (value !== undefined) {
|
|
163
|
-
frontmatter[field.key] = value;
|
|
164
|
-
context[field.key] = value;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const sections: RenderedPacketProfileV0['sections'] = [];
|
|
169
|
-
for (const section of profile.sections) {
|
|
170
|
-
const value = valueFor(section.key, input, context, section.default, section.derive);
|
|
171
|
-
if (section.required && isMissing(value)) {
|
|
172
|
-
throw new Error(`required packet profile section is missing: ${section.key}`);
|
|
173
|
-
}
|
|
174
|
-
if (value !== undefined) {
|
|
175
|
-
const body = Array.isArray(value) ? value.join('\n') : String(value);
|
|
176
|
-
sections.push({ key: section.key, heading: section.heading, body });
|
|
177
|
-
context[section.key] = value;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const title = profile.title ? renderTemplate(profile.title.derive, context) : undefined;
|
|
182
|
-
const yaml = stringifyYaml(frontmatter, { lineWidth: 0 }).trimEnd();
|
|
183
|
-
const lines = ['---', yaml, '---', ''];
|
|
184
|
-
if (title) lines.push(`# ${title}`, '');
|
|
185
|
-
for (const section of sections) {
|
|
186
|
-
lines.push(`## ${section.heading}`, '', section.body, '');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return {
|
|
190
|
-
filename: packetFilename(profile, context),
|
|
191
|
-
markdown: `${lines.join('\n').trimEnd()}\n`,
|
|
192
|
-
frontmatter,
|
|
193
|
-
sections,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PACKET_STATES,
|
|
3
|
-
SCRUTINY_STATES,
|
|
4
|
-
TASK_STATES,
|
|
5
|
-
type PacketState,
|
|
6
|
-
type ScrutinyState,
|
|
7
|
-
type Slice,
|
|
8
|
-
type SliceStatusSnapshot,
|
|
9
|
-
type TaskState,
|
|
10
|
-
} from './types';
|
|
11
|
-
import { assertScrutinyState, validateSlice } from './guards';
|
|
12
|
-
|
|
13
|
-
function emptyCounts<T extends readonly string[]>(values: T): Record<T[number], number> {
|
|
14
|
-
return Object.fromEntries(values.map((value) => [value, 0])) as Record<T[number], number>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function aggregateSliceStatus(
|
|
18
|
-
slice: Slice,
|
|
19
|
-
scrutinyState: ScrutinyState,
|
|
20
|
-
packetStates: Record<string, PacketState>,
|
|
21
|
-
): SliceStatusSnapshot {
|
|
22
|
-
validateSlice(slice);
|
|
23
|
-
assertScrutinyState(scrutinyState);
|
|
24
|
-
|
|
25
|
-
const taskCounts = emptyCounts(TASK_STATES);
|
|
26
|
-
const packetCounts = emptyCounts(PACKET_STATES);
|
|
27
|
-
const unresolvedTasks: string[] = [];
|
|
28
|
-
const blockers: string[] = [];
|
|
29
|
-
|
|
30
|
-
for (const task of slice.tasks) {
|
|
31
|
-
taskCounts[task.state] += 1;
|
|
32
|
-
if (task.state !== 'resolved') {
|
|
33
|
-
unresolvedTasks.push(task.taskId);
|
|
34
|
-
}
|
|
35
|
-
if (task.state === 'blocked' || task.state === 'countered') {
|
|
36
|
-
blockers.push(task.taskId);
|
|
37
|
-
}
|
|
38
|
-
const packetState = task.packetId ? packetStates[task.packetId] : undefined;
|
|
39
|
-
if (packetState) {
|
|
40
|
-
packetCounts[packetState] += 1;
|
|
41
|
-
if (packetState !== 'PROVISIONED' && task.state === 'resolved') {
|
|
42
|
-
blockers.push(`${task.taskId}:packet-not-provisioned`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const scrutinyBlocksClose = scrutinyState === 'DEFERRED' || scrutinyState === 'BLOCKED';
|
|
48
|
-
const readyToClose = unresolvedTasks.length === 0 && !scrutinyBlocksClose && blockers.length === 0;
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
sliceId: slice.sliceId,
|
|
52
|
-
sliceState: slice.state,
|
|
53
|
-
scrutinyState,
|
|
54
|
-
packetStates: packetCounts,
|
|
55
|
-
taskStates: taskCounts,
|
|
56
|
-
unresolvedTasks,
|
|
57
|
-
readyToClose,
|
|
58
|
-
blockers,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function deriveTaskStateFromLedger(value: string): TaskState {
|
|
63
|
-
switch (value) {
|
|
64
|
-
case 'incoming':
|
|
65
|
-
return 'incoming';
|
|
66
|
-
case 'pending':
|
|
67
|
-
return 'claimed';
|
|
68
|
-
case 'resolved':
|
|
69
|
-
return 'resolved';
|
|
70
|
-
default:
|
|
71
|
-
return 'incoming';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function isTerminalScrutinyState(value: string): value is Extract<ScrutinyState, 'RESOLVED' | 'VERIFIED' | 'WONT_FIX'> {
|
|
76
|
-
return value === 'RESOLVED' || value === 'VERIFIED' || value === 'WONT_FIX';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function isKnownScrutinyState(value: string): value is ScrutinyState {
|
|
80
|
-
return (SCRUTINY_STATES as readonly string[]).includes(value);
|
|
81
|
-
}
|