@wrongstack/requirement-intake 1.0.19 → 1.0.26
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/dist/index.d.ts +1 -1
- package/dist/index.js +89 -16
- package/dist/validation.d.ts +52 -2
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,6 @@ export { type IntakeCreateResult, type IntakeListFilter, type IntakeSubmitResult
|
|
|
17
17
|
export { type IntakeIndexEntry, newIntakeId, RequirementIntakeStore, type RequirementIntakeStoreOptions, type StoreCreateResult, type StoreUpdateOptions, } from './store.js';
|
|
18
18
|
export { assertSuggestionString, type LlmSuggestionGenerator, type LlmSuggestionOutput, type LlmSuggestionRequest, llmSuggestionOutputSchema, type NormalizedLlmSuggestion, toProposals, validateLlmSuggestionOutput, } from './suggestions.js';
|
|
19
19
|
export { type AddAnswerInput, type AttachResourceInput, type ChangeHistoryEntry, type CreateIntakeInput, INTAKE_EVENT_NAMES, type IntakeActor, type IntakeAnswer, type IntakeAttachment, type IntakeAttachmentInput, type IntakeContext, type IntakeEvent, type IntakeEventName, type IntakeQuestion, type IntakeQuestionTemplateInput, type LlmSuggestionProposal, type RelatedResource, type RelatedResourceInput, type RequirementIntakeRecord, type UpdateIntakeInput, } from './types.js';
|
|
20
|
-
export { answerInputSchema, attachmentInputSchema, attachResourceInputSchema, createIntakeSchema, deterministicSummary, deterministicTitle, isBlank, metadataSchema, normalizeRequestType, parseWithIssues, prioritySchema, questionTemplateInputSchema, relatedResourceInputSchema, requestTypeSchema, updateIntakeSchema, validateAnswerInput, validateAttachmentInput, validateAttachResourceInput, validateCreateInput, validateFieldSource, validateQuestionTemplateInput, validateRelatedResourceInput, validateUpdateInput, } from './validation.js';
|
|
20
|
+
export { answerInputSchema, attachmentInputSchema, attachResourceInputSchema, createIntakeSchema, deterministicSummary, deterministicTitle, isBlank, metadataSchema, normalizeRequestType, parseWithIssues, prioritySchema, questionTemplateInputSchema, relatedResourceInputSchema, requestTypeSchema, updateIntakeSchema, validateAnswerInput, validateAttachmentInput, validateAttachResourceInput, validateCreateInput, validateFieldSource, validateQuestionTemplateInput, validateRelatedResourceInput, validateUpdateInput, vibeProtocolStateSchema, } from './validation.js';
|
|
21
21
|
export { deriveVibeState, hasVibeTag, stripVibeTag, VIBE_TAG_REGEX, type VibeProtocolStage, type VibeProtocolState, } from './vibe.js';
|
|
22
22
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -413,6 +413,19 @@ var IDEMPOTENCY_PATH = "_idempotency.json";
|
|
|
413
413
|
function hashIdempotencyKey(key) {
|
|
414
414
|
return createHash("sha256").update(key, "utf8").digest("hex");
|
|
415
415
|
}
|
|
416
|
+
function indexEntryFor(record) {
|
|
417
|
+
return {
|
|
418
|
+
id: record.id,
|
|
419
|
+
projectId: record.projectId,
|
|
420
|
+
title: record.title,
|
|
421
|
+
status: record.status,
|
|
422
|
+
requestType: record.requestType,
|
|
423
|
+
priority: record.priority,
|
|
424
|
+
requestedBy: record.requestedBy,
|
|
425
|
+
updatedAt: record.updatedAt,
|
|
426
|
+
createdAt: record.createdAt
|
|
427
|
+
};
|
|
428
|
+
}
|
|
416
429
|
var RequirementIntakeStore = class {
|
|
417
430
|
baseDir;
|
|
418
431
|
indexPath;
|
|
@@ -430,6 +443,14 @@ var RequirementIntakeStore = class {
|
|
|
430
443
|
return this.baseDir;
|
|
431
444
|
}
|
|
432
445
|
recordPath(id) {
|
|
446
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(id)) {
|
|
447
|
+
throw new IntakeValidationError([
|
|
448
|
+
{
|
|
449
|
+
field: "id",
|
|
450
|
+
message: "record id must contain only letters, numbers, underscores, or hyphens"
|
|
451
|
+
}
|
|
452
|
+
]);
|
|
453
|
+
}
|
|
433
454
|
return path.join(this.baseDir, `${id}.json`);
|
|
434
455
|
}
|
|
435
456
|
// -------------------------------------------------------------------------
|
|
@@ -558,26 +579,37 @@ var RequirementIntakeStore = class {
|
|
|
558
579
|
const raw = await fsp.readFile(this.indexPath, "utf8");
|
|
559
580
|
const parsed = JSON.parse(raw);
|
|
560
581
|
if (parsed?.version === 1 && Array.isArray(parsed.entries)) return parsed;
|
|
561
|
-
} catch {
|
|
582
|
+
} catch (error) {
|
|
583
|
+
if (error.code !== "ENOENT" && !(error instanceof SyntaxError)) {
|
|
584
|
+
throw error;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
let files;
|
|
588
|
+
try {
|
|
589
|
+
files = await fsp.readdir(this.baseDir);
|
|
590
|
+
} catch (error) {
|
|
591
|
+
if (error.code === "ENOENT") return { version: 1, entries: [] };
|
|
592
|
+
throw error;
|
|
593
|
+
}
|
|
594
|
+
const entries = [];
|
|
595
|
+
for (const file of files) {
|
|
596
|
+
if (file === INDEX_PATH || file === IDEMPOTENCY_PATH || !file.endsWith(".json")) continue;
|
|
597
|
+
const id = file.slice(0, -".json".length);
|
|
598
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(id)) continue;
|
|
599
|
+
const record = JSON.parse(
|
|
600
|
+
await fsp.readFile(path.join(this.baseDir, file), "utf8")
|
|
601
|
+
);
|
|
602
|
+
if (record?.id !== id) throw new Error(`Invalid requirement intake record: ${file}`);
|
|
603
|
+
entries.push(indexEntryFor(record));
|
|
562
604
|
}
|
|
563
|
-
return { version: 1, entries
|
|
605
|
+
return { version: 1, entries };
|
|
564
606
|
}
|
|
565
607
|
async updateIndexFor(record) {
|
|
566
608
|
return withFileLock(
|
|
567
609
|
this.indexPath,
|
|
568
610
|
async () => {
|
|
569
611
|
const index = await this.readIndex();
|
|
570
|
-
const entry =
|
|
571
|
-
id: record.id,
|
|
572
|
-
projectId: record.projectId,
|
|
573
|
-
title: record.title,
|
|
574
|
-
status: record.status,
|
|
575
|
-
requestType: record.requestType,
|
|
576
|
-
priority: record.priority,
|
|
577
|
-
requestedBy: record.requestedBy,
|
|
578
|
-
updatedAt: record.updatedAt,
|
|
579
|
-
createdAt: record.createdAt
|
|
580
|
-
};
|
|
612
|
+
const entry = indexEntryFor(record);
|
|
581
613
|
const position = index.entries.findIndex((candidate) => candidate.id === record.id);
|
|
582
614
|
if (position >= 0) {
|
|
583
615
|
index.entries[position] = entry;
|
|
@@ -683,6 +715,15 @@ var optionalString = (max) => z.string().max(max).optional();
|
|
|
683
715
|
var optionalStringArray = z.array(z.string().trim().min(1).max(MAX_STRING_FIELD_LENGTH)).max(MAX_ARRAY_ITEMS).optional();
|
|
684
716
|
var optionalAttachmentArray = z.array(attachmentInputSchema).max(MAX_ATTACHMENTS).optional();
|
|
685
717
|
var optionalRelatedResourceArray = z.array(relatedResourceInputSchema).max(MAX_RELATED_RESOURCES).optional();
|
|
718
|
+
var vibeProtocolStateSchema = z.object({
|
|
719
|
+
isVibeMode: z.literal(true),
|
|
720
|
+
detectedAt: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
721
|
+
stage: z.enum(["synthesizer", "coder", "auditor", "passed"]),
|
|
722
|
+
synthesizedSpec: z.string().max(MAX_ANSWER_LENGTH).optional(),
|
|
723
|
+
coderContract: z.string().max(MAX_ANSWER_LENGTH).optional(),
|
|
724
|
+
auditVerdict: z.enum(["PASS", "REJECT"]).optional(),
|
|
725
|
+
auditNotes: z.array(z.string().max(MAX_STRING_FIELD_LENGTH)).max(MAX_ARRAY_ITEMS).optional()
|
|
726
|
+
}).strict();
|
|
686
727
|
var createIntakeSchema = z.object({
|
|
687
728
|
projectId: idSchema,
|
|
688
729
|
originalRequest: nonBlankString(MAX_REQUEST_LENGTH, "originalRequest"),
|
|
@@ -703,7 +744,7 @@ var createIntakeSchema = z.object({
|
|
|
703
744
|
knownFields: z.array(z.string().trim().min(1).max(64)).max(MAX_ARRAY_ITEMS).optional(),
|
|
704
745
|
questions: z.array(questionTemplateInputSchema).max(50).optional(),
|
|
705
746
|
isVibeMode: z.boolean().optional(),
|
|
706
|
-
vibeProtocol:
|
|
747
|
+
vibeProtocol: vibeProtocolStateSchema.optional()
|
|
707
748
|
}).strict();
|
|
708
749
|
var updateIntakeSchema = z.object({
|
|
709
750
|
title: optionalString(MAX_TITLE_LENGTH),
|
|
@@ -717,7 +758,7 @@ var updateIntakeSchema = z.object({
|
|
|
717
758
|
providedContext: optionalStringArray,
|
|
718
759
|
metadata: metadataSchema.optional(),
|
|
719
760
|
isVibeMode: z.boolean().optional(),
|
|
720
|
-
vibeProtocol:
|
|
761
|
+
vibeProtocol: vibeProtocolStateSchema.optional()
|
|
721
762
|
}).strict();
|
|
722
763
|
var answerInputSchema = z.object({
|
|
723
764
|
field: nonBlankString(64, "answer field"),
|
|
@@ -1203,6 +1244,12 @@ function applyAnswerUpdateToRecord(record, answerId, newAnswer) {
|
|
|
1203
1244
|
question.answer = newAnswer;
|
|
1204
1245
|
question.status = "answered";
|
|
1205
1246
|
}
|
|
1247
|
+
const mapping = ANSWER_FIELD_MAPPING[answer.field];
|
|
1248
|
+
if (mapping) {
|
|
1249
|
+
mapping.set(record, newAnswer);
|
|
1250
|
+
const sourceKey = answer.field;
|
|
1251
|
+
record.fieldSources[sourceKey] = "user";
|
|
1252
|
+
}
|
|
1206
1253
|
}
|
|
1207
1254
|
function applyAttachmentToRecord(record, validated, actorId, now) {
|
|
1208
1255
|
if (record.attachments.length >= MAX_ATTACHMENTS) {
|
|
@@ -1305,6 +1352,23 @@ var RequirementIntakeService = class {
|
|
|
1305
1352
|
const now = Date.now();
|
|
1306
1353
|
const record = this.buildNewRecord(validated, ctx, now);
|
|
1307
1354
|
const result = await this.store.create(record, validated.idempotencyKey?.trim());
|
|
1355
|
+
if (!result.created) {
|
|
1356
|
+
if (result.record.projectId !== ctx.projectId) {
|
|
1357
|
+
throw new IntakeValidationError([
|
|
1358
|
+
{
|
|
1359
|
+
field: "idempotencyKey",
|
|
1360
|
+
message: "idempotency key already used for a different project"
|
|
1361
|
+
}
|
|
1362
|
+
]);
|
|
1363
|
+
}
|
|
1364
|
+
this.metrics.increment("intake.duplicate_create");
|
|
1365
|
+
this.logger.info("intake", "intake.duplicate_create", {
|
|
1366
|
+
intakeId: result.record.id,
|
|
1367
|
+
projectId: result.record.projectId,
|
|
1368
|
+
actorId: ctx.id
|
|
1369
|
+
});
|
|
1370
|
+
return result;
|
|
1371
|
+
}
|
|
1308
1372
|
this.logger.info("intake", "intake.created", {
|
|
1309
1373
|
intakeId: result.record.id,
|
|
1310
1374
|
projectId: result.record.projectId,
|
|
@@ -1353,6 +1417,7 @@ var RequirementIntakeService = class {
|
|
|
1353
1417
|
throw new IntakeValidationError([{ field: "title", message: "title must not be blank" }]);
|
|
1354
1418
|
}
|
|
1355
1419
|
return this.store.update(id, this.updateMeta(ctx, "updated", changedKeys, expectedVersion), (next) => {
|
|
1420
|
+
this.assertMutable(next, "updateIntake");
|
|
1356
1421
|
if (validated.title !== void 0) {
|
|
1357
1422
|
next.title = validated.title.trim();
|
|
1358
1423
|
next.fieldSources.title = "user";
|
|
@@ -1390,6 +1455,7 @@ var RequirementIntakeService = class {
|
|
|
1390
1455
|
id,
|
|
1391
1456
|
this.updateMeta(ctx, "answer_added", [validated.field], expectedVersion),
|
|
1392
1457
|
(next) => {
|
|
1458
|
+
this.assertMutable(next, "addAnswer");
|
|
1393
1459
|
applyAnswerToRecord(next, validated, ctx.id, Date.now());
|
|
1394
1460
|
}
|
|
1395
1461
|
).then((updated) => {
|
|
@@ -1407,6 +1473,7 @@ var RequirementIntakeService = class {
|
|
|
1407
1473
|
})
|
|
1408
1474
|
);
|
|
1409
1475
|
return this.store.update(id, this.updateMeta(ctx, "answer_updated", [answerId], expectedVersion), (next) => {
|
|
1476
|
+
this.assertMutable(next, "updateAnswer");
|
|
1410
1477
|
applyAnswerUpdateToRecord(next, answerId, validated.answer);
|
|
1411
1478
|
}).then((updated) => {
|
|
1412
1479
|
this.afterMutation(updated, ctx, "RequirementIntakeUpdated");
|
|
@@ -1423,6 +1490,7 @@ var RequirementIntakeService = class {
|
|
|
1423
1490
|
id,
|
|
1424
1491
|
this.updateMeta(ctx, "attachment_added", ["attachments"], expectedVersion),
|
|
1425
1492
|
(next) => {
|
|
1493
|
+
this.assertMutable(next, "attachResource");
|
|
1426
1494
|
applyAttachmentToRecord(next, validated, ctx.id, now);
|
|
1427
1495
|
}
|
|
1428
1496
|
).then((updated) => {
|
|
@@ -1434,6 +1502,7 @@ var RequirementIntakeService = class {
|
|
|
1434
1502
|
id,
|
|
1435
1503
|
this.updateMeta(ctx, "related_resource_added", ["related_resources"], expectedVersion),
|
|
1436
1504
|
(next) => {
|
|
1505
|
+
this.assertMutable(next, "attachResource");
|
|
1437
1506
|
applyRelatedResourceToRecord(next, validated, ctx.id, now);
|
|
1438
1507
|
}
|
|
1439
1508
|
).then((updated) => {
|
|
@@ -1489,6 +1558,7 @@ var RequirementIntakeService = class {
|
|
|
1489
1558
|
to: previousStatus === nextStatus ? void 0 : nextStatus
|
|
1490
1559
|
},
|
|
1491
1560
|
(next) => {
|
|
1561
|
+
this.assertMutable(next, "generateSuggestions");
|
|
1492
1562
|
next.llmSuggestions.push(...proposals);
|
|
1493
1563
|
if (next.llmSuggestions.length > MAX_SUGGESTIONS) {
|
|
1494
1564
|
next.llmSuggestions = next.llmSuggestions.slice(
|
|
@@ -1530,6 +1600,7 @@ var RequirementIntakeService = class {
|
|
|
1530
1600
|
id,
|
|
1531
1601
|
this.updateMeta(ctx, "suggestion_accepted", [proposal2.kind], expectedVersion),
|
|
1532
1602
|
(next) => {
|
|
1603
|
+
this.assertMutable(next, "acceptSuggestion");
|
|
1533
1604
|
const target = next.llmSuggestions.find((candidate) => candidate.id === proposalId);
|
|
1534
1605
|
if (!target) {
|
|
1535
1606
|
throw new IntakeValidationError([
|
|
@@ -1553,6 +1624,7 @@ var RequirementIntakeService = class {
|
|
|
1553
1624
|
id,
|
|
1554
1625
|
this.updateMeta(ctx, "suggestion_rejected", [proposalId], expectedVersion),
|
|
1555
1626
|
(next) => {
|
|
1627
|
+
this.assertMutable(next, "rejectSuggestion");
|
|
1556
1628
|
const target = next.llmSuggestions.find((candidate) => candidate.id === proposalId);
|
|
1557
1629
|
if (!target) {
|
|
1558
1630
|
throw new IntakeValidationError([
|
|
@@ -1884,6 +1956,7 @@ export {
|
|
|
1884
1956
|
validateLlmSuggestionOutput,
|
|
1885
1957
|
validateQuestionTemplateInput,
|
|
1886
1958
|
validateRelatedResourceInput,
|
|
1887
|
-
validateUpdateInput
|
|
1959
|
+
validateUpdateInput,
|
|
1960
|
+
vibeProtocolStateSchema
|
|
1888
1961
|
};
|
|
1889
1962
|
//# sourceMappingURL=index.js.map
|
package/dist/validation.d.ts
CHANGED
|
@@ -75,6 +75,24 @@ export declare const questionTemplateInputSchema: z.ZodObject<{
|
|
|
75
75
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
76
76
|
}, z.core.$strip>;
|
|
77
77
|
export declare const metadataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
78
|
+
/** Strict, bounded representation of resumable VIBE protocol state. */
|
|
79
|
+
export declare const vibeProtocolStateSchema: z.ZodObject<{
|
|
80
|
+
isVibeMode: z.ZodLiteral<true>;
|
|
81
|
+
detectedAt: z.ZodNumber;
|
|
82
|
+
stage: z.ZodEnum<{
|
|
83
|
+
auditor: "auditor";
|
|
84
|
+
coder: "coder";
|
|
85
|
+
passed: "passed";
|
|
86
|
+
synthesizer: "synthesizer";
|
|
87
|
+
}>;
|
|
88
|
+
synthesizedSpec: z.ZodOptional<z.ZodString>;
|
|
89
|
+
coderContract: z.ZodOptional<z.ZodString>;
|
|
90
|
+
auditVerdict: z.ZodOptional<z.ZodEnum<{
|
|
91
|
+
PASS: "PASS";
|
|
92
|
+
REJECT: "REJECT";
|
|
93
|
+
}>>;
|
|
94
|
+
auditNotes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
95
|
+
}, z.core.$strict>;
|
|
78
96
|
export declare const createIntakeSchema: z.ZodObject<{
|
|
79
97
|
projectId: z.ZodString;
|
|
80
98
|
originalRequest: z.ZodString;
|
|
@@ -129,7 +147,23 @@ export declare const createIntakeSchema: z.ZodObject<{
|
|
|
129
147
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
130
148
|
}, z.core.$strip>>>;
|
|
131
149
|
isVibeMode: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
-
vibeProtocol: z.ZodOptional<z.
|
|
150
|
+
vibeProtocol: z.ZodOptional<z.ZodObject<{
|
|
151
|
+
isVibeMode: z.ZodLiteral<true>;
|
|
152
|
+
detectedAt: z.ZodNumber;
|
|
153
|
+
stage: z.ZodEnum<{
|
|
154
|
+
auditor: "auditor";
|
|
155
|
+
coder: "coder";
|
|
156
|
+
passed: "passed";
|
|
157
|
+
synthesizer: "synthesizer";
|
|
158
|
+
}>;
|
|
159
|
+
synthesizedSpec: z.ZodOptional<z.ZodString>;
|
|
160
|
+
coderContract: z.ZodOptional<z.ZodString>;
|
|
161
|
+
auditVerdict: z.ZodOptional<z.ZodEnum<{
|
|
162
|
+
PASS: "PASS";
|
|
163
|
+
REJECT: "REJECT";
|
|
164
|
+
}>>;
|
|
165
|
+
auditNotes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
166
|
+
}, z.core.$strict>>;
|
|
133
167
|
}, z.core.$strict>;
|
|
134
168
|
export declare const updateIntakeSchema: z.ZodObject<{
|
|
135
169
|
title: z.ZodOptional<z.ZodString>;
|
|
@@ -149,7 +183,23 @@ export declare const updateIntakeSchema: z.ZodObject<{
|
|
|
149
183
|
providedContext: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
150
184
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
151
185
|
isVibeMode: z.ZodOptional<z.ZodBoolean>;
|
|
152
|
-
vibeProtocol: z.ZodOptional<z.
|
|
186
|
+
vibeProtocol: z.ZodOptional<z.ZodObject<{
|
|
187
|
+
isVibeMode: z.ZodLiteral<true>;
|
|
188
|
+
detectedAt: z.ZodNumber;
|
|
189
|
+
stage: z.ZodEnum<{
|
|
190
|
+
auditor: "auditor";
|
|
191
|
+
coder: "coder";
|
|
192
|
+
passed: "passed";
|
|
193
|
+
synthesizer: "synthesizer";
|
|
194
|
+
}>;
|
|
195
|
+
synthesizedSpec: z.ZodOptional<z.ZodString>;
|
|
196
|
+
coderContract: z.ZodOptional<z.ZodString>;
|
|
197
|
+
auditVerdict: z.ZodOptional<z.ZodEnum<{
|
|
198
|
+
PASS: "PASS";
|
|
199
|
+
REJECT: "REJECT";
|
|
200
|
+
}>>;
|
|
201
|
+
auditNotes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
202
|
+
}, z.core.$strict>>;
|
|
153
203
|
}, z.core.$strict>;
|
|
154
204
|
export declare const answerInputSchema: z.ZodObject<{
|
|
155
205
|
field: z.ZodString;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/requirement-intake",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack Requirements Intake — collect, preserve, validate, normalize, and submit unstructured software development requests as structured intake records. Upstream of spec-driven development: it never plans, specifies, or implements.",
|
|
6
6
|
"repository": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"!dist/**/*.map"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@wrongstack/core": "1.0.
|
|
30
|
+
"@wrongstack/core": "1.0.26",
|
|
31
31
|
"zod": "4.6.2"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|