engineering-memory 0.4.0 → 1.0.1
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": "1.0.1",
|
|
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()) {
|
|
@@ -303,7 +304,7 @@ export class GitInspector {
|
|
|
303
304
|
return [...found.values()].sort((left, right) => left.path === right.path ? left.line - right.line : left.path.localeCompare(right.path));
|
|
304
305
|
}
|
|
305
306
|
async markerMatches(root, scope, origin) {
|
|
306
|
-
const result = await this.runner.run('git', ['grep', '--no-color', '-n', '-I', '-F', ...scope, '-e',
|
|
307
|
+
const result = await this.runner.run('git', ['grep', '--no-color', '-n', '-I', '-F', ...scope, '-e', temporaryScaffoldingPattern], { cwd: root });
|
|
307
308
|
if (result.exitCode !== 0)
|
|
308
309
|
return [];
|
|
309
310
|
return result.stdout
|
|
@@ -380,8 +380,22 @@ export function registerEngineeringMemoryTools(server, service) {
|
|
|
380
380
|
initialProjectProfile: z.object({
|
|
381
381
|
title: z.string().min(2),
|
|
382
382
|
content: z.string().min(2),
|
|
383
|
-
|
|
384
|
-
|
|
383
|
+
discoveryUnits: z
|
|
384
|
+
.array(z.object({
|
|
385
|
+
kind: z.enum([
|
|
386
|
+
'screen_logic',
|
|
387
|
+
'component_mapping',
|
|
388
|
+
'module_logic',
|
|
389
|
+
'data_model',
|
|
390
|
+
'api_endpoint',
|
|
391
|
+
]),
|
|
392
|
+
patterns: z.array(z.string().min(1)).min(1),
|
|
393
|
+
}))
|
|
394
|
+
.min(1)
|
|
395
|
+
.optional()
|
|
396
|
+
.describe('What this project calls its units and where they live. A Flutter project declares screen_logic and component_mapping; a backend declares module_logic, data_model and api_endpoint.'),
|
|
397
|
+
screenPathPatterns: z.array(z.string().min(1)).min(1).optional(),
|
|
398
|
+
componentPathPatterns: z.array(z.string().min(1)).min(1).optional(),
|
|
385
399
|
metadata: jsonObject,
|
|
386
400
|
provenance: jsonObject,
|
|
387
401
|
}),
|
|
@@ -1129,8 +1129,7 @@ export class BridgeService {
|
|
|
1129
1129
|
!policy ||
|
|
1130
1130
|
!marker ||
|
|
1131
1131
|
marker.projectId !== project.id ||
|
|
1132
|
-
|
|
1133
|
-
stringArray(policy.componentPathPatterns).length === 0) {
|
|
1132
|
+
readDiscoveryUnits(policy).length === 0) {
|
|
1134
1133
|
throw new Error('Project setup response is not policy-ready');
|
|
1135
1134
|
}
|
|
1136
1135
|
const markerPath = await this.dependencies.repositories.writeMarker(repository.repoRoot, project.id);
|
|
@@ -2369,18 +2368,11 @@ export function newMemoryResourceCandidate(entry, policy) {
|
|
|
2369
2368
|
return [];
|
|
2370
2369
|
}
|
|
2371
2370
|
if (policy) {
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
if (screen && component) {
|
|
2371
|
+
const matched = policy.units.filter((unit) => unit.patterns.some((pattern) => globMatches(entry.path, pattern)));
|
|
2372
|
+
if (matched.length > 1) {
|
|
2375
2373
|
throw new Error(`Resource discovery policy is ambiguous for path: ${entry.path}`);
|
|
2376
2374
|
}
|
|
2377
|
-
|
|
2378
|
-
return [{ path: entry.path, kind: 'screen_logic' }];
|
|
2379
|
-
}
|
|
2380
|
-
if (component) {
|
|
2381
|
-
return [{ path: entry.path, kind: 'component_mapping' }];
|
|
2382
|
-
}
|
|
2383
|
-
return [];
|
|
2375
|
+
return matched[0] ? [{ path: entry.path, kind: matched[0].kind }] : [];
|
|
2384
2376
|
}
|
|
2385
2377
|
const extension = entry.path.toLowerCase().match(/\.[^.\/]+$/)?.[0];
|
|
2386
2378
|
if (!extension ||
|
|
@@ -2420,12 +2412,32 @@ function readResourceDiscoveryPolicy(snapshot, activeLease) {
|
|
|
2420
2412
|
if (!discovery) {
|
|
2421
2413
|
return null;
|
|
2422
2414
|
}
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
if (screenPathPatterns.length === 0 || componentPathPatterns.length === 0) {
|
|
2415
|
+
const units = readDiscoveryUnits(discovery);
|
|
2416
|
+
if (units.length === 0) {
|
|
2426
2417
|
throw new Error('Pinned resource discovery policy is incomplete');
|
|
2427
2418
|
}
|
|
2428
|
-
return {
|
|
2419
|
+
return { units };
|
|
2420
|
+
}
|
|
2421
|
+
function readDiscoveryUnits(discovery) {
|
|
2422
|
+
if (!discovery)
|
|
2423
|
+
return [];
|
|
2424
|
+
const declared = Array.isArray(discovery.units) ? discovery.units : null;
|
|
2425
|
+
if (declared) {
|
|
2426
|
+
return declared.flatMap((entry) => {
|
|
2427
|
+
const unit = objectValue(entry);
|
|
2428
|
+
const patterns = stringArray(unit?.patterns);
|
|
2429
|
+
return typeof unit?.kind === 'string' && patterns.length > 0
|
|
2430
|
+
? [{ kind: unit.kind, patterns }]
|
|
2431
|
+
: [];
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
return [
|
|
2435
|
+
{ kind: 'screen_logic', patterns: stringArray(discovery.screenPathPatterns) },
|
|
2436
|
+
{
|
|
2437
|
+
kind: 'component_mapping',
|
|
2438
|
+
patterns: stringArray(discovery.componentPathPatterns),
|
|
2439
|
+
},
|
|
2440
|
+
].filter((unit) => unit.patterns.length > 0);
|
|
2429
2441
|
}
|
|
2430
2442
|
function stringArray(value) {
|
|
2431
2443
|
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string')) {
|
|
@@ -58,13 +58,15 @@ target inferred only from surrounding code. Carry both node ids into the discove
|
|
|
58
58
|
checkpoint as evidence. A defect in a calculation, a response shape or a service has no
|
|
59
59
|
answer in the design; do not spend the read there.
|
|
60
60
|
|
|
61
|
+
Name the areas this task works in, against the coverage map the core pack carries. Where an area it touches is marked as having no rule, that is not a reason to stop — it is a finding to raise at self review, so the absence is reported by the product rather than found later by whoever inherits the code.
|
|
62
|
+
|
|
61
63
|
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.
|
|
62
64
|
|
|
63
65
|
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.
|
|
64
66
|
|
|
65
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.
|
|
66
68
|
|
|
67
|
-
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`
|
|
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.
|
|
68
70
|
|
|
69
71
|
Do not write task Markdown files directly. The bridge owns event IDs, expected task versions, atomic projections, outbox state, and synchronization.
|
|
70
72
|
|
|
@@ -106,7 +108,9 @@ Before validation, read the changed code back against the rules that govern it.
|
|
|
106
108
|
|
|
107
109
|
Record `task.self_review` naming the resources reviewed and, for every conflict found, the file, the rule, what was wrong and what was done about it. A review that found nothing records an empty finding list, which is a claim about the work rather than a formality.
|
|
108
110
|
|
|
109
|
-
|
|
111
|
+
An area the task worked in that the coverage map marks as having no rule is recorded as a finding too, naming the area and what was decided in its absence. That is how the product learns which rule to write next: an area nothing has needed yet can wait, and one a real task just had to improvise in cannot.
|
|
112
|
+
|
|
113
|
+
This applies to a write task. A read-only task changed nothing, so there is nothing to read back against the rules and `task.self_review` is refused for it; go straight to verification. `task.verify` refuses a write task without a self review of the current diff. Editing after the review invalidates it, which is the point: the last thing that happens to the code is that someone read it against the rules. This exists because a task once shipped code that broke rules it had been given — the rules were present and correct, and nothing in the lifecycle ever asked whether the result matched them.
|
|
110
114
|
|
|
111
115
|
The review is the agent's own job at the end of the work. Do not wait to be asked for it, and do not treat a passing test suite as a substitute: tests leave a receipt and readability does not, which is exactly why the unreviewed one is the one that degrades.
|
|
112
116
|
|