engineering-memory 0.2.5 → 0.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engineering-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Installs the Engineering Memory skill and its local MCP bridge. Sign in after installing; your organization and project are resolved from your account.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -25,8 +25,26 @@ export const validationIds = [
|
|
|
25
25
|
export class BridgeService {
|
|
26
26
|
dependencies;
|
|
27
27
|
taskQueues = new Map();
|
|
28
|
+
deferDeliveries;
|
|
29
|
+
backgroundDelivery = Promise.resolve();
|
|
28
30
|
constructor(dependencies) {
|
|
29
31
|
this.dependencies = dependencies;
|
|
32
|
+
this.deferDeliveries = dependencies.deferDeliveries ?? true;
|
|
33
|
+
}
|
|
34
|
+
deliverInBackground() {
|
|
35
|
+
if (!this.deferDeliveries)
|
|
36
|
+
return;
|
|
37
|
+
this.backgroundDelivery = this.backgroundDelivery.then(async () => {
|
|
38
|
+
try {
|
|
39
|
+
await this.flushOutbox();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async settleDeliveries() {
|
|
47
|
+
await this.backgroundDelivery;
|
|
30
48
|
}
|
|
31
49
|
async sessionBootstrap(input) {
|
|
32
50
|
return await this.execute(async () => {
|
|
@@ -676,15 +694,31 @@ export class BridgeService {
|
|
|
676
694
|
}));
|
|
677
695
|
assertSafeToPersist(cleanJson({ entries }));
|
|
678
696
|
const snapshot = await this.activeTaskSnapshot(input.taskId, undefined, input.repoRoot);
|
|
697
|
+
const body = cleanJson({
|
|
698
|
+
taskId: input.taskId,
|
|
699
|
+
entries,
|
|
700
|
+
expectedTaskVersion: snapshot.taskVersion,
|
|
701
|
+
});
|
|
702
|
+
await this.dependencies.gate.invalidateTask(input.taskId);
|
|
703
|
+
if (this.deferDeliveries) {
|
|
704
|
+
const queued = await this.dependencies.outbox.enqueue({
|
|
705
|
+
operation: 'task.reconcile',
|
|
706
|
+
method: 'POST',
|
|
707
|
+
path: endpoints.taskReconcile,
|
|
708
|
+
body,
|
|
709
|
+
});
|
|
710
|
+
this.deliverInBackground();
|
|
711
|
+
return asJsonValue({
|
|
712
|
+
reconciled: entries.map((entry) => entry.resourceId),
|
|
713
|
+
queued: true,
|
|
714
|
+
outboxId: queued.id,
|
|
715
|
+
deliveryStatus: 'pending',
|
|
716
|
+
});
|
|
717
|
+
}
|
|
679
718
|
const response = await this.dependencies.client.request(endpoints.taskReconcile, {
|
|
680
719
|
method: 'POST',
|
|
681
|
-
body
|
|
682
|
-
taskId: input.taskId,
|
|
683
|
-
entries,
|
|
684
|
-
expectedTaskVersion: snapshot.taskVersion,
|
|
685
|
-
}),
|
|
720
|
+
body,
|
|
686
721
|
});
|
|
687
|
-
await this.dependencies.gate.invalidateTask(input.taskId);
|
|
688
722
|
return asJsonValue({ reconciliations: response.data });
|
|
689
723
|
});
|
|
690
724
|
}
|
|
@@ -1363,7 +1397,10 @@ export class BridgeService {
|
|
|
1363
1397
|
assertSafeToPersist(cleanJson(safeInput));
|
|
1364
1398
|
return await this.taskExclusive(input.taskId, async () => {
|
|
1365
1399
|
await this.recoverJournalOutbox(input.projectId, input.taskSlug);
|
|
1366
|
-
|
|
1400
|
+
if (this.deferDeliveries)
|
|
1401
|
+
this.deliverInBackground();
|
|
1402
|
+
else
|
|
1403
|
+
await this.flushOutbox();
|
|
1367
1404
|
const journalInput = {
|
|
1368
1405
|
eventId: idempotencyKey,
|
|
1369
1406
|
taskId: input.taskId,
|
|
@@ -1426,7 +1463,8 @@ export class BridgeService {
|
|
|
1426
1463
|
await this.dependencies.activeContexts.updateTaskSnapshot(input.taskId, expectedTaskVersion + 1, pointer.lastSequence + 1);
|
|
1427
1464
|
}
|
|
1428
1465
|
const predecessorEntries = taskEntries.filter((entry) => entry.id !== queued.id);
|
|
1429
|
-
if (predecessorEntries.length > 0) {
|
|
1466
|
+
if (predecessorEntries.length > 0 || this.deferDeliveries) {
|
|
1467
|
+
this.deliverInBackground();
|
|
1430
1468
|
return asJsonValue({
|
|
1431
1469
|
local: { directory: staged.directory, applied: staged.applied },
|
|
1432
1470
|
queued: true,
|
|
@@ -26,7 +26,7 @@ A request can be as short as "add the KYC flow from Figma". That is enough, and
|
|
|
26
26
|
|
|
27
27
|
1. The Figma address arrives in the core pack as the `figma_reference` record. Never ask where the design lives; read it. If the user names a different file, that is a correction to that record — propose the revision and ask for approval in the same reply, so the next session already knows.
|
|
28
28
|
2. Ask for existing flows with `memory.query` naming the `flow_logic` kind. If the flow is already recorded, read it and its history before touching anything.
|
|
29
|
-
3. Read the flow in Figma: which frames belong to it, the order its prototype links imply, and the node id of every screen. A file-level link is orientation, not evidence.
|
|
29
|
+
3. Read the flow in Figma, at orientation depth only: which frames belong to it, the order its prototype links imply, and the node id of every screen. A file-level link is orientation, not evidence. Stop there — geometry, fills, typography and tokens belong to the screen being built, read at the moment it is built, not gathered for the whole flow up front. Read this yourself rather than delegating it; nothing else can proceed until it is answered, so a second agent only adds the wait.
|
|
30
30
|
4. Read what surrounds it — the screen records the flow starts from and returns to, the navigation contract, the trackers that already exist — and pull `memory.history` wherever the header shows earlier work or a run of corrections.
|
|
31
31
|
5. Decide the entry point, the order, whether a tracker is needed and exactly what it carries, and where the flow ends.
|
|
32
32
|
6. Ask the user what neither Figma nor memory can answer. Where a flow is entered from and what abandoning it halfway does are almost never in the design. Never invent them, and never invent the content of a document the flow displays.
|
|
@@ -45,7 +45,9 @@ answer in the design; do not spend the read there.
|
|
|
45
45
|
|
|
46
46
|
Do not pull history for everything. Pull it for the records the task actually touches, and for anything the header shows a surprising number of corrections on. Record `task.checkpoint` with type `discovery` and update STATE, DECISIONS, DISCOVERY, and HANDOFF projections through the bridge.
|
|
47
47
|
|
|
48
|
-
Send calls that do not feed each other in one batch rather than one at a time: reads of any kind, and proposals for different records. Reconcile every record the task touched in a single `task.reconcile` call with `entries`, not one call per record.
|
|
48
|
+
Send calls that do not feed each other in one batch rather than one at a time: reads of any kind, and proposals for different records. Reconcile every record the task touched in a single `task.reconcile` call with `entries`, not one call per record.
|
|
49
|
+
|
|
50
|
+
Checkpoints, recorded corrections and reconciliations return before the backend has them, reporting `deliveryStatus: 'pending'` with the task version they will occupy. That is a completed call, not a pending one: the journal is already durable, delivery is already under way, and `task.verify` refuses while anything remains undelivered. Do not wait for it, poll it, or send it again. Wait only for what the next step uses — a proposal's identifiers, an approval's revision, a prepared lease, and verification itself.
|
|
49
51
|
|
|
50
52
|
Temporary code written to reach or force a path — a pinned state, a fixed service response, a jump straight to the screen — is allowed and expected, carries the marker `ENGINEERING-MEMORY-TEMPORARY` with its reason, and is removed before verification. `task.verify` refuses while any marker is in the tree and names every line, and the commit gate refuses while one is staged.
|
|
51
53
|
|
|
@@ -22,4 +22,12 @@ Screen logic is canonical in the backend. A screen revision should cover purpose
|
|
|
22
22
|
|
|
23
23
|
Component mappings should cover the Flutter symbol and path, public API, purpose, states, design tokens, responsive behavior, Figma file and exact node IDs, assets, usage guidance, tests, selectors, and evidence. Mark missing or partial Figma evidence instead of inventing node IDs.
|
|
24
24
|
|
|
25
|
+
A backend project records three kinds instead of screens and components, and which kinds a project has is declared in its project profile rather than assumed.
|
|
26
|
+
|
|
27
|
+
A `module_logic` record covers one resource: what it is for, the entities it owns, the endpoints it exposes, the guards that protect them, the background jobs and queues it runs, what it depends on and why that dependency exists.
|
|
28
|
+
|
|
29
|
+
A `data_model` record covers one entity: its columns and their types, its relations, what it inherits from the base entity, and every index it declares **with the query that index exists to serve**. An index nobody can name a query for is reported as a finding rather than recorded as a fact.
|
|
30
|
+
|
|
31
|
+
An `api_endpoint` record covers one route and is the surface a client binds to: method and path, the request DTO, the response DTO, the guards, the error codes it can return, the localization keys it uses, its pagination shape, and which client consumes it. When a linked project exists, these are the records that cross to it, so they are written for a reader on the other side of the wire rather than for the person who wrote the controller.
|
|
32
|
+
|
|
25
33
|
Do not send full source trees, generated files, vendor assets, raw diffs, credentials, or user data as memory content. Prefer relative paths, symbols, hashes, contract summaries, exact approved Figma identifiers, and bounded task-specific evidence.
|