instar 1.3.25 → 1.3.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/feedback-factory/dispatch/handlers.d.ts +44 -0
- package/dist/feedback-factory/dispatch/handlers.d.ts.map +1 -0
- package/dist/feedback-factory/dispatch/handlers.js +101 -0
- package/dist/feedback-factory/dispatch/handlers.js.map +1 -0
- package/dist/feedback-factory/store/FeedbackStore.d.ts +17 -0
- package/dist/feedback-factory/store/FeedbackStore.d.ts.map +1 -1
- package/dist/feedback-factory/store/FeedbackStore.js +14 -0
- package/dist/feedback-factory/store/FeedbackStore.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.26.md +25 -0
- package/upgrades/side-effects/feedback-factory-dispatch-handlers.md +32 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* handlers.ts — framework-agnostic dispatch (guidance-out) request handlers.
|
|
3
|
+
*
|
|
4
|
+
* Faithful port of handleList + handleCreate from the reference dispatch endpoint
|
|
5
|
+
* (the-portal/pages/api/instar/dispatches/index.ts), lifted out of Next.js into
|
|
6
|
+
* pure request→response functions over the FeedbackStore + the ported dispatch
|
|
7
|
+
* logic (dispatch.ts). The canonical front binds these; the operated deploy
|
|
8
|
+
* supplies the real store + the internal key. Reproduces the reference's exact
|
|
9
|
+
* status codes, auth, validation messages, and the version-compat filter.
|
|
10
|
+
*
|
|
11
|
+
* Note: unlike the public feedback receiver (which DEFAULTS an invalid type),
|
|
12
|
+
* dispatch create REJECTS an invalid type — it's an internal, authed endpoint.
|
|
13
|
+
*/
|
|
14
|
+
import type { FeedbackStore } from '../store/FeedbackStore.js';
|
|
15
|
+
export interface DispatchListRequest {
|
|
16
|
+
headers: Record<string, string | string[] | undefined>;
|
|
17
|
+
query: {
|
|
18
|
+
since?: string;
|
|
19
|
+
type?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface DispatchCreateRequest {
|
|
23
|
+
headers: Record<string, string | string[] | undefined>;
|
|
24
|
+
body: unknown;
|
|
25
|
+
}
|
|
26
|
+
export interface DispatchResponse {
|
|
27
|
+
status: number;
|
|
28
|
+
json: unknown;
|
|
29
|
+
}
|
|
30
|
+
/** Port of handleList: fingerprint-gate, since/type filter, version-compat filter, mapped output. */
|
|
31
|
+
export declare function handleDispatchList(req: DispatchListRequest, deps: {
|
|
32
|
+
store: FeedbackStore;
|
|
33
|
+
now: number;
|
|
34
|
+
}): DispatchResponse;
|
|
35
|
+
export interface DispatchCreateDeps {
|
|
36
|
+
store: FeedbackStore;
|
|
37
|
+
/** The internal API key the caller must present (x-internal-key or Bearer). */
|
|
38
|
+
internalKey: string;
|
|
39
|
+
now: number;
|
|
40
|
+
generateDispatchId?: () => string;
|
|
41
|
+
}
|
|
42
|
+
/** Port of handleCreate: internal-auth, validation (type REJECTED, priority defaulted), dedup-by-title, create. */
|
|
43
|
+
export declare function handleDispatchCreate(req: DispatchCreateRequest, deps: DispatchCreateDeps): DispatchResponse;
|
|
44
|
+
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/dispatch/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1C;AACD,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,EAAE,OAAO,CAAC;CACf;AACD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAOD,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAqC1H;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,aAAa,CAAC;IACrB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;CACnC;AAED,mHAAmH;AACnH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,qBAAqB,EAAE,IAAI,EAAE,kBAAkB,GAAG,gBAAgB,CAoD3G"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* handlers.ts — framework-agnostic dispatch (guidance-out) request handlers.
|
|
3
|
+
*
|
|
4
|
+
* Faithful port of handleList + handleCreate from the reference dispatch endpoint
|
|
5
|
+
* (the-portal/pages/api/instar/dispatches/index.ts), lifted out of Next.js into
|
|
6
|
+
* pure request→response functions over the FeedbackStore + the ported dispatch
|
|
7
|
+
* logic (dispatch.ts). The canonical front binds these; the operated deploy
|
|
8
|
+
* supplies the real store + the internal key. Reproduces the reference's exact
|
|
9
|
+
* status codes, auth, validation messages, and the version-compat filter.
|
|
10
|
+
*
|
|
11
|
+
* Note: unlike the public feedback receiver (which DEFAULTS an invalid type),
|
|
12
|
+
* dispatch create REJECTS an invalid type — it's an internal, authed endpoint.
|
|
13
|
+
*/
|
|
14
|
+
import { isValidDispatchType, isValidDispatchPriority, SEMVER_RE, DISPATCH_TYPES, filterDispatchesForVersion, normalizeDispatchTitle, } from './dispatch.js';
|
|
15
|
+
function header(headers, name) {
|
|
16
|
+
const v = headers[name];
|
|
17
|
+
return Array.isArray(v) ? v[0] : v;
|
|
18
|
+
}
|
|
19
|
+
/** Port of handleList: fingerprint-gate, since/type filter, version-compat filter, mapped output. */
|
|
20
|
+
export function handleDispatchList(req, deps) {
|
|
21
|
+
const ua = (header(req.headers, 'user-agent') || '').toLowerCase();
|
|
22
|
+
if (!ua.includes('instar/')) {
|
|
23
|
+
return { status: 400, json: { error: 'Invalid request format' } };
|
|
24
|
+
}
|
|
25
|
+
// since: only applied when it parses to a valid date (mirrors the reference).
|
|
26
|
+
let since;
|
|
27
|
+
if (req.query.since && !isNaN(new Date(req.query.since).getTime())) {
|
|
28
|
+
since = new Date(req.query.since).toISOString();
|
|
29
|
+
}
|
|
30
|
+
const type = req.query.type && isValidDispatchType(req.query.type) ? req.query.type : undefined;
|
|
31
|
+
let dispatches = deps.store.listDispatches({ since, type });
|
|
32
|
+
const version = header(req.headers, 'x-instar-version');
|
|
33
|
+
if (version && SEMVER_RE.test(version)) {
|
|
34
|
+
dispatches = filterDispatchesForVersion(dispatches, version);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
status: 200,
|
|
38
|
+
json: {
|
|
39
|
+
dispatches: dispatches.map((d) => ({
|
|
40
|
+
dispatchId: d.dispatchId,
|
|
41
|
+
type: d.type,
|
|
42
|
+
title: d.title,
|
|
43
|
+
content: d.content,
|
|
44
|
+
priority: d.priority,
|
|
45
|
+
minVersion: d.minVersion ?? null,
|
|
46
|
+
maxVersion: d.maxVersion ?? null,
|
|
47
|
+
createdAt: d.createdAt,
|
|
48
|
+
})),
|
|
49
|
+
count: dispatches.length,
|
|
50
|
+
asOf: new Date(deps.now).toISOString(),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Port of handleCreate: internal-auth, validation (type REJECTED, priority defaulted), dedup-by-title, create. */
|
|
55
|
+
export function handleDispatchCreate(req, deps) {
|
|
56
|
+
const provided = header(req.headers, 'x-internal-key')
|
|
57
|
+
|| header(req.headers, 'authorization')?.replace('Bearer ', '');
|
|
58
|
+
if (provided !== deps.internalKey) {
|
|
59
|
+
return { status: 401, json: { error: 'Authentication required' } };
|
|
60
|
+
}
|
|
61
|
+
if (!req.body || typeof req.body !== 'object') {
|
|
62
|
+
return { status: 400, json: { error: 'Request body must be JSON' } };
|
|
63
|
+
}
|
|
64
|
+
const body = req.body;
|
|
65
|
+
if (!body.title || typeof body.title !== 'string' || body.title.trim().length < 3) {
|
|
66
|
+
return { status: 400, json: { error: 'title is required (min 3 characters)' } };
|
|
67
|
+
}
|
|
68
|
+
if (!body.content || typeof body.content !== 'string' || body.content.trim().length < 10) {
|
|
69
|
+
return { status: 400, json: { error: 'content is required (min 10 characters)' } };
|
|
70
|
+
}
|
|
71
|
+
if (!body.type || !isValidDispatchType(body.type)) {
|
|
72
|
+
return { status: 400, json: { error: `type must be one of: ${DISPATCH_TYPES.join(', ')}` } };
|
|
73
|
+
}
|
|
74
|
+
const priority = body.priority && isValidDispatchPriority(body.priority) ? body.priority : 'normal';
|
|
75
|
+
if (body.minVersion && !SEMVER_RE.test(body.minVersion)) {
|
|
76
|
+
return { status: 400, json: { error: 'minVersion must be valid semver' } };
|
|
77
|
+
}
|
|
78
|
+
if (body.maxVersion && !SEMVER_RE.test(body.maxVersion)) {
|
|
79
|
+
return { status: 400, json: { error: 'maxVersion must be valid semver' } };
|
|
80
|
+
}
|
|
81
|
+
const normalizedTitle = normalizeDispatchTitle(body.title);
|
|
82
|
+
const existing = deps.store.findDispatchByTitle(normalizedTitle);
|
|
83
|
+
if (existing) {
|
|
84
|
+
return { status: 200, json: { dispatchId: existing.dispatchId, created: false, duplicate: true } };
|
|
85
|
+
}
|
|
86
|
+
const dispatchId = (deps.generateDispatchId ?? (() => `dsp-${deps.now.toString(36)}-${Math.random().toString(36).slice(2, 6)}`))();
|
|
87
|
+
const record = {
|
|
88
|
+
dispatchId,
|
|
89
|
+
type: body.type,
|
|
90
|
+
title: normalizedTitle,
|
|
91
|
+
content: body.content.trim().slice(0, 50000),
|
|
92
|
+
priority: priority,
|
|
93
|
+
minVersion: body.minVersion || null,
|
|
94
|
+
maxVersion: body.maxVersion || null,
|
|
95
|
+
active: true,
|
|
96
|
+
createdAt: new Date(deps.now).toISOString(),
|
|
97
|
+
};
|
|
98
|
+
deps.store.createDispatch(record);
|
|
99
|
+
return { status: 201, json: { dispatchId, created: true, duplicate: false } };
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../../src/feedback-factory/dispatch/handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import type { FeedbackItem, Cluster, ClusterResult } from '../processor/types.js';
|
|
16
16
|
import type { ReopenDecision } from '../processor/reopen.js';
|
|
17
|
+
import type { DispatchRecord } from '../dispatch/dispatch.js';
|
|
17
18
|
/** Read-only observability counters for the capture→cluster→reopen loop (spec §2.7). */
|
|
18
19
|
export interface FeedbackMetrics {
|
|
19
20
|
captured: number;
|
|
@@ -39,6 +40,15 @@ export interface FeedbackStore {
|
|
|
39
40
|
hasFeedback(feedbackId: string): boolean;
|
|
40
41
|
/** Persist a newly-received feedback item (the receiver write). */
|
|
41
42
|
addFeedback(item: FeedbackItem): void;
|
|
43
|
+
/** Active dispatches matching an optional since/type filter, oldest-first (the dispatch list read). */
|
|
44
|
+
listDispatches(filter?: {
|
|
45
|
+
since?: string;
|
|
46
|
+
type?: string;
|
|
47
|
+
}): DispatchRecord[];
|
|
48
|
+
/** Find an existing dispatch by exact (normalized) title — the create-dedup check. */
|
|
49
|
+
findDispatchByTitle(title: string): DispatchRecord | undefined;
|
|
50
|
+
/** Persist a new dispatch. */
|
|
51
|
+
createDispatch(record: DispatchRecord): void;
|
|
42
52
|
metrics(): FeedbackMetrics;
|
|
43
53
|
}
|
|
44
54
|
/**
|
|
@@ -64,6 +74,13 @@ export declare class InMemoryFeedbackStore implements FeedbackStore {
|
|
|
64
74
|
markProcessed(feedbackId: string, clusterId: string): void;
|
|
65
75
|
hasFeedback(feedbackId: string): boolean;
|
|
66
76
|
addFeedback(item: FeedbackItem): void;
|
|
77
|
+
private dispatches;
|
|
78
|
+
listDispatches(filter?: {
|
|
79
|
+
since?: string;
|
|
80
|
+
type?: string;
|
|
81
|
+
}): DispatchRecord[];
|
|
82
|
+
findDispatchByTitle(title: string): DispatchRecord | undefined;
|
|
83
|
+
createDispatch(record: DispatchRecord): void;
|
|
67
84
|
metrics(): FeedbackMetrics;
|
|
68
85
|
/** Test helper: snapshot all clusters. */
|
|
69
86
|
allClusters(): Cluster[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackStore.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/store/FeedbackStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"FeedbackStore.d.ts","sourceRoot":"","sources":["../../../src/feedback-factory/store/FeedbackStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -83,6 +83,20 @@ export class InMemoryFeedbackStore {
|
|
|
83
83
|
addFeedback(item) {
|
|
84
84
|
this.feedback.set(item.feedbackId, { status: 'unprocessed', ...item });
|
|
85
85
|
}
|
|
86
|
+
dispatches = new Map();
|
|
87
|
+
listDispatches(filter) {
|
|
88
|
+
return [...this.dispatches.values()]
|
|
89
|
+
.filter((d) => d.active !== false)
|
|
90
|
+
.filter((d) => !filter?.since || String(d.createdAt ?? '') >= filter.since)
|
|
91
|
+
.filter((d) => !filter?.type || d.type === filter.type)
|
|
92
|
+
.sort((a, b) => String(a.createdAt ?? '').localeCompare(String(b.createdAt ?? '')));
|
|
93
|
+
}
|
|
94
|
+
findDispatchByTitle(title) {
|
|
95
|
+
return [...this.dispatches.values()].find((d) => d.title === title);
|
|
96
|
+
}
|
|
97
|
+
createDispatch(record) {
|
|
98
|
+
this.dispatches.set(record.dispatchId, { active: true, ...record });
|
|
99
|
+
}
|
|
86
100
|
metrics() {
|
|
87
101
|
return { ...this.counts };
|
|
88
102
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackStore.js","sourceRoot":"","sources":["../../../src/feedback-factory/store/FeedbackStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"FeedbackStore.js","sourceRoot":"","sources":["../../../src/feedback-factory/store/FeedbackStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyCH;;;;;GAKG;AACH,MAAM,OAAO,qBAAqB;IACxB,QAAQ,GAAG,IAAI,GAAG,EAAwB,CAAC;IAC3C,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IACtC,MAAM,GAAoB,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAEtF,YAAY,IAA0D;QACpE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,QAAQ,IAAI,EAAE;YAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACvG,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,QAAQ,IAAI,EAAE;YAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,sBAAsB;QACpB,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;aAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,aAAa,CAAC,KAAK,aAAa,CAAC;aAC5D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1F,CAAC;IAED,iBAAiB;QACf,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;IAC5E,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAED,qBAAqB,CAAC,SAAiB,EAAE,IAAkB;QACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,WAAW,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE;gBAC3B,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC;aAC7F,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,SAAiB,EAAE,KAAmB;QACrD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC;YAAE,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;IAED,WAAW,CAAC,SAAiB,EAAE,QAAwB;QACrD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC;QAC9B,IAAI,QAAQ,CAAC,cAAc;YAAE,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC;QACrC,MAAM,KAAK,GAAI,CAAC,CAAC,KAAK,CAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,aAAa,CAAC,UAAkB,EAAE,SAAiB;QACjD,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC;YAAC,CAAC,CAAC,MAAM,GAAG,YAAY,CAAC;YAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC;QAAC,CAAC;QAC5D,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,WAAW,CAAC,UAAkB;QAC5B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,WAAW,CAAC,IAAkB;QAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;IACzE,CAAC;IAEO,UAAU,GAAG,IAAI,GAAG,EAA0B,CAAC;IAEvD,cAAc,CAAC,MAA0C;QACvD,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;aACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC;aACjC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC;aAC1E,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC;aACtD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,mBAAmB,CAAC,KAAa;QAC/B,OAAO,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IACtE,CAAC;IAED,cAAc,CAAC,MAAsB;QACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,OAAO;QACL,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED,0CAA0C;IAC1C,WAAW;QACT,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-05-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-05-27T16:22:05.020Z",
|
|
5
|
+
"instarVersion": "1.3.26",
|
|
6
6
|
"entryCount": 192,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: patch -->
|
|
4
|
+
|
|
5
|
+
## What Changed
|
|
6
|
+
|
|
7
|
+
Twelfth increment of the **Feedback Factory Migration** (Dawn → Echo; spec `docs/specs/feedback-factory-migration.md`, approved). Ports the **dispatch request handlers** — the "send guidance back to agents" side — out of the reference Next.js endpoint (`the-portal/pages/api/instar/dispatches/index.ts`) into framework-agnostic functions at `src/feedback-factory/dispatch/handlers.ts`, plus the store operations they need (`listDispatches` / `findDispatchByTitle` / `createDispatch`).
|
|
8
|
+
|
|
9
|
+
List gates on the agent fingerprint, applies the since/type filter and the version-compat filter (so guidance only reaches the agent versions it applies to), and returns the mapped result. Create requires the internal key, validates the body, dedups by title, and creates. **Not wired into any route yet** — no behavioral change. This completes the front door's request layer (receive a report + send guidance both ported).
|
|
10
|
+
|
|
11
|
+
## What to Tell Your User
|
|
12
|
+
|
|
13
|
+
- Both halves of the feedback loop's front door are now ported and behave exactly like Dawn's originals — catching reports, and sending version-targeted guidance back.
|
|
14
|
+
- That's the entire request + decision layer done. What's left to actually go live is the database wiring and the deploy — now being coordinated with Dawn.
|
|
15
|
+
|
|
16
|
+
## Summary of New Capabilities
|
|
17
|
+
|
|
18
|
+
| Capability | How to Use |
|
|
19
|
+
|-----------|-----------|
|
|
20
|
+
| Dispatch handlers (TS port) | `handleDispatchList` + `handleDispatchCreate` in `src/feedback-factory/dispatch/handlers.ts` — framework-agnostic, not yet wired |
|
|
21
|
+
| Store dispatch seam | `FeedbackStore.listDispatches` / `findDispatchByTitle` / `createDispatch` |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
- Reference is TypeScript, so equivalence is by faithful transcription plus both-sides-of-boundary tests (8): list UA-gate 400; basic list with count + asOf; version-compat filter hiding a min-version dispatch from an older agent; type filter; create 401 without the key; Bearer-form auth accepted; exact validation messages for title/content/type/version; 201-then-200-duplicate dedup by title.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Side-Effects Review — dispatch request handlers (Phase 1, increment 12)
|
|
2
|
+
|
|
3
|
+
**Slug:** `feedback-factory-dispatch-handlers`
|
|
4
|
+
**Date:** `2026-05-27`
|
|
5
|
+
**Author:** Echo (autonomous → interactive)
|
|
6
|
+
**Spec:** `docs/specs/feedback-factory-migration.md` (converged v2, approved by Justin 2026-05-26)
|
|
7
|
+
**Scope:** The framework-agnostic dispatch request handlers (faithful ports of `handleList` + `handleCreate`) + the store dispatch ops. Completes the front's request layer (receiver + dispatch). NOT the HTTP binding/deploy.
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
Adds `src/feedback-factory/dispatch/handlers.ts` — `handleDispatchList` + `handleDispatchCreate`, faithful ports of the reference dispatch endpoint, as pure request→response functions over the FeedbackStore + the ported dispatch logic (dispatch.ts). Adds `FeedbackStore.listDispatches`/`findDispatchByTitle`/`createDispatch` + `InMemoryFeedbackStore` impls. Reproduces the reference exactly: list gates on the `instar/` UA, applies the since/type DB filter + the version-compat filter, returns the mapped `{dispatches, count, asOf}`; create requires the internal key (x-internal-key or Bearer), validates (title ≥3, content ≥10, **type REJECTED** if invalid, priority defaulted, min/maxVersion semver), dedups by normalized title, and returns 201/200-duplicate. **Not wired into any route yet** — no behavioral change.
|
|
12
|
+
|
|
13
|
+
Note the asymmetry with the public receiver: dispatch create REJECTS an invalid type (it's an internal, authed endpoint), whereas the public feedback receiver defaults it — both faithful to their respective references.
|
|
14
|
+
|
|
15
|
+
## Seven-dimension review
|
|
16
|
+
|
|
17
|
+
1. **Over/under-reach** — Pure request→response functions over the store. Not wired to any route. The internal-key auth is injected (the operated binding provides the real key).
|
|
18
|
+
2. **Level-of-abstraction fit** — Reusable "recipe" → core; the framework binding + real store are operated-side. The version-compat filter reuses the already-tested `filterDispatchesForVersion`.
|
|
19
|
+
3. **Signal vs Authority** — N/A; create is internal-authed (the reference's 401 gate is reproduced); list is the `instar/`-UA fingerprint gate.
|
|
20
|
+
4. **Interactions** — `dispatch/handlers.ts` imports dispatch.ts + the store interface; nothing imports it yet. Store gains a dispatches Map (isolated from feedback/clusters).
|
|
21
|
+
5. **Rollback cost** — Trivial: delete the handlers + store ops + tests.
|
|
22
|
+
6. **Migration parity** — N/A. Core library code; no agent-installed file.
|
|
23
|
+
7. **Failure modes** — (a) Response divergence → exact status/message/auth/order tested. (b) Version-compat filter wrong → reuses the 9/9-tested `filterDispatchesForVersion` + a hidden-from-older-agent handler test. (c) Dedup-by-title divergence → tested (201 then 200-duplicate). (d) Auth bypass → 401 tested for missing/wrong key; Bearer form tested.
|
|
24
|
+
|
|
25
|
+
## Tests
|
|
26
|
+
|
|
27
|
+
- Tier-1 unit (CI): `tests/unit/feedback-factory/dispatch-handlers.test.ts` — 8 tests (list: UA-gate, basic list+asOf, version-compat hide, type filter; create: 401, Bearer-auth, validation messages, 201-then-dedup).
|
|
28
|
+
- No E2E this increment — the route binding + deploy are gated on the blocked app-placement + Prisma-adapter decisions.
|
|
29
|
+
|
|
30
|
+
## Status note
|
|
31
|
+
|
|
32
|
+
With this, the **entire front + processor request/decision layer is ported** (receiver submit + dispatch list/create handlers, all the processor brain, the store seam + composition + observability). The only remaining feedback-factory work is genuinely blocked on external inputs: the real Prisma adapter (DB creds/schema), the framework binding + Vercel deploy (app-placement + Vercel project), agent-awareness (after live), and the operational cutover (Dawn + live systems).
|