engineering-memory 1.0.0 → 1.2.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": "1.0.0",
3
+ "version": "1.2.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",
@@ -4,6 +4,7 @@ import { assertManagedPath, canonicalPath } from '../utilities/files.js';
4
4
  import { sha256, stableStringify } from '../utilities/hash.js';
5
5
  import { NativeCommandRunner } from '../utilities/process.js';
6
6
  export const temporaryScaffoldingMarker = 'ENGINEERING-MEMORY-TEMPORARY';
7
+ export const temporaryScaffoldingPattern = `${temporaryScaffoldingMarker}:`;
7
8
  export class GitInspector {
8
9
  runner;
9
10
  constructor(runner = new NativeCommandRunner()) {
@@ -47,6 +48,7 @@ export class GitInspector {
47
48
  repoRoot: root,
48
49
  head,
49
50
  changedPaths,
51
+ worktreeHash: sha256(`${stableStringify(normalizedManifest)}\n`),
50
52
  diffHash: sha256(`${head ?? '<unborn>'}\n${stableStringify(normalizedManifest)}\n`),
51
53
  };
52
54
  }
@@ -303,7 +305,7 @@ export class GitInspector {
303
305
  return [...found.values()].sort((left, right) => left.path === right.path ? left.line - right.line : left.path.localeCompare(right.path));
304
306
  }
305
307
  async markerMatches(root, scope, origin) {
306
- const result = await this.runner.run('git', ['grep', '--no-color', '-n', '-I', '-F', ...scope, '-e', temporaryScaffoldingMarker], { cwd: root });
308
+ const result = await this.runner.run('git', ['grep', '--no-color', '-n', '-I', '-F', ...scope, '-e', temporaryScaffoldingPattern], { cwd: root });
307
309
  if (result.exitCode !== 0)
308
310
  return [];
309
311
  return result.stdout
@@ -81,6 +81,13 @@ export const engineeringMemoryToolNames = [
81
81
  'auth.signin_browser',
82
82
  'auth.logout',
83
83
  ];
84
+ const discoverableKindNames = [
85
+ 'screen_logic',
86
+ 'component_mapping',
87
+ 'module_logic',
88
+ 'data_model',
89
+ 'api_endpoint',
90
+ ];
84
91
  const reconciliationEntry = z.object({
85
92
  resourceId: z.string().min(1),
86
93
  type: z.enum(['approved_revision', 'no_semantic_memory_change']),
@@ -193,6 +200,9 @@ export function registerEngineeringMemoryTools(server, service) {
193
200
  'figma_mapping',
194
201
  'figma_reference',
195
202
  'flow_logic',
203
+ 'module_logic',
204
+ 'data_model',
205
+ 'api_endpoint',
196
206
  'current_deviation',
197
207
  'quality_gate',
198
208
  'task_history',
@@ -348,7 +358,7 @@ export function registerEngineeringMemoryTools(server, service) {
348
358
  newResources: z
349
359
  .array(z.object({
350
360
  path: z.string().min(1),
351
- kind: z.enum(['screen_logic', 'component_mapping']),
361
+ kind: z.enum(discoverableKindNames),
352
362
  resourceKey: z.string().min(2),
353
363
  }))
354
364
  .optional(),
@@ -382,13 +392,7 @@ export function registerEngineeringMemoryTools(server, service) {
382
392
  content: z.string().min(2),
383
393
  discoveryUnits: z
384
394
  .array(z.object({
385
- kind: z.enum([
386
- 'screen_logic',
387
- 'component_mapping',
388
- 'module_logic',
389
- 'data_model',
390
- 'api_endpoint',
391
- ]),
395
+ kind: z.enum(discoverableKindNames),
392
396
  patterns: z.array(z.string().min(1)).min(1),
393
397
  }))
394
398
  .min(1)
@@ -94,7 +94,9 @@ export class BridgeService {
94
94
  taskKind: persistedBootstrap.taskKind,
95
95
  mode: persistedBootstrap.mode ?? 'write',
96
96
  repoFingerprint: repository.repoFingerprint,
97
- ...(input.mode === 'read_only' ? { baselineDiffHash: repository.git.diffHash } : {}),
97
+ ...(input.mode === 'read_only'
98
+ ? { baselineDiffHash: repository.git.worktreeHash }
99
+ : {}),
98
100
  checkpointIdempotencyKey: checkpointId,
99
101
  knownRevisions: persistedBootstrap.knownRevisions,
100
102
  }),
@@ -1009,6 +1011,7 @@ export class BridgeService {
1009
1011
  changedPaths: taskMode === 'read_only' ? [] : taskChangedPaths,
1010
1012
  pathChanges: pathChangeManifest,
1011
1013
  diffHash: repository.git.diffHash,
1014
+ ...(taskMode === 'read_only' ? { worktreeHash: repository.git.worktreeHash } : {}),
1012
1015
  validations,
1013
1016
  pendingOutboxCount,
1014
1017
  newResources: input.newResources,
@@ -1305,12 +1308,19 @@ export class BridgeService {
1305
1308
  const authenticated = authentication.authenticated === true;
1306
1309
  const state = decision?.state ?? (repository.projectId ? 'bound' : 'none');
1307
1310
  const client = await this.clientUpdate(authenticated);
1311
+ const shipped = objectValue(objectValue(client)?.shippedKnowledge);
1312
+ const incomplete = shipped && (shipped.failure !== null || Number(shipped.live) < Number(shipped.expected));
1308
1313
  return asJsonValue({
1309
1314
  authenticated,
1310
1315
  repository: publicRepository(repository),
1311
1316
  decision: state,
1312
1317
  projectId: repository.projectId ?? decision?.projectId ?? null,
1313
1318
  client,
1319
+ ...(incomplete
1320
+ ? {
1321
+ shippedKnowledgeWarning: 'The backend is not serving all of the knowledge this release ships. Tell the user before relying on the rules: some are missing from the running server, so a review against them is incomplete. The counts and the reason are in client.shippedKnowledge.',
1322
+ }
1323
+ : {}),
1314
1324
  nextAction: entryNextAction(authenticated, state),
1315
1325
  });
1316
1326
  });
@@ -2393,6 +2403,16 @@ export function newMemoryResourceCandidate(entry, policy) {
2393
2403
  ]).has(extension)) {
2394
2404
  return [];
2395
2405
  }
2406
+ const name = entry.path.toLowerCase().split('/').slice(-1)[0] ?? '';
2407
+ if (name.endsWith('.entity.ts')) {
2408
+ return [{ path: entry.path, kind: 'data_model' }];
2409
+ }
2410
+ if (name.endsWith('.controller.ts')) {
2411
+ return [{ path: entry.path, kind: 'api_endpoint' }];
2412
+ }
2413
+ if (name.endsWith('.module.ts') || name.endsWith('.service.ts')) {
2414
+ return [{ path: entry.path, kind: 'module_logic' }];
2415
+ }
2396
2416
  const segments = entry.path.toLowerCase().split('/').slice(0, -1);
2397
2417
  const screenIndex = segments.findIndex((segment) => ['screen', 'screens', 'view', 'views'].includes(segment));
2398
2418
  const componentIndex = segments.findIndex((segment) => ['widget', 'widgets', 'component', 'components'].includes(segment));
@@ -66,7 +66,7 @@ Send calls that do not feed each other in one batch rather than one at a time: r
66
66
 
67
67
  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.
68
68
 
69
- 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.
69
+ 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` immediately followed by a colon and 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.
70
70
 
71
71
  Do not write task Markdown files directly. The bridge owns event IDs, expected task versions, atomic projections, outbox state, and synchronization.
72
72