@thebackstoryis/engineering-with-ai 0.2.9 → 0.3.0-beta.2

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.
Files changed (37) hide show
  1. package/Docs/README.md +2 -0
  2. package/Docs/autonomous-intent-delivery.md +37 -0
  3. package/Docs/guide-catalogue.md +2 -0
  4. package/Docs/quality/persona-driven-test-scenarios.md +6 -0
  5. package/README.md +17 -0
  6. package/config/autonomy-worker.schema.json +70 -0
  7. package/config/autonomy.schema.json +25 -0
  8. package/package.json +3 -3
  9. package/public/app.js +7 -0
  10. package/public/autonomy.js +470 -0
  11. package/public/index.html +26 -0
  12. package/public/styles.css +64 -0
  13. package/skills-src/ewai-autonomy/SKILL.md +18 -0
  14. package/skills-src/ewai-autonomy/agents/openai.yaml +4 -0
  15. package/skills-src/ewai-deliver/SKILL.md +2 -0
  16. package/skills-src/ewai-test-scenarios/references/scenario-contract.md +6 -0
  17. package/src/autonomy-phase-contracts.mjs +88 -0
  18. package/src/autonomy-worker.mjs +15 -0
  19. package/src/autonomy.mjs +199 -0
  20. package/src/checkin.mjs +30 -6
  21. package/src/cli.mjs +67 -0
  22. package/src/delivery-gates.mjs +8 -1
  23. package/src/delivery.mjs +74 -3
  24. package/src/design-system-application.mjs +28 -0
  25. package/src/runtime/afk-conductor.mjs +191 -71
  26. package/src/runtime/autonomy-operations.mjs +250 -0
  27. package/src/runtime/autonomy-supervisor.mjs +426 -0
  28. package/src/runtime/autonomy-workers.mjs +333 -0
  29. package/src/runtime/autonomy-workspace.mjs +310 -0
  30. package/src/runtime/dashboard-actions.mjs +69 -0
  31. package/src/runtime/dashboard-server.mjs +25 -0
  32. package/src/runtime/database.mjs +25 -6
  33. package/src/runtime/intent-ownership.mjs +272 -0
  34. package/src/runtime/mcp-server.mjs +38 -0
  35. package/src/runtime/provider-adapters.mjs +365 -7
  36. package/src/runtime/work.mjs +21 -5
  37. package/src/test-scenarios.mjs +173 -20
package/src/delivery.mjs CHANGED
@@ -13,6 +13,7 @@ import YAML from 'yaml';
13
13
  import { updateIntentDeliveryState, validateIntentStateCopies } from './intents.mjs';
14
14
  import { loadProjectConfig } from './project.mjs';
15
15
  import { readRuntimeIntent } from './runtime/intents.mjs';
