@thebackstoryis/engineering-with-ai 0.2.9
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/Docs/README.md +50 -0
- package/Docs/adoption/consultancy-and-multi-project-rollout.md +135 -0
- package/Docs/adoption/non-technical-team-guide.md +126 -0
- package/Docs/archaeology-technology-and-hosting-discovery.md +212 -0
- package/Docs/blast-radius-and-impact-routing-guide.md +325 -0
- package/Docs/blueprints/internal-blueprint-catalogue.md +146 -0
- package/Docs/blueprints/maintaining-organisation-blueprints.md +154 -0
- package/Docs/blueprints/validation-and-troubleshooting.md +168 -0
- package/Docs/cli-reference.md +113 -0
- package/Docs/completed-phase-evidence-amendments.md +74 -0
- package/Docs/consultancy-network-rollout-control-plane-guide.md +202 -0
- package/Docs/context-aware-delivery-companion-guide.md +198 -0
- package/Docs/context-aware-delivery-companion-user-guide.md +184 -0
- package/Docs/context-management-and-token-efficiency.md +113 -0
- package/Docs/design-systems/design-system-implementation-guide.md +85 -0
- package/Docs/design-systems/design-system-pack-authoring-guide.md +95 -0
- package/Docs/design-systems/design-system-review-guide.md +51 -0
- package/Docs/design-systems/design-system-user-guide.md +96 -0
- package/Docs/design-systems/product-owner-guide.md +49 -0
- package/Docs/designing-organisation-blueprint-packs.md +384 -0
- package/Docs/developer-delivery-guide.md +224 -0
- package/Docs/error-reporting-guide.md +110 -0
- package/Docs/error-reporting-provider-guide.md +49 -0
- package/Docs/examples/error-report-adapter.md +70 -0
- package/Docs/examples/meeting-review.md +76 -0
- package/Docs/examples/minimal-design-system.md +67 -0
- package/Docs/examples/prototype-review-inputs.md +175 -0
- package/Docs/examples/reproducible-archaeology-depth-example.md +144 -0
- package/Docs/examples/test-scenario-input.md +68 -0
- package/Docs/examples/worked-examples.md +147 -0
- package/Docs/existing-project-onboarding-guide.md +214 -0
- package/Docs/explanation/core-concepts.md +26 -0
- package/Docs/explanation/delivery-workflow.md +48 -0
- package/Docs/governance/governance-team-guide.md +139 -0
- package/Docs/governed-starter-project-materialisation-guide.md +284 -0
- package/Docs/guide-catalogue.md +117 -0
- package/Docs/guided-discovery-facilitator-guide.md +172 -0
- package/Docs/guided-intent-workspace-guide.md +109 -0
- package/Docs/guided-phase-evidence-drafting-guide.md +119 -0
- package/Docs/human-approval-and-assurance-guide.md +146 -0
- package/Docs/knowledge-proposals-implementer-guide.md +106 -0
- package/Docs/knowledge-proposals-user-guide.md +247 -0
- package/Docs/maintainers/context-benchmarks.md +29 -0
- package/Docs/maintainers/contributing.md +58 -0
- package/Docs/maintainers/evidence-depth-acceptance.md +72 -0
- package/Docs/maintainers/verification-walkthroughs.md +104 -0
- package/Docs/meeting-evidence-implementer-guide.md +132 -0
- package/Docs/meeting-evidence-user-guide.md +200 -0
- package/Docs/operations/dashboard-and-delivery-state.md +153 -0
- package/Docs/operations/dashboard-configuration.md +87 -0
- package/Docs/operations/installation-updating-and-entitlements.md +135 -0
- package/Docs/operations/premium-personas-setup.md +76 -0
- package/Docs/operations/troubleshooting-and-recovery.md +205 -0
- package/Docs/organisation-rollout-guide.md +142 -0
- package/Docs/persona-entitlement-provider-guide.md +199 -0
- package/Docs/persona-guided-prototype-iteration.md +129 -0
- package/Docs/personas/organisation-specific-personas.md +103 -0
- package/Docs/personas/persona-authoring-cookbook.md +176 -0
- package/Docs/personas/persona-engagement-ui.md +133 -0
- package/Docs/personas/persona-governance.md +118 -0
- package/Docs/platform-export-analysis-guide.md +336 -0
- package/Docs/policies/governance-owner-guide.md +36 -0
- package/Docs/policies/implementation-guide.md +42 -0
- package/Docs/policies/organisation-policy-design-gates.md +58 -0
- package/Docs/policies/policy-pack-authoring-guide.md +108 -0
- package/Docs/policies/product-owner-guide.md +43 -0
- package/Docs/policies/technical-owner-guide.md +37 -0
- package/Docs/product-owner-guide.md +327 -0
- package/Docs/project-portfolio-orchestration-guide.md +199 -0
- package/Docs/quality/manual-qa-and-acceptance.md +162 -0
- package/Docs/quality/persona-driven-test-scenarios.md +172 -0
- package/Docs/quality/reproducible-archaeology-depth-review-checklist.md +89 -0
- package/Docs/reference/capabilities-and-project-layout.md +678 -0
- package/Docs/reference/cli-and-configuration.md +398 -0
- package/Docs/reference/contributions-api.md +23 -0
- package/Docs/reference/security-adapter-authoring.md +81 -0
- package/Docs/reference/starter-adapter-authoring.md +74 -0
- package/Docs/repository-source-map-guide.md +381 -0
- package/Docs/reproducible-archaeology-and-discovery-depth.md +292 -0
- package/Docs/screen-prototype-creation-guide.md +324 -0
- package/Docs/security-validation-guide.md +353 -0
- package/Docs/solution-readiness-review-guide.md +123 -0
- package/Docs/standards/project-standards-authoring.md +157 -0
- package/Docs/team-hub-guide.md +162 -0
- package/Docs/team-hub-resource-registry-guide.md +167 -0
- package/Docs/tutorials/first-delivery.md +83 -0
- package/Docs/tutorials/first-session.md +62 -0
- package/Docs/using-lifecycle-hooks.md +381 -0
- package/Docs/working-with-personas.md +274 -0
- package/LICENSE +165 -0
- package/README.md +96 -0
- package/agents-src/claude/ewai-security-reviewer.md +15 -0
- package/bin/ewai +5 -0
- package/config/archaeology-record-families.yaml +59 -0
- package/config/delivery-artifacts.yaml +121 -0
- package/config/delivery-stages.yaml +77 -0
- package/config/design-system.schema.json +46 -0
- package/config/error-reporting.schema.json +79 -0
- package/config/evidence-depth.schema.json +53 -0
- package/config/intent.schema.json +90 -0
- package/config/knowledge-proposals-proposal.schema.json +34 -0
- package/config/lifecycle-event.schema.json +68 -0
- package/config/lifecycle-handler.schema.json +45 -0
- package/config/lifecycle-hook-ack.schema.json +19 -0
- package/config/meeting-evidence-candidate.schema.json +102 -0
- package/config/organisation-policy.schema.json +137 -0
- package/config/pack.schema.json +250 -0
- package/config/persona-pack.schema.json +21 -0
- package/config/persona.schema.json +17 -0
- package/config/policy-evaluation.schema.json +77 -0
- package/config/policy-facts.schema.json +140 -0
- package/config/portfolio.schema.json +68 -0
- package/config/project.schema.json +313 -0
- package/config/prototype-iteration.schema.json +128 -0
- package/config/rollout.schema.json +87 -0
- package/config/security-adapter.schema.json +31 -0
- package/config/security-scan-request.schema.json +64 -0
- package/config/security-scan-response.schema.json +52 -0
- package/config/security-validation-policy.schema.json +74 -0
- package/config/starter-source-acknowledgement.schema.json +13 -0
- package/config/starter-source-adapter.schema.json +38 -0
- package/config/starter-source-request.schema.json +61 -0
- package/package.json +77 -0
- package/packs/core/pack.yaml +7 -0
- package/packs/design-systems/default/experience-promise.md +9 -0
- package/packs/design-systems/default/intentional-review.md +10 -0
- package/packs/design-systems/default/interaction-and-entry.md +9 -0
- package/packs/design-systems/default/meaningful-content-and-states.md +9 -0
- package/packs/design-systems/default/pack.yaml +44 -0
- package/packs/design-systems/default/principles.md +10 -0
- package/packs/personas/core/pack.yaml +7 -0
- package/packs/personas/core/personas/archaeologist.md +37 -0
- package/packs/personas/core/personas/end-user.md +17 -0
- package/packs/personas/core/personas/maintainer.md +17 -0
- package/packs/personas/core/personas/operator.md +17 -0
- package/packs/personas/core/personas/specs-knowledge-curator.md +35 -0
- package/packs/technologies/laravel/pack.yaml +30 -0
- package/packs/technologies/laravel-nuxt/pack.yaml +30 -0
- package/packs/technologies/nuxt/pack.yaml +30 -0
- package/packs/technologies/power-platform/pack.yaml +31 -0
- package/packs/technologies/salesforce/pack.yaml +25 -0
- package/public/app.js +4896 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/backstory-icon.png +0 -0
- package/public/dashboard-navigation.js +98 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +789 -0
- package/public/styles.css +2693 -0
- package/public/team-hub/app.js +202 -0
- package/public/team-hub/index.html +79 -0
- package/public/team-hub/styles.css +90 -0
- package/scripts/publication-check.mjs +140 -0
- package/scripts/setup.mjs +21 -0
- package/skills-src/ewai-archaeology/SKILL.md +334 -0
- package/skills-src/ewai-archaeology/agents/openai.yaml +4 -0
- package/skills-src/ewai-archaeology/references/archaeology-contract.md +201 -0
- package/skills-src/ewai-archaeology/references/lifecycle-reconstruction.md +177 -0
- package/skills-src/ewai-archaeology/references/maximum-detail-reconstruction.md +97 -0
- package/skills-src/ewai-archaeology/references/model-routing.md +26 -0
- package/skills-src/ewai-architecture/SKILL.md +108 -0
- package/skills-src/ewai-architecture/agents/openai.yaml +4 -0
- package/skills-src/ewai-architecture/references/architecture-contract.md +176 -0
- package/skills-src/ewai-context/SKILL.md +68 -0
- package/skills-src/ewai-context/agents/openai.yaml +4 -0
- package/skills-src/ewai-context-import/SKILL.md +118 -0
- package/skills-src/ewai-context-import/agents/openai.yaml +4 -0
- package/skills-src/ewai-context-import/references/context-import-contract.md +106 -0
- package/skills-src/ewai-dashboard-configuration/SKILL.md +20 -0
- package/skills-src/ewai-deliver/SKILL.md +122 -0
- package/skills-src/ewai-deliver/references/delivery-evidence.md +92 -0
- package/skills-src/ewai-deliver/references/phase-routing.md +31 -0
- package/skills-src/ewai-design-system-apply/SKILL.md +27 -0
- package/skills-src/ewai-design-system-apply/agents/openai.yaml +4 -0
- package/skills-src/ewai-design-system-apply/references/application-contract.md +36 -0
- package/skills-src/ewai-design-system-author/SKILL.md +28 -0
- package/skills-src/ewai-design-system-author/agents/openai.yaml +4 -0
- package/skills-src/ewai-design-system-author/references/authoring-contract.md +38 -0
- package/skills-src/ewai-design-system-review/SKILL.md +26 -0
- package/skills-src/ewai-design-system-review/agents/openai.yaml +4 -0
- package/skills-src/ewai-design-system-review/references/review-contract.md +40 -0
- package/skills-src/ewai-error-reporting/SKILL.md +46 -0
- package/skills-src/ewai-error-reporting/agents/openai.yaml +4 -0
- package/skills-src/ewai-error-reporting/references/provider-contract.md +74 -0
- package/skills-src/ewai-evidence-depth/SKILL.md +72 -0
- package/skills-src/ewai-evidence-depth/agents/openai.yaml +4 -0
- package/skills-src/ewai-evidence-depth/references/evidence-depth-contract.md +127 -0
- package/skills-src/ewai-intent/SKILL.md +68 -0
- package/skills-src/ewai-intent/agents/openai.yaml +4 -0
- package/skills-src/ewai-intent/references/intent-contract.md +42 -0
- package/skills-src/ewai-knowledge-proposals/SKILL.md +105 -0
- package/skills-src/ewai-knowledge-proposals/agents/openai.yaml +4 -0
- package/skills-src/ewai-knowledge-proposals/references/proposal-contract.md +59 -0
- package/skills-src/ewai-meeting-evidence/SKILL.md +106 -0
- package/skills-src/ewai-meeting-evidence/agents/openai.yaml +4 -0
- package/skills-src/ewai-meeting-evidence/references/candidate-contract.md +64 -0
- package/skills-src/ewai-organisation-policy/SKILL.md +62 -0
- package/skills-src/ewai-organisation-policy/agents/openai.yaml +4 -0
- package/skills-src/ewai-organisation-policy/references/policy-contract.md +94 -0
- package/skills-src/ewai-palace-housekeeping/SKILL.md +55 -0
- package/skills-src/ewai-palace-housekeeping/agents/openai.yaml +4 -0
- package/skills-src/ewai-persona-entitlement/SKILL.md +60 -0
- package/skills-src/ewai-persona-entitlement/agents/openai.yaml +4 -0
- package/skills-src/ewai-phase-evidence/SKILL.md +79 -0
- package/skills-src/ewai-phase-evidence/agents/openai.yaml +4 -0
- package/skills-src/ewai-pipeline/SKILL.md +130 -0
- package/skills-src/ewai-pipeline/agents/openai.yaml +4 -0
- package/skills-src/ewai-pipeline/references/cli.md +86 -0
- package/skills-src/ewai-pipeline/references/specs-contract.md +16 -0
- package/skills-src/ewai-portfolio/SKILL.md +70 -0
- package/skills-src/ewai-portfolio/agents/openai.yaml +4 -0
- package/skills-src/ewai-portfolio/references/portfolio-contract.md +67 -0
- package/skills-src/ewai-project-discovery/SKILL.md +95 -0
- package/skills-src/ewai-project-discovery/agents/openai.yaml +4 -0
- package/skills-src/ewai-project-discovery/references/discovery-contract.md +34 -0
- package/skills-src/ewai-prototype-iteration/SKILL.md +30 -0
- package/skills-src/ewai-prototype-iteration/agents/openai.yaml +4 -0
- package/skills-src/ewai-prototype-iteration/references/review-contract.md +49 -0
- package/skills-src/ewai-retro/SKILL.md +48 -0
- package/skills-src/ewai-retro/agents/openai.yaml +4 -0
- package/skills-src/ewai-retro/references/asset-routing.md +14 -0
- package/skills-src/ewai-rollout/SKILL.md +74 -0
- package/skills-src/ewai-rollout/agents/openai.yaml +4 -0
- package/skills-src/ewai-rollout/references/rollout-contract.md +74 -0
- package/skills-src/ewai-shape-intents/SKILL.md +84 -0
- package/skills-src/ewai-shape-intents/agents/openai.yaml +4 -0
- package/skills-src/ewai-shape-intents/references/intent-mapping-contract.md +109 -0
- package/skills-src/ewai-solution-readiness/SKILL.md +55 -0
- package/skills-src/ewai-solution-readiness/agents/openai.yaml +4 -0
- package/skills-src/ewai-standards-check/SKILL.md +93 -0
- package/skills-src/ewai-standards-check/agents/openai.yaml +4 -0
- package/skills-src/ewai-standards-check/references/report-contract.md +116 -0
- package/skills-src/ewai-test-scenarios/SKILL.md +94 -0
- package/skills-src/ewai-test-scenarios/agents/openai.yaml +4 -0
- package/skills-src/ewai-test-scenarios/references/scenario-contract.md +88 -0
- package/src/afk-worker.mjs +16 -0
- package/src/archaeology.mjs +1333 -0
- package/src/checkin.mjs +261 -0
- package/src/cli.mjs +2427 -0
- package/src/companion-guidance.mjs +257 -0
- package/src/companion-opening.mjs +62 -0
- package/src/companion.mjs +256 -0
- package/src/context.mjs +210 -0
- package/src/dashboard-preferences.mjs +80 -0
- package/src/delivery-artifacts.mjs +204 -0
- package/src/delivery-documents.mjs +248 -0
- package/src/delivery-gates.mjs +317 -0
- package/src/delivery.mjs +1433 -0
- package/src/design-system-application.mjs +291 -0
- package/src/design-system-authoring.mjs +101 -0
- package/src/design-systems.mjs +466 -0
- package/src/discovery.mjs +1314 -0
- package/src/error-reporting.mjs +323 -0
- package/src/evidence-depth.mjs +543 -0
- package/src/execution-state.mjs +243 -0
- package/src/install.mjs +166 -0
- package/src/intent-dependencies.mjs +117 -0
- package/src/intent-maps.mjs +402 -0
- package/src/intents.mjs +747 -0
- package/src/knowledge-proposals.mjs +717 -0
- package/src/launcher.mjs +51 -0
- package/src/meeting-evidence.mjs +703 -0
- package/src/network-rollout.mjs +386 -0
- package/src/organisation-blueprints.mjs +438 -0
- package/src/organisation-policies.mjs +448 -0
- package/src/packs.mjs +44 -0
- package/src/paths.mjs +62 -0
- package/src/persona-entitlements.mjs +438 -0
- package/src/persona-licence-config.mjs +98 -0
- package/src/persona-website-provider.mjs +134 -0
- package/src/persona-zip.mjs +87 -0
- package/src/personas.mjs +159 -0
- package/src/platform-metadata-analysis.mjs +314 -0
- package/src/policy-design-gates.mjs +623 -0
- package/src/policy-gate-integration.mjs +318 -0
- package/src/portfolio.mjs +509 -0
- package/src/power-platform-source-map.mjs +190 -0
- package/src/project.mjs +449 -0
- package/src/prototype-iterations.mjs +730 -0
- package/src/repository-source-map.mjs +603 -0
- package/src/runtime/afk-conductor.mjs +973 -0
- package/src/runtime/context-assembly.mjs +457 -0
- package/src/runtime/context-benchmarks.mjs +115 -0
- package/src/runtime/dashboard-actions.mjs +109 -0
- package/src/runtime/dashboard-handoffs.mjs +149 -0
- package/src/runtime/dashboard-server.mjs +1272 -0
- package/src/runtime/dashboard.mjs +197 -0
- package/src/runtime/database.mjs +789 -0
- package/src/runtime/error-reporting.mjs +581 -0
- package/src/runtime/evidence-depth-workspace.mjs +412 -0
- package/src/runtime/execution-leases.mjs +299 -0
- package/src/runtime/guided-discovery.mjs +350 -0
- package/src/runtime/guided-intents.mjs +517 -0
- package/src/runtime/impact-analysis.mjs +535 -0
- package/src/runtime/intents.mjs +222 -0
- package/src/runtime/knowledge.mjs +86 -0
- package/src/runtime/lifecycle-hooks.mjs +1239 -0
- package/src/runtime/mcp-config.mjs +110 -0
- package/src/runtime/mcp-server.mjs +1885 -0
- package/src/runtime/palace.mjs +362 -0
- package/src/runtime/paths.mjs +58 -0
- package/src/runtime/persona-engagement.mjs +255 -0
- package/src/runtime/phase-contributions.mjs +594 -0
- package/src/runtime/policy-workspace.mjs +170 -0
- package/src/runtime/prototype-iterations.mjs +235 -0
- package/src/runtime/provider-adapters.mjs +163 -0
- package/src/runtime/repository-index.mjs +838 -0
- package/src/runtime/runs.mjs +185 -0
- package/src/runtime/security-validation.mjs +1230 -0
- package/src/runtime/starter-materialisation.mjs +1155 -0
- package/src/runtime/team-hub-client.mjs +479 -0
- package/src/runtime/team-hub-database.mjs +288 -0
- package/src/runtime/team-hub-server.mjs +191 -0
- package/src/runtime/team-hub.mjs +110 -0
- package/src/runtime/tree-sitter-index.mjs +390 -0
- package/src/runtime/version.mjs +1 -0
- package/src/runtime/work.mjs +633 -0
- package/src/salesforce-source-map.mjs +212 -0
- package/src/security-validation-config.mjs +224 -0
- package/src/solution-readiness.mjs +620 -0
- package/src/starter-materialisation-contract.mjs +407 -0
- package/src/task-graph.mjs +544 -0
- package/src/team-hub-resources.mjs +239 -0
- package/src/team-hub.mjs +242 -0
- package/src/test-scenarios.mjs +622 -0
- package/src/validation-config.mjs +289 -0
- package/templates/SPECS/1.Scope/personas/registry.yaml +12 -0
- package/templates/SPECS/5.Strategy/patterns/context-packet.md +119 -0
- package/templates/SPECS/6.Build/_tracker-template.md +16 -0
- package/templates/SPECS/pipeline.yaml +62 -0
- package/templates/discovery-answers.yaml +86 -0
- package/templates/intent-body.md +21 -0
- package/tests/fixtures/context-benchmarks.json +9 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
recordBuildApproval,
|
|
3
|
+
startDeliveryPhase,
|
|
4
|
+
} from '../delivery.mjs';
|
|
5
|
+
import { readRuntimeIntent } from './intents.mjs';
|
|
6
|
+
import { readWorkItemView } from './work.mjs';
|
|
7
|
+
import {
|
|
8
|
+
afkRunStatus,
|
|
9
|
+
cancelAfkRun,
|
|
10
|
+
pauseAfkRun,
|
|
11
|
+
preflightAfkRun,
|
|
12
|
+
resumeAfkRun,
|
|
13
|
+
startAfkRun,
|
|
14
|
+
} from './afk-conductor.mjs';
|
|
15
|
+
import { queueDashboardHandoff } from './dashboard-handoffs.mjs';
|
|
16
|
+
|
|
17
|
+
function requireConfirmation(input, action) {
|
|
18
|
+
if (input?.confirmed !== true) throw new Error(`${action} requires explicit confirmation.`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function context(projectRoot, reference) {
|
|
22
|
+
const intent = readRuntimeIntent(projectRoot, reference);
|
|
23
|
+
if (!intent) throw new Error(`Unknown intent: ${reference}`);
|
|
24
|
+
const view = readWorkItemView(projectRoot, intent.id);
|
|
25
|
+
if (!view) throw new Error(`No work item is available for intent: ${reference}`);
|
|
26
|
+
return { intent, view };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function requireAction(view, actionName) {
|
|
30
|
+
const action = view.item.execution?.actions?.[actionName];
|
|
31
|
+
if (!action?.permitted) {
|
|
32
|
+
const reason = action?.blockers?.map((item) => item.message).join(' ') || 'The evidence-derived action is not available.';
|
|
33
|
+
throw new Error(reason);
|
|
34
|
+
}
|
|
35
|
+
return action;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function requestGuidedDashboardWork(projectRoot, reference, input = {}) {
|
|
39
|
+
requireConfirmation(input, 'Guided EWAI work');
|
|
40
|
+
const requested = String(input.action ?? '').trim();
|
|
41
|
+
const actionName = requested === 'begin' ? 'beginHarness' : requested === 'continue' ? 'continueHarness' : '';
|
|
42
|
+
if (!actionName) throw new Error(`Unsupported guided action: ${requested || '<missing>'}`);
|
|
43
|
+
const { intent, view } = context(projectRoot, reference);
|
|
44
|
+
const action = requireAction(view, actionName);
|
|
45
|
+
return queueDashboardHandoff(projectRoot, {
|
|
46
|
+
intentId: intent.id,
|
|
47
|
+
slug: intent.slug,
|
|
48
|
+
title: intent.title,
|
|
49
|
+
action: requested,
|
|
50
|
+
phase: action.nextPhase ?? view.item.currentPhase ?? (requested === 'begin' ? 'intent' : ''),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function approveBuildFromDashboard(projectRoot, reference, input = {}) {
|
|
55
|
+
requireConfirmation(input, 'Build approval');
|
|
56
|
+
const approvedBy = String(input.approvedBy ?? '').trim();
|
|
57
|
+
if (!approvedBy) throw new Error('Build approval requires the approving person’s name.');
|
|
58
|
+
const { intent, view } = context(projectRoot, reference);
|
|
59
|
+
requireAction(view, 'approveBuild');
|
|
60
|
+
const result = recordBuildApproval(projectRoot, intent.slug, {
|
|
61
|
+
decision: 'approved',
|
|
62
|
+
approvedBy,
|
|
63
|
+
scope: String(input.scope ?? '').trim(),
|
|
64
|
+
});
|
|
65
|
+
return { approval: result, view: readWorkItemView(projectRoot, intent.id) };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function enterBuildFromDashboard(projectRoot, reference, input = {}) {
|
|
69
|
+
requireConfirmation(input, 'Entering Build');
|
|
70
|
+
const { intent, view } = context(projectRoot, reference);
|
|
71
|
+
requireAction(view, 'enterBuild');
|
|
72
|
+
const state = startDeliveryPhase(projectRoot, intent.slug, 'build');
|
|
73
|
+
return { state, view: readWorkItemView(projectRoot, intent.id) };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function preflightAfkFromDashboard(projectRoot, reference, input = {}) {
|
|
77
|
+
const { intent } = context(projectRoot, reference);
|
|
78
|
+
return preflightAfkRun(projectRoot, intent.slug, {
|
|
79
|
+
provider: input.provider ?? 'auto',
|
|
80
|
+
maxParallel: Number(input.maxParallel ?? 1),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function startAfkFromDashboard(projectRoot, reference, input = {}) {
|
|
85
|
+
requireConfirmation(input, 'Starting AFK Build');
|
|
86
|
+
const { intent, view } = context(projectRoot, reference);
|
|
87
|
+
requireAction(view, 'acquireTask');
|
|
88
|
+
const timeoutMinutes = Number(input.timeoutMinutes ?? 45);
|
|
89
|
+
if (!Number.isInteger(timeoutMinutes) || timeoutMinutes < 1 || timeoutMinutes > 1440) {
|
|
90
|
+
throw new Error('AFK timeout must be between 1 and 1440 minutes.');
|
|
91
|
+
}
|
|
92
|
+
return startAfkRun(projectRoot, intent.slug, {
|
|
93
|
+
provider: input.provider ?? 'auto',
|
|
94
|
+
maxParallel: Number(input.maxParallel ?? 1),
|
|
95
|
+
timeoutMs: timeoutMinutes * 60 * 1000,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function controlAfkFromDashboard(projectRoot, runId, input = {}) {
|
|
100
|
+
requireConfirmation(input, `AFK ${input.action ?? 'control'}`);
|
|
101
|
+
if (input.action === 'pause') return pauseAfkRun(projectRoot, runId);
|
|
102
|
+
if (input.action === 'resume') return resumeAfkRun(projectRoot, runId);
|
|
103
|
+
if (input.action === 'cancel') return cancelAfkRun(projectRoot, runId);
|
|
104
|
+
throw new Error(`Unsupported AFK control: ${input.action ?? '<missing>'}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function dashboardAfkRuns(projectRoot, slug = '') {
|
|
108
|
+
return afkRunStatus(projectRoot).filter((run) => !slug || run.slug === slug);
|
|
109
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { atomicJson, now } from '../delivery-documents.mjs';
|
|
5
|
+
import { runtimePaths } from './paths.mjs';
|
|
6
|
+
|
|
7
|
+
const schema = 'ewai.dashboard-handoffs/v1';
|
|
8
|
+
const statuses = new Set(['pending', 'claimed', 'completed', 'cancelled', 'superseded']);
|
|
9
|
+
const reviewDigestPattern = /^[a-f0-9]{64}$/;
|
|
10
|
+
|
|
11
|
+
function boundedText(value, label, maximum = 1_000) {
|
|
12
|
+
const text = String(value ?? '').trim();
|
|
13
|
+
if (text.length > maximum) throw new Error(`${label} exceeds ${maximum} characters.`);
|
|
14
|
+
if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(text)) throw new Error(`${label} contains unsafe control characters.`);
|
|
15
|
+
return text;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normaliseReviewContext(value = {}) {
|
|
19
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Contribution review context must be an object.');
|
|
20
|
+
const allowed = ['revision', 'digest', 'ownerContext', 'summary', 'activePersonas'];
|
|
21
|
+
for (const key of Object.keys(value)) {
|
|
22
|
+
if (!allowed.includes(key)) throw new Error(`Contribution review context contains unknown field: ${key}`);
|
|
23
|
+
}
|
|
24
|
+
const revision = Number(value.revision);
|
|
25
|
+
if (!Number.isInteger(revision) || revision < 1) throw new Error('Contribution review context requires a positive revision.');
|
|
26
|
+
const digest = boundedText(value.digest, 'Contribution review digest', 64);
|
|
27
|
+
if (!reviewDigestPattern.test(digest)) throw new Error('Contribution review context requires a SHA-256 digest.');
|
|
28
|
+
const ownerContext = boundedText(value.ownerContext, 'Contribution review owner context', 40);
|
|
29
|
+
if (!['business', 'technical', 'shared-review'].includes(ownerContext)) throw new Error('Contribution review owner context is invalid.');
|
|
30
|
+
const summary = boundedText(value.summary, 'Contribution review summary');
|
|
31
|
+
if (!summary) throw new Error('Contribution review context requires a bounded summary.');
|
|
32
|
+
if (!Array.isArray(value.activePersonas) || value.activePersonas.length > 4) throw new Error('Contribution review supports no more than four safe active personas.');
|
|
33
|
+
const activePersonas = value.activePersonas.map((persona, index) => {
|
|
34
|
+
if (!persona || typeof persona !== 'object' || Array.isArray(persona)) throw new Error(`Contribution review persona ${index + 1} must be an object.`);
|
|
35
|
+
const personaAllowed = ['id', 'name', 'tier', 'matchedSignals', 'engagementReason'];
|
|
36
|
+
for (const key of Object.keys(persona)) {
|
|
37
|
+
if (!personaAllowed.includes(key)) throw new Error(`Contribution review persona ${index + 1} contains unknown field: ${key}`);
|
|
38
|
+
}
|
|
39
|
+
if (!Array.isArray(persona.matchedSignals) || persona.matchedSignals.length > 12) throw new Error(`Contribution review persona ${index + 1} has invalid matched signals.`);
|
|
40
|
+
return {
|
|
41
|
+
id: boundedText(persona.id, `Contribution review persona ${index + 1} id`, 160),
|
|
42
|
+
name: boundedText(persona.name, `Contribution review persona ${index + 1} name`, 160),
|
|
43
|
+
tier: boundedText(persona.tier, `Contribution review persona ${index + 1} tier`, 40),
|
|
44
|
+
matchedSignals: persona.matchedSignals.map((signal) => boundedText(signal, `Contribution review persona ${index + 1} signal`, 120)),
|
|
45
|
+
engagementReason: boundedText(persona.engagementReason, `Contribution review persona ${index + 1} reason`, 500),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
return { revision, digest, ownerContext, summary, activePersonas };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handoffPath(projectRoot) {
|
|
52
|
+
return resolve(runtimePaths(projectRoot).runtimeStateRoot, 'dashboard-handoffs.json');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readState(projectRoot) {
|
|
56
|
+
const path = handoffPath(projectRoot);
|
|
57
|
+
if (!existsSync(path)) return { schema, items: [] };
|
|
58
|
+
const state = JSON.parse(readFileSync(path, 'utf8'));
|
|
59
|
+
if (state.schema !== schema || !Array.isArray(state.items)) {
|
|
60
|
+
throw new Error(`Unsupported dashboard handoff state: ${path}`);
|
|
61
|
+
}
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function writeState(projectRoot, state) {
|
|
66
|
+
atomicJson(handoffPath(projectRoot), state);
|
|
67
|
+
return state;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function listDashboardHandoffs(projectRoot, options = {}) {
|
|
71
|
+
const state = readState(projectRoot);
|
|
72
|
+
const requestedStatus = String(options.status ?? '').trim();
|
|
73
|
+
return state.items
|
|
74
|
+
.filter((item) => !requestedStatus || item.status === requestedStatus)
|
|
75
|
+
.sort((left, right) => right.requestedAt.localeCompare(left.requestedAt));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function dashboardHandoffStatus(projectRoot) {
|
|
79
|
+
try {
|
|
80
|
+
return { status: 'available', handoffs: listDashboardHandoffs(projectRoot, { status: 'pending' }), error: null };
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return { status: 'invalid', handoffs: [], error: error.message };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function queueDashboardHandoff(projectRoot, input = {}) {
|
|
87
|
+
const action = String(input.action ?? '').trim();
|
|
88
|
+
if (!['begin', 'continue', 'review-contribution'].includes(action)) throw new Error(`Unsupported dashboard handoff action: ${action || '<missing>'}`);
|
|
89
|
+
if (!input.intentId || !input.slug || !input.title) throw new Error('Dashboard handoff requires an intent id, slug, and title.');
|
|
90
|
+
const contributionReview = action === 'review-contribution';
|
|
91
|
+
const reviewContext = contributionReview ? normaliseReviewContext(input.reviewContext) : null;
|
|
92
|
+
|
|
93
|
+
const state = readState(projectRoot);
|
|
94
|
+
const timestamp = now();
|
|
95
|
+
for (const item of state.items) {
|
|
96
|
+
const sameReview = contributionReview && item.action === 'review-contribution' && item.phase === String(input.phase ?? '');
|
|
97
|
+
const sameDeliveryRequest = !contributionReview && ['begin', 'continue'].includes(item.action);
|
|
98
|
+
if (item.intentId === input.intentId && item.status === 'pending' && (sameReview || sameDeliveryRequest)) {
|
|
99
|
+
item.status = 'superseded';
|
|
100
|
+
item.updatedAt = timestamp;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const handoff = {
|
|
104
|
+
schema: 'ewai.dashboard-handoff/v1',
|
|
105
|
+
id: randomUUID(),
|
|
106
|
+
intentId: input.intentId,
|
|
107
|
+
slug: input.slug,
|
|
108
|
+
title: input.title,
|
|
109
|
+
action,
|
|
110
|
+
phase: String(input.phase ?? (action === 'begin' ? 'intent' : '')),
|
|
111
|
+
authority: contributionReview ? 'none' : 'guarded-delivery-handoff',
|
|
112
|
+
reviewContext,
|
|
113
|
+
status: 'pending',
|
|
114
|
+
requestedAt: timestamp,
|
|
115
|
+
updatedAt: timestamp,
|
|
116
|
+
resolvedAt: null,
|
|
117
|
+
resolvedBy: null,
|
|
118
|
+
claimedAt: null,
|
|
119
|
+
claimedBy: null,
|
|
120
|
+
notes: '',
|
|
121
|
+
};
|
|
122
|
+
state.items.push(handoff);
|
|
123
|
+
writeState(projectRoot, state);
|
|
124
|
+
return handoff;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function resolveDashboardHandoff(projectRoot, id, input = {}) {
|
|
128
|
+
const status = String(input.status ?? '').trim();
|
|
129
|
+
if (!statuses.has(status) || status === 'pending') throw new Error(`Unsupported dashboard handoff outcome: ${status || '<missing>'}`);
|
|
130
|
+
const state = readState(projectRoot);
|
|
131
|
+
const handoff = state.items.find((item) => item.id === id);
|
|
132
|
+
if (!handoff) throw new Error(`Unknown dashboard handoff: ${id}`);
|
|
133
|
+
if (!['pending', 'claimed'].includes(handoff.status)) {
|
|
134
|
+
throw new Error(`Dashboard handoff ${id} is already ${handoff.status}.`);
|
|
135
|
+
}
|
|
136
|
+
handoff.status = status;
|
|
137
|
+
handoff.updatedAt = now();
|
|
138
|
+
const actor = String(input.resolvedBy ?? '').trim() || null;
|
|
139
|
+
if (status === 'claimed') {
|
|
140
|
+
handoff.claimedAt = handoff.updatedAt;
|
|
141
|
+
handoff.claimedBy = actor;
|
|
142
|
+
} else {
|
|
143
|
+
handoff.resolvedAt = handoff.updatedAt;
|
|
144
|
+
handoff.resolvedBy = actor;
|
|
145
|
+
}
|
|
146
|
+
handoff.notes = String(input.notes ?? '').trim();
|
|
147
|
+
writeState(projectRoot, state);
|
|
148
|
+
return handoff;
|
|
149
|
+
}
|