@wrongstack/requirement-intake 0.299.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ECOSTACK TECHNOLOGY OÜ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,237 @@
1
+ # @wrongstack/requirement-intake
2
+
3
+ Requirements Intake — collect, preserve, validate, normalize, and submit
4
+ unstructured software development requests as structured intake records.
5
+
6
+ This module is **upstream of spec-driven development**. It only collects and
7
+ preserves the initial request and its supporting information. It does not
8
+ plan tasks, generate executable specifications, resolve contradictions,
9
+ produce architecture, or generate code — those concerns belong to separate
10
+ modules (`@wrongstack/sdd` and friends).
11
+
12
+ ## Design goals
13
+
14
+ 1. **The original request is sacred.** The exact user input is stored verbatim
15
+ in `originalRequest` and is immutable after creation. No update path, no
16
+ LLM suggestion, and no normalization step can overwrite it.
17
+ 2. **Every derived value is separable and source-annotated.** Normalized,
18
+ summarized, categorized, or LLM-generated content is stored separately and
19
+ tagged with `fieldSources` / `source` (`user`, `llm`, `deterministic`).
20
+ 3. **LLM output is always a proposal.** It is validated against a schema,
21
+ stored with `source: 'llm'`, and only applied after an explicit user
22
+ `acceptSuggestion`. It never controls persistence, authorization, or
23
+ lifecycle state.
24
+ 4. **Deterministic validation and lifecycle.** Enums are authoritative;
25
+ unknown request types map to `other`/`unspecified`; status transitions are
26
+ enforced by application logic.
27
+ 5. **Safety first.** Authorization is enforced on every operation (fail
28
+ closed), concurrent writes use optimistic concurrency + file locks,
29
+ create/submit are idempotent, and no sensitive request content ever
30
+ reaches logs, metrics, or events.
31
+
32
+ ## Quick start
33
+
34
+ ```ts
35
+ import {
36
+ RequirementIntakeStore,
37
+ RequirementIntakeService,
38
+ AllowAllIntakeAuthorizer,
39
+ } from '@wrongstack/requirement-intake';
40
+
41
+ const store = new RequirementIntakeStore({ baseDir: '.wrongstack/requirement-intakes' });
42
+ const service = new RequirementIntakeService({
43
+ store,
44
+ authorizer: new AllowAllIntakeAuthorizer(), // wire your own policy in production
45
+ });
46
+
47
+ const ctx = { id: 'user-42', type: 'user', projectId: 'proj_01ABCDEF123456789' };
48
+
49
+ const { record } = await service.createIntake(
50
+ {
51
+ projectId: ctx.projectId,
52
+ originalRequest: 'Add email-based password reset so users can recover access.',
53
+ requestedBy: ctx.id,
54
+ },
55
+ ctx,
56
+ );
57
+
58
+ await service.addAnswer(record.id, { field: 'business_goal', answer: 'Reduce support tickets' }, ctx);
59
+ const { record: submitted } = await service.submitIntake(record.id, ctx);
60
+ ```
61
+
62
+ ## Operations
63
+
64
+ | Operation | Method |
65
+ |---|---|
66
+ | Create intake | `createIntake(input, ctx)` |
67
+ | Update draft | `updateIntake(id, patch, ctx, expectedVersion?)` |
68
+ | Get intake | `getIntake(id, ctx)` |
69
+ | Add answer | `addAnswer(id, { field, answer }, ctx)` |
70
+ | Update answer | `updateAnswer(id, answerId, { answer }, ctx)` |
71
+ | Attach resources | `attachResource(id, { attachment \| relatedResource }, ctx)` |
72
+ | LLM suggestions | `generateSuggestions(id, ctx, focus?)` |
73
+ | Accept suggestion | `acceptSuggestion(id, proposalId, ctx)` |
74
+ | Reject suggestion | `rejectSuggestion(id, proposalId, ctx)` |
75
+ | Submit | `submitIntake(id, ctx)` |
76
+ | Cancel | `cancelIntake(id, ctx, reason?)` |
77
+ | Archive | `archiveIntake(id, ctx)` |
78
+ | List project intakes | `listIntakes(projectId, ctx, filter?)` |
79
+ | Pending questions | `pendingQuestions(id, ctx)` |
80
+
81
+ Every mutation accepts an optional `expectedVersion` for optimistic
82
+ concurrency; a mismatch throws `IntakeConflictError`.
83
+
84
+ ## Data model
85
+
86
+ The record adapts the spec's snake_case JSON to the codebase's camelCase
87
+ convention:
88
+
89
+ | Concept (spec) | Field |
90
+ |---|---|
91
+ | `id` | `id` — `reqi_<ulid>` |
92
+ | `project_id` | `projectId` |
93
+ | `original_request` | `originalRequest` — immutable |
94
+ | `normalized_summary` | `normalizedSummary` |
95
+ | `request_type` | `requestType` |
96
+ | `requested_by` | `requestedBy` |
97
+ | `business_goal` | `businessGoal` |
98
+ | `target_users` | `targetUsers` |
99
+ | `expected_outcome` | `expectedOutcome` |
100
+ | `scope_notes` | `scopeNotes` |
101
+ | `provided_context` | `providedContext` |
102
+ | `related_resources` | `relatedResources` |
103
+ | `created_at` / `updated_at` | `createdAt` / `updatedAt` (epoch ms) |
104
+
105
+ Plus: `status`, `priority`, `constraints`, `attachments`, `answers`,
106
+ `questions`, `llmSuggestions`, `metadata`, `fieldSources`, `version`,
107
+ `history`, submission/cancellation/archival stamps, and the create
108
+ `idempotencyKey`.
109
+
110
+ Request types: `feature`, `bug_fix`, `refactor`, `performance`, `security`,
111
+ `ui_change`, `api_change`, `infrastructure`, `migration`, `testing`,
112
+ `documentation`, `maintenance`, `other`, `unspecified`.
113
+
114
+ Lifecycle: `draft → collecting_information → submitted`, `draft → cancelled`,
115
+ `collecting_information → cancelled`, `submitted/cancelled → archived`.
116
+ Invalid transitions throw `IntakeStateTransitionError`. Duplicate submission
117
+ is idempotent (returns the submitted record).
118
+
119
+ ## LLM suggestions
120
+
121
+ Wire an adapter that implements `LlmSuggestionGenerator` and returns
122
+ structured output:
123
+
124
+ ```ts
125
+ const service = new RequirementIntakeService({
126
+ store,
127
+ authorizer,
128
+ generator: {
129
+ async generate({ record, focus }) {
130
+ // call your LLM; return structured JSON only
131
+ return {
132
+ suggested_title: 'Add email-based password reset',
133
+ normalized_summary: 'Allow users to reset forgotten passwords through an email link.',
134
+ suggested_request_type: 'feature',
135
+ extracted_constraints: ['Rate-limit reset emails'],
136
+ suggested_questions: [{ field: 'target_users', question: 'Which users?' }],
137
+ };
138
+ },
139
+ },
140
+ });
141
+ ```
142
+
143
+ Output is validated with zod (`llmSuggestionOutputSchema`); malformed output
144
+ throws `IntakeSuggestionError` and is never persisted. Generating suggestions
145
+ moves a `draft` to `collecting_information` (application logic, not the LLM).
146
+
147
+ ## Authorization
148
+
149
+ Pass an `IntakeAuthorizer` to the service. The module fails closed: without a
150
+ permissive authorizer, every operation throws `IntakeAuthorizationError`.
151
+
152
+ - `AllowAllIntakeAuthorizer` — embedded/single-user hosts.
153
+ - `DenyAllIntakeAuthorizer` — fail-closed default.
154
+ - `ProjectMembershipIntakeAuthorizer` — membership-based policy with optional
155
+ owner-only operations and built-in cross-project denial.
156
+
157
+ The service always verifies `record.projectId === ctx.projectId` through the
158
+ authorizer and rejects `listIntakes` for a project different from the
159
+ context's project.
160
+
161
+ ## Persistence & concurrency
162
+
163
+ File-backed JSON store (mirrors `SpecStore` conventions):
164
+
165
+ ```
166
+ baseDir/<id>.json — one record per file
167
+ baseDir/_index.json — listing index
168
+ baseDir/_idempotency.json — create-idempotency key map
169
+ ```
170
+
171
+ - Every write goes through `atomicWrite` (temp + rename) under a per-file
172
+ exclusive lock (`withFileLock`), serializing concurrent writers within and
173
+ across processes.
174
+ - `version` is a per-write mutation counter; passing a stale `expectedVersion`
175
+ throws `IntakeConflictError` instead of silently overwriting.
176
+ - Create is idempotent via `idempotencyKey` (hashed in `_idempotency.json`).
177
+ - Change history is appended per write (`history`, capped at 200 entries).
178
+ - Records are never hard-deleted; `archived` is the soft-delete path.
179
+
180
+ Default location: `~/.wrongstack/projects/<slug>/requirement-intakes`
181
+ (`resolveWstackPaths(...).projectRequirementIntakes` — the `baseDir` option is
182
+ optional and falls back to this when omitted).
183
+
184
+ ## Integrations
185
+
186
+ - **REST** — the WebUI server exposes the intake API under
187
+ `/api/projects/:projectId/requirement-intakes` (create/list) and
188
+ `/api/requirement-intakes/:intakeId` (get/patch/answers/suggestions/
189
+ submit/cancel/archive), token-gated like every other `/api` route. The
190
+ service is constructed per project in `startHttpServer` with an
191
+ `AllowAllIntakeAuthorizer` (the HTTP token gate is the authorization
192
+ boundary); hosts may inject their own via `intakeService`.
193
+ - **CLI** — `/intake [text]` creates and submits an intake record from the
194
+ given text or the most recent session prompt (see `docs/slash/intake.md`).
195
+ - **MCP** — `@wrongstack/requirement-intake-mcp` provides
196
+ `wstack-requirement-intake-mcp`, a project-scoped MCP server with
197
+ `requirement_intake_list` (read tier) and `requirement_intake_submit`
198
+ (writable tier) tools, mirroring the kanban-mcp pattern.
199
+ - **SDD** — `startInterviewFromIntake(driver, record)` /
200
+ `intakeToInterviewKickoff(record)` in `@wrongstack/sdd` seed a spec-builder
201
+ interview from a submitted intake record, using the original request as the
202
+ interview intent and the collected facts as project context.
203
+
204
+ ## Observability
205
+
206
+ - **Events** — `RequirementIntakeCreated`, `RequirementIntakeUpdated`,
207
+ `RequirementIntakeInformationRequested`, `RequirementIntakeSubmitted`,
208
+ `RequirementIntakeCancelled`, `RequirementIntakeArchived`. Payloads carry
209
+ identifiers and safe metadata only — never request content.
210
+ - **Metrics** (`IntakeMetrics`) — created/submitted/cancelled/archived counts,
211
+ duplicate create/submit, validation failures, suggestion
212
+ requested/succeeded/failed, unauthorized attempts, and
213
+ `intake.time_to_submit` duration.
214
+ - **Logging** (`IntakeLogger`) — structured, scope-tagged, identifiers only.
215
+
216
+ All three are injectable; defaults are silent. Request content, answers, and
217
+ metadata values are never passed to the logger, metrics, or events.
218
+
219
+ ## Security
220
+
221
+ - Prompt-injection text is ordinary data: preserved verbatim, never executed.
222
+ - Oversized input, blank fields, malformed metadata, and unknown enums are
223
+ rejected/normalized deterministically.
224
+ - Cross-project access is denied; automation identities receive only what the
225
+ authorizer grants.
226
+ - Sensitive content does not leak into logs/events (covered by tests).
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ pnpm --filter @wrongstack/requirement-intake typecheck
232
+ pnpm --filter @wrongstack/requirement-intake test
233
+ pnpm --filter @wrongstack/requirement-intake build
234
+ ```
235
+
236
+ Tests: 147 unit/integration/security tests in `tests/` (run under both the
237
+ package config and the workspace root config).
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Requirements Intake — authorization.
3
+ *
4
+ * The module enforces authorization through an injected `IntakeAuthorizer`.
5
+ * Hosts wire their own policy (session user, project membership, agent
6
+ * permissions). The service awaits `isAllowed` on every operation and throws
7
+ * `IntakeAuthorizationError` on denial, so a misconfigured host fails closed
8
+ * unless it explicitly installs an allow-all policy.
9
+ */
10
+ import type { IntakeContext, RequirementIntakeRecord } from './types.js';
11
+ export declare const INTAKE_OPERATIONS: readonly ['create', 'read', 'list', 'update', 'answer', 'attach', 'suggest', 'accept_suggestion', 'reject_suggestion', 'submit', 'cancel', 'archive'];
12
+ export type IntakeOperation = (typeof INTAKE_OPERATIONS)[number];
13
+ export interface IntakeAuthorizer {
14
+ /**
15
+ * Decide whether `actor` may perform `operation` in `ctx.projectId`,
16
+ * optionally against the target record. May return a Promise for
17
+ * async resolvers. Implementations must deny cross-project access:
18
+ * the record's `projectId` must equal `ctx.projectId`.
19
+ */
20
+ isAllowed(operation: IntakeOperation, ctx: IntakeContext, record?: RequirementIntakeRecord | undefined): boolean | Promise<boolean>;
21
+ }
22
+ /** Permissive policy — every operation is allowed. For embedded/single-user hosts. */
23
+ export declare class AllowAllIntakeAuthorizer implements IntakeAuthorizer {
24
+ isAllowed(_operation: IntakeOperation, _ctx: IntakeContext, _record?: RequirementIntakeRecord | undefined): boolean;
25
+ }
26
+ /** Fail-closed policy — every operation is denied. Default safety net. */
27
+ export declare class DenyAllIntakeAuthorizer implements IntakeAuthorizer {
28
+ isAllowed(_operation: IntakeOperation, _ctx: IntakeContext, _record?: RequirementIntakeRecord | undefined): boolean;
29
+ }
30
+ export interface ProjectMembershipIntakeAuthorizerOptions {
31
+ /** Resolve which projects an actor belongs to. */
32
+ projectsOf: (actorId: string, actorType: string) => Promise<ReadonlySet<string>> | ReadonlySet<string>;
33
+ /** Operations that additionally require the actor to own the record. */
34
+ ownerOnlyOperations?: ReadonlySet<IntakeOperation> | undefined;
35
+ }
36
+ /**
37
+ * Membership-based policy: an actor may operate on a project only when the
38
+ * `projectsOf` resolver lists it. Cross-project access is always denied.
39
+ * `ownerOnlyOperations` (e.g. `submit`) can additionally require
40
+ * `record.requestedBy === actor.id`.
41
+ */
42
+ export declare class ProjectMembershipIntakeAuthorizer implements IntakeAuthorizer {
43
+ private readonly projectsOf;
44
+ private readonly ownerOnly;
45
+ constructor(options: ProjectMembershipIntakeAuthorizerOptions);
46
+ isAllowed(operation: IntakeOperation, ctx: IntakeContext, record?: RequirementIntakeRecord | undefined): Promise<boolean>;
47
+ }
48
+ //# sourceMappingURL=authorization.d.ts.map
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Requirements Intake — domain constants.
3
+ *
4
+ * Deterministic enums and size limits. The authoritative request-type and
5
+ * status values live here; unknown values are normalized (see validation.ts)
6
+ * and never persisted as uncontrolled strings.
7
+ */
8
+ export declare const INTAKE_ID_PREFIX = "reqi_";
9
+ export declare const ANSWER_ID_PREFIX = "ans_";
10
+ export declare const ATTACHMENT_ID_PREFIX = "attach_";
11
+ export declare const RELATED_RESOURCE_ID_PREFIX = "relres_";
12
+ export declare const QUESTION_ID_PREFIX = "q_";
13
+ export declare const SUGGESTION_ID_PREFIX = "sug_";
14
+ /**
15
+ * Supported request types. `unspecified` is the neutral default; `other`
16
+ * absorbs any unknown/unsupported value supplied by callers or the LLM.
17
+ */
18
+ export declare const REQUEST_TYPES: readonly ['feature', 'bug_fix', 'refactor', 'performance', 'security', 'ui_change', 'api_change', 'infrastructure', 'migration', 'testing', 'documentation', 'maintenance', 'other', 'unspecified'];
19
+ export type RequestType = (typeof REQUEST_TYPES)[number];
20
+ /** Deterministic lifecycle. Only application logic may change status. */
21
+ export declare const INTAKE_STATUSES: readonly ['draft', 'collecting_information', 'submitted', 'cancelled', 'archived'];
22
+ export type IntakeStatus = (typeof INTAKE_STATUSES)[number];
23
+ export declare const INTAKE_PRIORITIES: readonly ['unspecified', 'low', 'medium', 'high', 'critical'];
24
+ export type IntakePriority = (typeof INTAKE_PRIORITIES)[number];
25
+ /** Who produced a stored value. Distinguishes user vs machine content. */
26
+ export declare const INTAKE_FIELD_SOURCES: readonly ['user', 'llm', 'deterministic', 'system'];
27
+ export type IntakeFieldSource = (typeof INTAKE_FIELD_SOURCES)[number];
28
+ /** Record fields that carry a source annotation. */
29
+ export declare const INTAKE_FIELDS: readonly ['title', 'normalized_summary', 'request_type', 'priority', 'business_goal', 'target_users', 'expected_outcome', 'scope_notes', 'constraints', 'provided_context', 'attachments', 'related_resources'];
30
+ export type IntakeField = (typeof INTAKE_FIELDS)[number];
31
+ export declare const INTAKE_ATTACHMENT_KINDS: readonly ['file', 'image', 'document', 'link', 'other'];
32
+ export type IntakeAttachmentKind = (typeof INTAKE_ATTACHMENT_KINDS)[number];
33
+ export declare const RELATED_RESOURCE_KINDS: readonly ['spec', 'issue', 'pr', 'doc', 'url', 'other'];
34
+ export type RelatedResourceKind = (typeof RELATED_RESOURCE_KINDS)[number];
35
+ export declare const INTAKE_QUESTION_STATUSES: readonly ['unanswered', 'answered', 'skipped'];
36
+ export type IntakeQuestionStatus = (typeof INTAKE_QUESTION_STATUSES)[number];
37
+ export declare const SUGGESTION_STATUSES: readonly ['pending', 'accepted', 'rejected'];
38
+ export type SuggestionStatus = (typeof SUGGESTION_STATUSES)[number];
39
+ export declare const SUGGESTION_KINDS: readonly ['title', 'summary', 'request_type', 'priority', 'question', 'constraint', 'target_user', 'outcome'];
40
+ export type SuggestionKind = (typeof SUGGESTION_KINDS)[number];
41
+ /** Maximum length of the original request, in characters. */
42
+ export declare const MAX_REQUEST_LENGTH = 100000;
43
+ /** Maximum length of a title, in characters. */
44
+ export declare const MAX_TITLE_LENGTH = 200;
45
+ /** Maximum length of the normalized summary, in characters. */
46
+ export declare const MAX_SUMMARY_LENGTH = 2000;
47
+ /** Maximum length of any free-form string field (goal, outcome, scope notes, …). */
48
+ export declare const MAX_STRING_FIELD_LENGTH = 5000;
49
+ /** Maximum number of items in a list field (target users, constraints, …). */
50
+ export declare const MAX_ARRAY_ITEMS = 50;
51
+ /** Maximum number of attachments on one intake record. */
52
+ export declare const MAX_ATTACHMENTS = 20;
53
+ /** Maximum number of related resources on one intake record. */
54
+ export declare const MAX_RELATED_RESOURCES = 50;
55
+ /** Maximum number of top-level metadata keys. */
56
+ export declare const MAX_METADATA_ENTRIES = 50;
57
+ /** Maximum serialized metadata size, in bytes. */
58
+ export declare const MAX_METADATA_BYTES: number;
59
+ /** Maximum length of an intake answer, in characters. */
60
+ export declare const MAX_ANSWER_LENGTH = 50000;
61
+ /** Maximum length of an idempotency key, in characters. */
62
+ export declare const MAX_IDEMPOTENCY_KEY_LENGTH = 128;
63
+ /** Maximum length of an attachment/related-resource reference. */
64
+ export declare const MAX_REFERENCE_LENGTH = 500;
65
+ /** Maximum length of a question text. */
66
+ export declare const MAX_QUESTION_LENGTH = 500;
67
+ /** Maximum number of change-history entries retained per record. */
68
+ export declare const MAX_HISTORY_ENTRIES = 200;
69
+ /** Maximum number of pending LLM suggestions retained per record. */
70
+ export declare const MAX_SUGGESTIONS = 100;
71
+ /** Deterministic fallback summary length when no LLM summary is available. */
72
+ export declare const DETERMINISTIC_SUMMARY_LENGTH = 240;
73
+ /**
74
+ * Default intake question catalog. Hosts may override via create options.
75
+ * `field` values map to record properties through the answer pipeline
76
+ * (see service.ts) or stay as structured answers for later modules.
77
+ */
78
+ export interface IntakeQuestionTemplate {
79
+ field: string;
80
+ question: string;
81
+ required: boolean;
82
+ }
83
+ export declare const DEFAULT_INTAKE_QUESTIONS: readonly IntakeQuestionTemplate[];
84
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Requirements Intake — typed errors.
3
+ *
4
+ * Every failure path throws an `IntakeError` subclass carrying a stable
5
+ * `code` so hosts can map them to HTTP statuses, CLI messages, or events.
6
+ */
7
+ export type IntakeErrorCode = 'INTAKE_VALIDATION_ERROR' | 'INTAKE_NOT_FOUND' | 'INTAKE_INVALID_TRANSITION' | 'INTAKE_STATUS_LOCKED' | 'INTAKE_CONFLICT' | 'INTAKE_UNAUTHORIZED' | 'INTAKE_SUGGESTION_ERROR';
8
+ export declare class IntakeError extends Error {
9
+ readonly code: IntakeErrorCode;
10
+ constructor(code: IntakeErrorCode, message: string, options?: ErrorOptions);
11
+ }
12
+ /** One field-level validation problem. */
13
+ export interface IntakeValidationIssue {
14
+ /** Path of the offending field, e.g. `originalRequest` or `metadata.foo`. */
15
+ field: string;
16
+ message: string;
17
+ }
18
+ export declare class IntakeValidationError extends IntakeError {
19
+ readonly issues: IntakeValidationIssue[];
20
+ constructor(issues: IntakeValidationIssue[], message?: string);
21
+ }
22
+ export declare class IntakeNotFoundError extends IntakeError {
23
+ constructor(id: string);
24
+ }
25
+ export declare class IntakeStateTransitionError extends IntakeError {
26
+ constructor(from: string, to: string);
27
+ }
28
+ /** Raised when mutating a record whose status locks its content. */
29
+ export declare class IntakeStatusLockedError extends IntakeError {
30
+ constructor(id: string, status: string, action: string);
31
+ }
32
+ /** Raised on optimistic-concurrency version mismatch. */
33
+ export declare class IntakeConflictError extends IntakeError {
34
+ constructor(id: string, expectedVersion: number, actualVersion: number);
35
+ }
36
+ export declare class IntakeAuthorizationError extends IntakeError {
37
+ constructor(operation: string, actorId: string, projectId: string);
38
+ }
39
+ /** Raised when the LLM suggestion pipeline fails or returns malformed output. */
40
+ export declare class IntakeSuggestionError extends IntakeError {
41
+ constructor(message: string, options?: ErrorOptions);
42
+ }
43
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Requirements Intake — domain event emitter.
3
+ *
4
+ * Events carry identifiers and safe metadata only — never a copy of the
5
+ * user request. Pattern mirrors the Kanban board event emitter.
6
+ */
7
+ import type { IntakeEvent, IntakeEventName } from './types.js';
8
+ type Listener = (event: IntakeEvent) => void;
9
+ export declare class IntakeEventEmitter {
10
+ private readonly listeners;
11
+ /** Publish an event. Listener errors are swallowed so one bad listener cannot break others. */
12
+ emit(event: IntakeEventName, data: Omit<IntakeEvent, 'event' | 'timestamp'>): void;
13
+ /** Subscribe; returns a disposer. Past the cap, returns a no-op disposer. */
14
+ subscribe(listener: Listener): () => void;
15
+ get listenerCount(): number;
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Requirements Intake — public surface.
3
+ *
4
+ * Collect, preserve, validate, normalize, and submit unstructured software
5
+ * development requests as structured intake records. Upstream of spec-driven
6
+ * development; this module never plans, specifies, or implements.
7
+ */
8
+ export { INTAKE_ID_PREFIX, REQUEST_TYPES, INTAKE_STATUSES, INTAKE_PRIORITIES, INTAKE_FIELD_SOURCES, INTAKE_FIELDS, INTAKE_ATTACHMENT_KINDS, RELATED_RESOURCE_KINDS, INTAKE_QUESTION_STATUSES, SUGGESTION_STATUSES, SUGGESTION_KINDS, MAX_REQUEST_LENGTH, MAX_TITLE_LENGTH, MAX_SUMMARY_LENGTH, MAX_STRING_FIELD_LENGTH, MAX_ARRAY_ITEMS, MAX_ATTACHMENTS, MAX_RELATED_RESOURCES, MAX_METADATA_ENTRIES, MAX_METADATA_BYTES, MAX_ANSWER_LENGTH, MAX_IDEMPOTENCY_KEY_LENGTH, MAX_REFERENCE_LENGTH, MAX_QUESTION_LENGTH, MAX_HISTORY_ENTRIES, MAX_SUGGESTIONS, DEFAULT_INTAKE_QUESTIONS, type RequestType, type IntakeStatus, type IntakePriority, type IntakeFieldSource, type IntakeField, type IntakeAttachmentKind, type RelatedResourceKind, type IntakeQuestionStatus, type SuggestionStatus, type SuggestionKind, type IntakeQuestionTemplate, } from './constants.js';
9
+ export { INTAKE_EVENT_NAMES, type IntakeActor, type IntakeContext, type IntakeAttachment, type RelatedResource, type IntakeAnswer, type IntakeQuestion, type ChangeHistoryEntry, type LlmSuggestionProposal, type RequirementIntakeRecord, type CreateIntakeInput, type IntakeAttachmentInput, type RelatedResourceInput, type IntakeQuestionTemplateInput, type UpdateIntakeInput, type AddAnswerInput, type AttachResourceInput, type IntakeEvent, type IntakeEventName, } from './types.js';
10
+ export { IntakeError, IntakeValidationError, IntakeNotFoundError, IntakeStateTransitionError, IntakeStatusLockedError, IntakeConflictError, IntakeAuthorizationError, IntakeSuggestionError, type IntakeErrorCode, type IntakeValidationIssue, } from './errors.js';
11
+ export { requestTypeSchema, prioritySchema, metadataSchema, attachmentInputSchema, relatedResourceInputSchema, questionTemplateInputSchema, createIntakeSchema, updateIntakeSchema, answerInputSchema, attachResourceInputSchema, normalizeRequestType, isBlank, parseWithIssues, validateCreateInput, validateUpdateInput, validateAnswerInput, validateAttachResourceInput, validateAttachmentInput, validateRelatedResourceInput, validateQuestionTemplateInput, validateFieldSource, deterministicSummary, deterministicTitle, } from './validation.js';
12
+ export { ALLOWED_TRANSITIONS, MUTABLE_STATUSES, canTransition, assertTransition, isMutableStatus, isTerminalStatus, isKnownStatus, } from './lifecycle.js';
13
+ export { buildInitialQuestions, pendingQuestions, upsertQuestion } from './questions.js';
14
+ export { INTAKE_OPERATIONS, AllowAllIntakeAuthorizer, DenyAllIntakeAuthorizer, ProjectMembershipIntakeAuthorizer, type IntakeAuthorizer, type IntakeOperation, type ProjectMembershipIntakeAuthorizerOptions, } from './authorization.js';
15
+ export { NoopIntakeLogger, InMemoryIntakeLogger, type IntakeLogger, type IntakeLogFields, } from './logger.js';
16
+ export { INTAKE_COUNTERS, INTAKE_TIMERS, NoopIntakeMetrics, InMemoryIntakeMetrics, type IntakeCounter, type IntakeTimer, type IntakeMetrics, } from './metrics.js';
17
+ export { IntakeEventEmitter } from './events.js';
18
+ export { RequirementIntakeStore, newIntakeId, type RequirementIntakeStoreOptions, type IntakeIndexEntry, type StoreUpdateOptions, type StoreCreateResult, } from './store.js';
19
+ export { llmSuggestionOutputSchema, validateLlmSuggestionOutput, toProposals, assertSuggestionString, type LlmSuggestionRequest, type LlmSuggestionOutput, type LlmSuggestionGenerator, type NormalizedLlmSuggestion, } from './suggestions.js';
20
+ export { RequirementIntakeService, type RequirementIntakeServiceOptions, type IntakeCreateResult, type IntakeSubmitResult, type IntakeListFilter, } from './service.js';
21
+ //# sourceMappingURL=index.d.ts.map