16
+ import { withIntentMutation, assertIntentMutation, intentOwnershipError } from './runtime/intent-ownership.mjs';
16
17
  import {
17
18
  addActivityEvent,
18
19
  finishActiveSession,
@@ -204,6 +205,7 @@ function lifecycleScope(state, phase = '') {
204
205
  }
205
206
 
206
207
  function publishDeliveryLifecycle(paths, state, eventName, input = {}) {
208
+ assertIntentMutation(paths.projectRoot, state.intent.id);
207
209
  let personas = state.intent.personas;
208
210
  if (!Array.isArray(personas)) {
209
211
  try {
@@ -287,11 +289,15 @@ function restoreTransition(paths, snapshot) {
287
289
  }
288
290
 
289
291
  function persistTransition(paths, state) {
292
+ assertIntentMutation(paths.projectRoot, state.intent.id);
290
293
  const snapshot = transitionSnapshot(paths, state);
291
294
  try {
295
+ assertIntentMutation(paths.projectRoot, state.intent.id);
292
296
  state.updatedAt = now();
293
297
  const copies = syncIntentCopies(paths, state);
298
+ assertIntentMutation(paths.projectRoot, state.intent.id);
294
299
  writeDeliveryDocuments(paths, state);
300
+ assertIntentMutation(paths.projectRoot, state.intent.id);
295
301
  const item = syncOperationalProjection(paths, state);
296
302
  validateIntentStateCopies(paths.projectRoot, copies.markdownPath, {
297
303
  status: state.intent.status,
@@ -305,6 +311,7 @@ function persistTransition(paths, state) {
305
311
  return item;
306
312
  } catch (error) {
307
313
  try {
314
+ assertIntentMutation(paths.projectRoot, state.intent.id);
308
315
  restoreTransition(paths, snapshot);
309
316
  } catch (rollbackError) {
310
317
  error.rollbackError = rollbackError.message;
@@ -347,6 +354,7 @@ function finishCurrentRun(paths, state, status, summary) {
347
354
  run.status = status;
348
355
  run.completedAt = now();
349
356
  run.summary = summary;
357
+ assertIntentMutation(paths.projectRoot, state.intent.id);
350
358
  atomicJson(runPath, run);
351
359
  finishCommandRun(paths.projectRoot, runId, {
352
360
  status,
@@ -483,12 +491,19 @@ export function readDeliveryState(projectRoot, slug) {
483
491
  }
484
492
 
485
493
  export function beginDelivery(projectRoot, slug, options = {}) {
494
+ const resolvedOptions = { ...options, tool: options.tool ?? process.env.EWAI_ORCHESTRATOR ?? 'manual' };
495
+ return withIntentMutation(projectRoot, slug, { action: 'begin-harness', provider: resolvedOptions.tool, input: resolvedOptions, ownership: options.ownership }, () => beginOwnedDelivery(projectRoot, slug, resolvedOptions));
496
+ }
497
+
498
+ function beginOwnedDelivery(projectRoot, slug, options) {
486
499
  const paths = deliveryPaths(projectRoot, slug);
487
- if (existsSync(paths.statePath)) throw new Error(`Delivery already exists for ${slug}; continue the existing work instead.`);
500
+ if (existsSync(paths.statePath)) intentOwnershipError('intent-transition-conflict');
488
501
  const { config } = loadProjectConfig(paths.projectRoot);
489
502
  const intent = readRuntimeIntent(paths.projectRoot, slug);
490
503
  if (!intent) throw new Error(`Cannot begin delivery without an intent: ${slug}`);
491
- const orchestrator = options.tool ?? process.env.EWAI_ORCHESTRATOR ?? 'manual';
504
+ const currentOwner = readWorkItemView(paths.projectRoot, intent.id)?.activity.session;
505
+ if (currentOwner && !currentOwner.endedAt) intentOwnershipError('intent-ownership-conflict');
506
+ const orchestrator = options.tool;
492
507
  const validation = effectiveValidationConfig(config, orchestrator);
493
508
  const providers = [...new Set(
494
509
  Object.values(validation.checkpoints).flatMap((checkpoint) => checkpoint.validators),
@@ -561,6 +576,7 @@ export function beginDelivery(projectRoot, slug, options = {}) {
561
576
  blockedReason: ''
562
577
  };
563
578
 
579
+ assertIntentMutation(paths.projectRoot, intent.id);
564
580
  mkdirSync(paths.runsRoot, { recursive: true });
565
581
  mkdirSync(paths.gatesRoot, { recursive: true });
566
582
  atomicJson(resolve(paths.runsRoot, `${run.id}.json`), run);
@@ -600,6 +616,10 @@ export function beginDelivery(projectRoot, slug, options = {}) {
600
616
  }
601
617
 
602
618
  export function recordPhaseGate(projectRoot, slug, phaseId, input = {}) {
619
+ return withIntentMutation(projectRoot, slug, { action: 'record-gate', phase: phaseId, input, ownership: input.ownership }, () => recordOwnedPhaseGate(projectRoot, slug, phaseId, input));
620
+ }
621
+
622
+ function recordOwnedPhaseGate(projectRoot, slug, phaseId, input) {
603
623
  const paths = deliveryPaths(projectRoot, slug);
604
624
  const state = readDeliveryState(projectRoot, slug);
605
625
  const phase = findTrackedPhase(state, phaseId);
@@ -609,6 +629,10 @@ export function recordPhaseGate(projectRoot, slug, phaseId, input = {}) {
609
629
  }
610
630
 
611
631
  export function recordExternalValidationCycle(projectRoot, slug, phaseId, input = {}) {
632
+ return withIntentMutation(projectRoot, slug, { action: 'record-validation', phase: phaseId, input, ownership: input.ownership }, () => recordOwnedExternalValidationCycle(projectRoot, slug, phaseId, input));
633
+ }
634
+
635
+ function recordOwnedExternalValidationCycle(projectRoot, slug, phaseId, input) {
612
636
  const paths = deliveryPaths(projectRoot, slug);
613
637
  const state = readDeliveryState(projectRoot, slug);
614
638
  const phase = findTrackedPhase(state, phaseId);
@@ -672,6 +696,7 @@ export function recordExternalValidationCycle(projectRoot, slug, phaseId, input
672
696
  );
673
697
 
674
698
  const ledgerPath = resolve(paths.deliveryRoot, phaseId, 'validation-cycles.json');
699
+ assertIntentMutation(paths.projectRoot, state.intent.id);
675
700
  atomicJson(ledgerPath, {
676
701
  schema: 'ewai.external-validation-cycles/v1',
677
702
  slug,
@@ -743,7 +768,11 @@ export function continueDelivery(projectRoot, slug) {
743
768
  };
744
769
  }
745
770
 
746
- export function startDeliveryPhase(projectRoot, slug, phaseId) {
771
+ export function startDeliveryPhase(projectRoot, slug, phaseId, input = {}) {
772
+ return withIntentMutation(projectRoot, slug, { action: 'start-phase', phase: phaseId, input, ownership: input.ownership }, () => startOwnedDeliveryPhase(projectRoot, slug, phaseId));
773
+ }
774
+
775
+ function startOwnedDeliveryPhase(projectRoot, slug, phaseId) {
747
776
  const paths = deliveryPaths(projectRoot, slug);
748
777
  const state = readDeliveryState(projectRoot, slug);
749
778
  assertIntentCopiesConsistent(paths, state);
@@ -788,6 +817,10 @@ export function startDeliveryPhase(projectRoot, slug, phaseId) {
788
817
  }
789
818
 
790
819
  export function completeDeliveryPhase(projectRoot, slug, phaseId, options = {}) {
820
+ return withIntentMutation(projectRoot, slug, { action: 'complete-phase', phase: phaseId, input: options, ownership: options.ownership }, () => completeOwnedDeliveryPhase(projectRoot, slug, phaseId, options));
821
+ }
822
+
823
+ function completeOwnedDeliveryPhase(projectRoot, slug, phaseId, options) {
791
824
  const paths = deliveryPaths(projectRoot, slug);
792
825
  const state = readDeliveryState(projectRoot, slug);
793
826
  const phase = findTrackedPhase(state, phaseId);
@@ -868,6 +901,10 @@ export function completeDeliveryPhase(projectRoot, slug, phaseId, options = {})
868
901
  }
869
902
 
870
903
  export function resumeShelvedDelivery(projectRoot, slug, input = {}) {
904
+ return withIntentMutation(projectRoot, slug, { action: 'resume-shelf', input, ownership: input.ownership }, () => resumeOwnedShelvedDelivery(projectRoot, slug, input));
905
+ }
906
+
907
+ function resumeOwnedShelvedDelivery(projectRoot, slug, input) {
871
908
  const paths = deliveryPaths(projectRoot, slug);
872
909
  const state = readDeliveryState(projectRoot, slug);
873
910
  assertIntentCopiesConsistent(paths, state);
@@ -894,6 +931,7 @@ export function resumeShelvedDelivery(projectRoot, slug, input = {}) {
894
931
  completedAt: null,
895
932
  summary: 'Shelf-ready delivery resumed through mandatory FitCheck.'
896
933
  };
934
+ assertIntentMutation(paths.projectRoot, state.intent.id);
897
935
  atomicJson(resolve(paths.runsRoot, `${run.id}.json`), run);
898
936
  startCommandRun(paths.projectRoot, state.intent.id, {
899
937
  runUuid: run.id,
@@ -916,6 +954,10 @@ export function resumeShelvedDelivery(projectRoot, slug, input = {}) {
916
954
  }
917
955
 
918
956
  export function ratifyDeliveryAmendments(projectRoot, slug, input = {}) {
957
+ return withIntentMutation(projectRoot, slug, { action: 'ratify-plan', input, ownership: input.ownership }, () => ratifyOwnedDeliveryAmendments(projectRoot, slug, input));
958
+ }
959
+
960
+ function ratifyOwnedDeliveryAmendments(projectRoot, slug, input) {
919
961
  const paths = deliveryPaths(projectRoot, slug);
920
962
  const state = readDeliveryState(projectRoot, slug);
921
963
  assertIntentCopiesConsistent(paths, state);
@@ -980,10 +1022,12 @@ export function ratifyDeliveryAmendments(projectRoot, slug, input = {}) {
980
1022
  },
981
1023
  ];
982
1024
 
1025
+ assertIntentMutation(paths.projectRoot, state.intent.id);
983
1026
  atomicJson(ratificationPath, ratification);
984
1027
  try {
985
1028
  persistTransition(paths, state);
986
1029
  } catch (error) {
1030
+ assertIntentMutation(paths.projectRoot, state.intent.id);
987
1031
  if (ratificationBefore.existed) atomicText(ratificationPath, ratificationBefore.content);
988
1032
  else rmSync(ratificationPath, { force: true });
989
1033
  throw error;
@@ -1197,6 +1241,17 @@ function replayCompletedEvidenceAmendment(paths, state, receiptPath, input) {
1197
1241
  }
1198
1242
 
1199
1243
  export function ratifyCompletedEvidenceAmendment(projectRoot, slug, input = {}) {
1244
+ try {
1245
+ return withIntentMutation(projectRoot, slug, { action: 'ratify-evidence', input, ownership: input.ownership }, () => ratifyOwnedCompletedEvidenceAmendment(projectRoot, slug, input));
1246
+ } catch (error) {
1247
+ if (['intent-mutation-conflict', 'intent-ownership-conflict'].includes(error.code)) {
1248
+ error.message = 'Another completed evidence amendment ratification is in progress. Preview again after it finishes.';
1249
+ }
1250
+ throw error;
1251
+ }
1252
+ }
1253
+
1254
+ function ratifyOwnedCompletedEvidenceAmendment(projectRoot, slug, input) {
1200
1255
  if (input.confirmed !== true) {
1201
1256
  throw new Error('Completed evidence amendment requires explicit confirmation.');
1202
1257
  }
@@ -1245,6 +1300,7 @@ function ratifyCompletedEvidenceAmendmentUnderLease(projectRoot, slug, paths, in
1245
1300
 
1246
1301
  const amendmentRoot = resolve(paths.deliveryRoot, 'evidence-amendments');
1247
1302
  const rootExisted = existsSync(amendmentRoot);
1303
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1248
1304
  completedEvidenceAmendmentRoot(paths, { create: true });
1249
1305
  const ledgerSnapshots = assessment.internalPhases.map(({ ledgerPath }) => fileSnapshot(ledgerPath));
1250
1306
  const receiptSnapshot = fileSnapshot(receiptPath);
@@ -1278,16 +1334,19 @@ function ratifyCompletedEvidenceAmendmentUnderLease(projectRoot, slug, paths, in
1278
1334
 
1279
1335
  try {
1280
1336
  for (const amendment of assessment.internalPhases) {
1337
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1281
1338
  atomicText(amendment.ledgerPath, amendment.candidateRaw.toString('utf8'));
1282
1339
  validatePhaseGateAtPaths(paths, amendment.phase, JSON.parse(readFileSync(amendment.ledgerPath, 'utf8')), {
1283
1340
  providers: [...state.phases, ...state.adjuncts]
1284
1341
  .find((phase) => phase.id === amendment.phase)?.validation?.providers ?? state.providers ?? [],
1285
1342
  });
1286
1343
  }
1344
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1287
1345
  atomicJson(receiptPath, receipt);
1288
1346
  input.afterAmendmentFilesWritten?.();
1289
1347
  persistTransition(paths, state);
1290
1348
  } catch (error) {
1349
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1291
1350
  for (const snapshot of ledgerSnapshots) {
1292
1351
  if (snapshot.existed) atomicText(snapshot.path, snapshot.content);
1293
1352
  else rmSync(snapshot.path, { force: true });
@@ -1307,6 +1366,10 @@ function ratifyCompletedEvidenceAmendmentUnderLease(projectRoot, slug, paths, in
1307
1366
  }
1308
1367
 
1309
1368
  export function recordBuildApproval(projectRoot, slug, input = {}) {
1369
+ return withIntentMutation(projectRoot, slug, { action: 'approve-build', input, ownership: input.ownership }, () => recordOwnedBuildApproval(projectRoot, slug, input));
1370
+ }
1371
+
1372
+ function recordOwnedBuildApproval(projectRoot, slug, input) {
1310
1373
  const paths = deliveryPaths(projectRoot, slug);
1311
1374
  const state = readDeliveryState(projectRoot, slug);
1312
1375
  assertIntentCopiesConsistent(paths, state);
@@ -1330,6 +1393,7 @@ export function recordBuildApproval(projectRoot, slug, input = {}) {
1330
1393
  };
1331
1394
  const approvalPath = resolve(paths.gatesRoot, 'build/build-approval.json');
1332
1395
  const approvalBefore = fileSnapshot(approvalPath);
1396
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1333
1397
  atomicJson(approvalPath, approval);
1334
1398
  state.approvals.build = {
1335
1399
  path: relative(paths.projectRoot, approvalPath).replaceAll('\\', '/'),
@@ -1341,6 +1405,7 @@ export function recordBuildApproval(projectRoot, slug, input = {}) {
1341
1405
  try {
1342
1406
  persistTransition(paths, state);
1343
1407
  } catch (error) {
1408
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1344
1409
  if (approvalBefore.existed) atomicText(approvalPath, approvalBefore.content);
1345
1410
  else rmSync(approvalPath, { force: true });
1346
1411
  throw error;
@@ -1363,6 +1428,10 @@ export function recordBuildApproval(projectRoot, slug, input = {}) {
1363
1428
  }
1364
1429
 
1365
1430
  export function recordManualQaApproval(projectRoot, slug, input = {}) {
1431
+ return withIntentMutation(projectRoot, slug, { action: 'approve-manual-qa', input, ownership: input.ownership }, () => recordOwnedManualQaApproval(projectRoot, slug, input));
1432
+ }
1433
+
1434
+ function recordOwnedManualQaApproval(projectRoot, slug, input) {
1366
1435
  const paths = deliveryPaths(projectRoot, slug);
1367
1436
  const state = readDeliveryState(projectRoot, slug);
1368
1437
  assertIntentCopiesConsistent(paths, state);
@@ -1386,6 +1455,7 @@ export function recordManualQaApproval(projectRoot, slug, input = {}) {
1386
1455
  };
1387
1456
  const approvalPath = resolve(paths.gatesRoot, 'manual-qa/approval.json');
1388
1457
  const approvalBefore = fileSnapshot(approvalPath);
1458
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1389
1459
  atomicJson(approvalPath, approval);
1390
1460
  const gate = state.humanGates.find((candidate) => candidate.id === 'manual-qa');
1391
1461
  gate.status = 'approved';
@@ -1398,6 +1468,7 @@ export function recordManualQaApproval(projectRoot, slug, input = {}) {
1398
1468
  try {
1399
1469
  persistTransition(paths, state);
1400
1470
  } catch (error) {
1471
+ assertIntentMutation(paths.projectRoot, state.intent.id);
1401
1472
  if (approvalBefore.existed) atomicText(approvalPath, approvalBefore.content);
1402
1473
  else rmSync(approvalPath, { force: true });
1403
1474
  throw error;
@@ -13,6 +13,9 @@ import { prepareContextPack } from './runtime/context-assembly.mjs';
13
13
 
14
14
  const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
15
  const sha256Pattern = /^sha256:[a-f0-9]{64}$/;
16
+ // Disposable acceleration only: every use still performs fresh pack validation.
17
+ // One small entry bounds retained source bodies across projects and revisions.
18
+ let recentResolution = null;
16
19
 
17
20
  function clean(value) {
18
21
  return String(value ?? '').trim();
@@ -66,12 +69,37 @@ function deliveryWorkspace(projectRoot, slug) {
66
69
  return { project, root, state };
67
70
  }
68
71
 
72
+ function resolutionIdentity(projectRoot, selection) {
73
+ const pin = ({ id, version, digest, sourceClass }) => ({ id, version, digest, sourceClass });
74
+ return canonical({ project: realpathSync(projectRoot), root: pin(selection.root),
75
+ packs: selection.packs.map(pin), effectiveDigest: selection.effectiveDigest });
76
+ }
77
+
78
+ function smallResolution(resolved) {
79
+ if (resolved.packs.length > 8) return false;
80
+ let bytes = 0, count = 0;
81
+ for (const pack of resolved.packs) {
82
+ for (const item of pack.design_system.contributions) {
83
+ bytes += Buffer.byteLength(item.content);
84
+ if (++count > 256 || bytes > 256 * 1024) return false;
85
+ }
86
+ }
87
+ return true;
88
+ }
89
+
69
90
  function resolvedSelection(projectRoot, options = {}) {
70
91
  const status = designSystemStatus(projectRoot, options);
71
92
  if (status.status !== 'ready') throw new Error(`Project design system is ${status.status}; re-resolve and select it before application`);
93
+ const identity = resolutionIdentity(projectRoot, status);
94
+ if (recentResolution?.identity === identity) return { status, resolved: recentResolution.resolved };
95
+ recentResolution = null;
72
96
  const { config } = loadProjectConfig(projectRoot);
73
97
  const catalogue = listDesignSystems({ ...options, projectRoot });
74
98
  const resolved = resolveDesignSystem(config.design_system?.root?.id ?? DEFAULT_DESIGN_SYSTEM_ID, catalogue);
99
+ // A cold resolution must describe the exact source that passed validation,
100
+ // including provenance; otherwise a changed pack could inherit old approval.
101
+ if (resolutionIdentity(projectRoot, resolved) !== identity) throw new Error('Design-system source changed during preparation; re-resolve before application');
102
+ if (smallResolution(resolved)) recentResolution = { identity, resolved };
75
103
  return { status, resolved };
76
104
  }
77
105