@devflow-tools/delivery-line 0.18.8
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/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/__tests__/artifact-generation.test.d.ts +2 -0
- package/dist/__tests__/artifact-generation.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-generation.test.js +16 -0
- package/dist/__tests__/artifact-generation.test.js.map +1 -0
- package/dist/__tests__/backend-scenario.e2e.test.d.ts +2 -0
- package/dist/__tests__/backend-scenario.e2e.test.d.ts.map +1 -0
- package/dist/__tests__/backend-scenario.e2e.test.js +25 -0
- package/dist/__tests__/backend-scenario.e2e.test.js.map +1 -0
- package/dist/__tests__/claude-events.test.d.ts +2 -0
- package/dist/__tests__/claude-events.test.d.ts.map +1 -0
- package/dist/__tests__/claude-events.test.js +28 -0
- package/dist/__tests__/claude-events.test.js.map +1 -0
- package/dist/__tests__/facade-e2e.test.d.ts +2 -0
- package/dist/__tests__/facade-e2e.test.d.ts.map +1 -0
- package/dist/__tests__/facade-e2e.test.js +167 -0
- package/dist/__tests__/facade-e2e.test.js.map +1 -0
- package/dist/__tests__/frontend-scenario.e2e.test.d.ts +2 -0
- package/dist/__tests__/frontend-scenario.e2e.test.d.ts.map +1 -0
- package/dist/__tests__/frontend-scenario.e2e.test.js +35 -0
- package/dist/__tests__/frontend-scenario.e2e.test.js.map +1 -0
- package/dist/__tests__/git-artifact-store.test.d.ts +2 -0
- package/dist/__tests__/git-artifact-store.test.d.ts.map +1 -0
- package/dist/__tests__/git-artifact-store.test.js +44 -0
- package/dist/__tests__/git-artifact-store.test.js.map +1 -0
- package/dist/__tests__/governance-adapters.test.d.ts +2 -0
- package/dist/__tests__/governance-adapters.test.d.ts.map +1 -0
- package/dist/__tests__/governance-adapters.test.js +51 -0
- package/dist/__tests__/governance-adapters.test.js.map +1 -0
- package/dist/__tests__/handoff-validator.test.d.ts +2 -0
- package/dist/__tests__/handoff-validator.test.d.ts.map +1 -0
- package/dist/__tests__/handoff-validator.test.js +13 -0
- package/dist/__tests__/handoff-validator.test.js.map +1 -0
- package/dist/__tests__/interaction-session.test.d.ts +2 -0
- package/dist/__tests__/interaction-session.test.d.ts.map +1 -0
- package/dist/__tests__/interaction-session.test.js +13 -0
- package/dist/__tests__/interaction-session.test.js.map +1 -0
- package/dist/__tests__/interaction.test.d.ts +2 -0
- package/dist/__tests__/interaction.test.d.ts.map +1 -0
- package/dist/__tests__/interaction.test.js +27 -0
- package/dist/__tests__/interaction.test.js.map +1 -0
- package/dist/__tests__/persistence.test.d.ts +2 -0
- package/dist/__tests__/persistence.test.d.ts.map +1 -0
- package/dist/__tests__/persistence.test.js +41 -0
- package/dist/__tests__/persistence.test.js.map +1 -0
- package/dist/__tests__/recovery-spike.test.d.ts +2 -0
- package/dist/__tests__/recovery-spike.test.d.ts.map +1 -0
- package/dist/__tests__/recovery-spike.test.js +20 -0
- package/dist/__tests__/recovery-spike.test.js.map +1 -0
- package/dist/__tests__/sqlite-recovery.test.d.ts +2 -0
- package/dist/__tests__/sqlite-recovery.test.d.ts.map +1 -0
- package/dist/__tests__/sqlite-recovery.test.js +90 -0
- package/dist/__tests__/sqlite-recovery.test.js.map +1 -0
- package/dist/__tests__/state-machine.test.d.ts +2 -0
- package/dist/__tests__/state-machine.test.d.ts.map +1 -0
- package/dist/__tests__/state-machine.test.js +49 -0
- package/dist/__tests__/state-machine.test.js.map +1 -0
- package/dist/__tests__/subagent-orchestrator.test.d.ts +2 -0
- package/dist/__tests__/subagent-orchestrator.test.d.ts.map +1 -0
- package/dist/__tests__/subagent-orchestrator.test.js +33 -0
- package/dist/__tests__/subagent-orchestrator.test.js.map +1 -0
- package/dist/__tests__/subagent-recovery.e2e.test.d.ts +2 -0
- package/dist/__tests__/subagent-recovery.e2e.test.d.ts.map +1 -0
- package/dist/__tests__/subagent-recovery.e2e.test.js +35 -0
- package/dist/__tests__/subagent-recovery.e2e.test.js.map +1 -0
- package/dist/__tests__/task-dag.test.d.ts +2 -0
- package/dist/__tests__/task-dag.test.d.ts.map +1 -0
- package/dist/__tests__/task-dag.test.js +31 -0
- package/dist/__tests__/task-dag.test.js.map +1 -0
- package/dist/__tests__/worker-integration.test.d.ts +2 -0
- package/dist/__tests__/worker-integration.test.d.ts.map +1 -0
- package/dist/__tests__/worker-integration.test.js +217 -0
- package/dist/__tests__/worker-integration.test.js.map +1 -0
- package/dist/adapters/approval.d.ts +40 -0
- package/dist/adapters/approval.d.ts.map +1 -0
- package/dist/adapters/approval.js +100 -0
- package/dist/adapters/approval.js.map +1 -0
- package/dist/adapters/delivery-package.d.ts +23 -0
- package/dist/adapters/delivery-package.d.ts.map +1 -0
- package/dist/adapters/delivery-package.js +37 -0
- package/dist/adapters/delivery-package.js.map +1 -0
- package/dist/adapters/integration.d.ts +37 -0
- package/dist/adapters/integration.d.ts.map +1 -0
- package/dist/adapters/integration.js +84 -0
- package/dist/adapters/integration.js.map +1 -0
- package/dist/adapters/review.d.ts +12 -0
- package/dist/adapters/review.d.ts.map +1 -0
- package/dist/adapters/review.js +33 -0
- package/dist/adapters/review.js.map +1 -0
- package/dist/adapters/verification.d.ts +25 -0
- package/dist/adapters/verification.d.ts.map +1 -0
- package/dist/adapters/verification.js +57 -0
- package/dist/adapters/verification.js.map +1 -0
- package/dist/adapters/workflow-execution.d.ts +130 -0
- package/dist/adapters/workflow-execution.d.ts.map +1 -0
- package/dist/adapters/workflow-execution.js +135 -0
- package/dist/adapters/workflow-execution.js.map +1 -0
- package/dist/artifact-generation.test.d.ts +2 -0
- package/dist/artifact-generation.test.d.ts.map +1 -0
- package/dist/artifact-generation.test.js +11 -0
- package/dist/artifact-generation.test.js.map +1 -0
- package/dist/artifacts/context-manifest.d.ts +20 -0
- package/dist/artifacts/context-manifest.d.ts.map +1 -0
- package/dist/artifacts/context-manifest.js +24 -0
- package/dist/artifacts/context-manifest.js.map +1 -0
- package/dist/artifacts/generation-guard.d.ts +13 -0
- package/dist/artifacts/generation-guard.d.ts.map +1 -0
- package/dist/artifacts/generation-guard.js +16 -0
- package/dist/artifacts/generation-guard.js.map +1 -0
- package/dist/artifacts/git-artifact-store.d.ts +16 -0
- package/dist/artifacts/git-artifact-store.d.ts.map +1 -0
- package/dist/artifacts/git-artifact-store.js +47 -0
- package/dist/artifacts/git-artifact-store.js.map +1 -0
- package/dist/claude-events.test.d.ts +2 -0
- package/dist/claude-events.test.d.ts.map +1 -0
- package/dist/claude-events.test.js +14 -0
- package/dist/claude-events.test.js.map +1 -0
- package/dist/composition.d.ts +13 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +47 -0
- package/dist/composition.js.map +1 -0
- package/dist/domain/interaction.d.ts +23 -0
- package/dist/domain/interaction.d.ts.map +1 -0
- package/dist/domain/interaction.js +60 -0
- package/dist/domain/interaction.js.map +1 -0
- package/dist/domain/state-machine.d.ts +26 -0
- package/dist/domain/state-machine.d.ts.map +1 -0
- package/dist/domain/state-machine.js +67 -0
- package/dist/domain/state-machine.js.map +1 -0
- package/dist/domain/task-dag.d.ts +5 -0
- package/dist/domain/task-dag.d.ts.map +1 -0
- package/dist/domain/task-dag.js +78 -0
- package/dist/domain/task-dag.js.map +1 -0
- package/dist/domain/types.d.ts +320 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +9 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/facade/delivery-line-facade.d.ts +77 -0
- package/dist/facade/delivery-line-facade.d.ts.map +1 -0
- package/dist/facade/delivery-line-facade.js +417 -0
- package/dist/facade/delivery-line-facade.js.map +1 -0
- package/dist/git-artifact-store.test.d.ts +2 -0
- package/dist/git-artifact-store.test.d.ts.map +1 -0
- package/dist/git-artifact-store.test.js +27 -0
- package/dist/git-artifact-store.test.js.map +1 -0
- package/dist/governance-adapters.test.d.ts +2 -0
- package/dist/governance-adapters.test.d.ts.map +1 -0
- package/dist/governance-adapters.test.js +14 -0
- package/dist/governance-adapters.test.js.map +1 -0
- package/dist/handoff-validator.test.d.ts +2 -0
- package/dist/handoff-validator.test.d.ts.map +1 -0
- package/dist/handoff-validator.test.js +10 -0
- package/dist/handoff-validator.test.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction-session.d.ts +12 -0
- package/dist/interaction-session.d.ts.map +1 -0
- package/dist/interaction-session.js +21 -0
- package/dist/interaction-session.js.map +1 -0
- package/dist/interaction-session.test.d.ts +2 -0
- package/dist/interaction-session.test.d.ts.map +1 -0
- package/dist/interaction-session.test.js +13 -0
- package/dist/interaction-session.test.js.map +1 -0
- package/dist/persistence/cas.d.ts +4 -0
- package/dist/persistence/cas.d.ts.map +1 -0
- package/dist/persistence/cas.js +7 -0
- package/dist/persistence/cas.js.map +1 -0
- package/dist/persistence/event-ledger.d.ts +10 -0
- package/dist/persistence/event-ledger.d.ts.map +1 -0
- package/dist/persistence/event-ledger.js +6 -0
- package/dist/persistence/event-ledger.js.map +1 -0
- package/dist/persistence/memory-store.d.ts +51 -0
- package/dist/persistence/memory-store.d.ts.map +1 -0
- package/dist/persistence/memory-store.js +99 -0
- package/dist/persistence/memory-store.js.map +1 -0
- package/dist/persistence/schema.d.ts +2 -0
- package/dist/persistence/schema.d.ts.map +1 -0
- package/dist/persistence/schema.js +107 -0
- package/dist/persistence/schema.js.map +1 -0
- package/dist/persistence/sqlite-store.d.ts +59 -0
- package/dist/persistence/sqlite-store.d.ts.map +1 -0
- package/dist/persistence/sqlite-store.js +133 -0
- package/dist/persistence/sqlite-store.js.map +1 -0
- package/dist/persistence.test.d.ts +2 -0
- package/dist/persistence.test.d.ts.map +1 -0
- package/dist/persistence.test.js +28 -0
- package/dist/persistence.test.js.map +1 -0
- package/dist/ports/index.d.ts +248 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/recovery-spike.d.ts +22 -0
- package/dist/recovery-spike.d.ts.map +1 -0
- package/dist/recovery-spike.js +28 -0
- package/dist/recovery-spike.js.map +1 -0
- package/dist/recovery-spike.test.d.ts +2 -0
- package/dist/recovery-spike.test.d.ts.map +1 -0
- package/dist/recovery-spike.test.js +20 -0
- package/dist/recovery-spike.test.js.map +1 -0
- package/dist/runtime/claude-host-adapter.d.ts +28 -0
- package/dist/runtime/claude-host-adapter.d.ts.map +1 -0
- package/dist/runtime/claude-host-adapter.js +43 -0
- package/dist/runtime/claude-host-adapter.js.map +1 -0
- package/dist/runtime/context-binder.d.ts +61 -0
- package/dist/runtime/context-binder.d.ts.map +1 -0
- package/dist/runtime/context-binder.js +26 -0
- package/dist/runtime/context-binder.js.map +1 -0
- package/dist/runtime/execution-coordinator.d.ts +32 -0
- package/dist/runtime/execution-coordinator.d.ts.map +1 -0
- package/dist/runtime/execution-coordinator.js +243 -0
- package/dist/runtime/execution-coordinator.js.map +1 -0
- package/dist/runtime/gate-queue.d.ts +17 -0
- package/dist/runtime/gate-queue.d.ts.map +1 -0
- package/dist/runtime/gate-queue.js +33 -0
- package/dist/runtime/gate-queue.js.map +1 -0
- package/dist/runtime/host-conformance.d.ts +47 -0
- package/dist/runtime/host-conformance.d.ts.map +1 -0
- package/dist/runtime/host-conformance.js +61 -0
- package/dist/runtime/host-conformance.js.map +1 -0
- package/dist/runtime/host-routing.d.ts +5 -0
- package/dist/runtime/host-routing.d.ts.map +1 -0
- package/dist/runtime/host-routing.js +42 -0
- package/dist/runtime/host-routing.js.map +1 -0
- package/dist/runtime/memory-writeback.d.ts +18 -0
- package/dist/runtime/memory-writeback.d.ts.map +1 -0
- package/dist/runtime/memory-writeback.js +23 -0
- package/dist/runtime/memory-writeback.js.map +1 -0
- package/dist/runtime/operation-journal.d.ts +13 -0
- package/dist/runtime/operation-journal.d.ts.map +1 -0
- package/dist/runtime/operation-journal.js +38 -0
- package/dist/runtime/operation-journal.js.map +1 -0
- package/dist/runtime/plan-compiler.d.ts +37 -0
- package/dist/runtime/plan-compiler.d.ts.map +1 -0
- package/dist/runtime/plan-compiler.js +62 -0
- package/dist/runtime/plan-compiler.js.map +1 -0
- package/dist/runtime/project-runtime-factory.d.ts +15 -0
- package/dist/runtime/project-runtime-factory.d.ts.map +1 -0
- package/dist/runtime/project-runtime-factory.js +11 -0
- package/dist/runtime/project-runtime-factory.js.map +1 -0
- package/dist/runtime/review-coordinator.d.ts +25 -0
- package/dist/runtime/review-coordinator.d.ts.map +1 -0
- package/dist/runtime/review-coordinator.js +22 -0
- package/dist/runtime/review-coordinator.js.map +1 -0
- package/dist/subagents/claude-events.d.ts +59 -0
- package/dist/subagents/claude-events.d.ts.map +1 -0
- package/dist/subagents/claude-events.js +60 -0
- package/dist/subagents/claude-events.js.map +1 -0
- package/dist/subagents/handoff-validator.d.ts +8 -0
- package/dist/subagents/handoff-validator.d.ts.map +1 -0
- package/dist/subagents/handoff-validator.js +20 -0
- package/dist/subagents/handoff-validator.js.map +1 -0
- package/dist/subagents/orchestrator.d.ts +47 -0
- package/dist/subagents/orchestrator.d.ts.map +1 -0
- package/dist/subagents/orchestrator.js +85 -0
- package/dist/subagents/orchestrator.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 shilongfeicool
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @devflow-tools/delivery-line
|
|
2
|
+
|
|
3
|
+
> 从需求澄清到受控 `integrationRef` 与可审计交付包的宿主中立交付编排。
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@devflow-tools/delivery-line)
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @devflow-tools/delivery-line
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 快速开始
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { ProjectRuntimeFactory } from '@devflow-tools/delivery-line';
|
|
17
|
+
|
|
18
|
+
const runtime = await ProjectRuntimeFactory.create({ projectRoot: process.cwd() });
|
|
19
|
+
const status = await runtime.facade.createCase({
|
|
20
|
+
actor: { id: 'owner', kind: 'human' },
|
|
21
|
+
sessionId: 'session-1',
|
|
22
|
+
executionId: 'execution-1',
|
|
23
|
+
idempotencyKey: 'create-1',
|
|
24
|
+
contextReceipt: 'context-receipt-1',
|
|
25
|
+
projectRoot: runtime.projectRoot,
|
|
26
|
+
baseRef: 'HEAD',
|
|
27
|
+
targetRef: 'main',
|
|
28
|
+
title: 'Implement and verify a scoped change',
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## 运行边界
|
|
33
|
+
|
|
34
|
+
- Delivery Line 只更新案件专属 `integrationRef`,不会写入受保护的 `targetRef`。
|
|
35
|
+
- Worker、Verification、Review、Approval 与 Package 都通过显式 Port/Adapter 接入。
|
|
36
|
+
- 宿主能力必须先通过 conformance probe;缺失能力会使案件进入 `held`,不会伪造成功。
|
|
37
|
+
- 代码、Memory 与 Knowledge 只以带来源哈希的 Context Receipt 进入交付链。
|
|
38
|
+
|
|
39
|
+
## 公共 API
|
|
40
|
+
|
|
41
|
+
主要入口包括 `ProjectRuntimeFactory`、`DeliveryLineFacade`、`ContextBinder`、`PlanCompiler`、`ExecutionCoordinator`、`ReviewCoordinator` 与宿主 conformance 契约。
|
|
42
|
+
|
|
43
|
+
## 相关链接
|
|
44
|
+
|
|
45
|
+
- [DevFlow 仓库](https://github.com/shilongfeicool/dev-flow)
|
|
46
|
+
- [问题反馈](https://github.com/shilongfeicool/dev-flow/issues)
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-generation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/artifact-generation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { validateArtifactGeneration } from '../artifacts/generation-guard.js';
|
|
3
|
+
import { assertContextArtifactBinding } from '../artifacts/context-manifest.js';
|
|
4
|
+
const context = { id: 'ctx', caseId: 'case', receipt: 'receipt', refs: [{ path: 'src/a.ts', hash: 'hash', classification: 'internal', untrusted: true }], artifactRefs: [{ id: 'artifact-1', ref: 'refs/devflow/delivery-artifacts/case', commit: 'commit-1', path: 'spec.md', blobHash: 'blob-1', contentHash: 'parent' }], omitted: [], compressed: false };
|
|
5
|
+
const base = { type: 'spec', parentHash: 'parent', context, generator: { id: 'agent', kind: 'agent', role: 'designer' }, content: '# spec' };
|
|
6
|
+
describe('artifact generation guard', () => {
|
|
7
|
+
it('accepts a bounded artifact and rejects secret-like content', () => {
|
|
8
|
+
expect(() => validateArtifactGeneration(base)).not.toThrow();
|
|
9
|
+
expect(() => validateArtifactGeneration({ ...base, content: 'api_key: leaked' })).toThrow('secret-like');
|
|
10
|
+
expect(() => assertContextArtifactBinding(context, context.artifactRefs[0])).not.toThrow();
|
|
11
|
+
expect(() => assertContextArtifactBinding(context, { ...context.artifactRefs[0], contentHash: 'other' })).toThrow('not bound');
|
|
12
|
+
expect(() => validateArtifactGeneration({ ...base, context: { ...context, compressed: true, compactionMarker: 'compact-1' } })).toThrow('source hashes');
|
|
13
|
+
expect(() => validateArtifactGeneration({ ...base, context: { ...context, compressed: true, compactionMarker: 'compact-1', retainedSourceHashes: ['hash'] } })).not.toThrow();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=artifact-generation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-generation.test.js","sourceRoot":"","sources":["../../src/__tests__/artifact-generation.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAEhF,MAAM,OAAO,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,UAAmB,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,EAAE,sCAAsC,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACvW,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,MAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAE/J,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7D,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACzG,MAAM,CAAC,GAAG,EAAE,CAAC,4BAA4B,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC3F,MAAM,CAAC,GAAG,EAAE,CAAC,4BAA4B,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC/H,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACzJ,MAAM,CAAC,GAAG,EAAE,CAAC,0BAA0B,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAChL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend-scenario.e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/backend-scenario.e2e.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DeliveryLineError } from '../domain/types.js';
|
|
3
|
+
import { assertIntegrationTarget } from '../adapters/integration.js';
|
|
4
|
+
import { assertReviewPasses } from '../adapters/review.js';
|
|
5
|
+
import { assertNonBypassableGates } from '../adapters/approval.js';
|
|
6
|
+
describe('backend developer journey', () => {
|
|
7
|
+
it('rejects target ref writes and requires all security gates', () => {
|
|
8
|
+
const bugInput = 'Workflow Run 详情接口必须绑定请求中的 project scope;跨项目的 runId 返回 403,且不能改变任何状态。先补充能稳定复现问题的测试,再做最小修复。';
|
|
9
|
+
expect(bugInput).toContain('403');
|
|
10
|
+
expect(() => assertIntegrationTarget({ integrationRef: 'refs/devflow/delivery/case', targetRef: 'main', candidateRevision: 'candidate' })).not.toThrow();
|
|
11
|
+
expect(() => assertIntegrationTarget({ integrationRef: 'main', targetRef: 'main', candidateRevision: 'candidate' })).toThrow(DeliveryLineError);
|
|
12
|
+
const passed = ['bugs', 'security', 'compliance'].map(kind => ({ id: kind, kind, artifactHash: 'h', findings: [], passed: true, createdAt: 1 }));
|
|
13
|
+
expect(() => assertReviewPasses(passed, 'h')).not.toThrow();
|
|
14
|
+
expect(() => assertReviewPasses(passed.map(receipt => receipt.kind === 'security' ? { ...receipt, passed: false } : receipt), 'h')).toThrow('security');
|
|
15
|
+
const state = { role: 'user', balance: 100 };
|
|
16
|
+
const before = structuredClone(state);
|
|
17
|
+
const request = { role: 'user', resource: 'admin/balance' };
|
|
18
|
+
expect(request.resource.startsWith('admin/')).toBe(true);
|
|
19
|
+
expect(() => { if (request.resource.startsWith('admin/'))
|
|
20
|
+
throw new DeliveryLineError('MISSING_PREREQUISITE', '403'); }).toThrow('403');
|
|
21
|
+
expect(state).toEqual(before);
|
|
22
|
+
expect(() => assertNonBypassableGates({ auth: true, dataIntegrity: true, redTest: false })).toThrow('safety');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=backend-scenario.e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend-scenario.e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/backend-scenario.e2e.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,QAAQ,GAAG,2FAA2F,CAAC;QAC7G,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,EAAE,cAAc,EAAE,4BAA4B,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACzJ,MAAM,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAChJ,MAAM,MAAM,GAAI,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,CAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5J,MAAM,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QAC5D,MAAM,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACxJ,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;QAC5D,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACxI,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-events.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/claude-events.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { consumeClaudeSession, createClaudeSessionState, mapClaudeEvent, replayClaudeHistory, shouldBreak } from '../subagents/claude-events.js';
|
|
3
|
+
describe('Claude event contract', () => {
|
|
4
|
+
it('deduplicates replayed events while preserving terminal idle semantics', () => {
|
|
5
|
+
const state = createClaudeSessionState();
|
|
6
|
+
expect(mapClaudeEvent({ id: 'm1', type: 'agent.message', text: 'ok' }, state)).toMatchObject({ type: 'message' });
|
|
7
|
+
expect(mapClaudeEvent({ id: 'm1', type: 'agent.message', text: 'ok' }, state)).toBeUndefined();
|
|
8
|
+
const waiting = mapClaudeEvent({ id: 'i1', type: 'session.status_idle', stop_reason: { type: 'requires_action' } }, state);
|
|
9
|
+
expect(shouldBreak(waiting)).toBe(false);
|
|
10
|
+
const done = mapClaudeEvent({ id: 'i2', type: 'session.status_idle', stop_reason: { type: 'end_turn' } }, state);
|
|
11
|
+
expect(shouldBreak(done)).toBe(true);
|
|
12
|
+
const history = replayClaudeHistory([{ id: 'u1', type: 'user.message' }, { id: 'u1', type: 'user.message' }, { id: 'c1', type: 'compaction', marker: 'compact-1' }]);
|
|
13
|
+
expect(history.map(event => event.type)).toEqual(['user_event_queued', 'compaction']);
|
|
14
|
+
expect(mapClaudeEvent({ id: 'tool-1', type: 'agent.custom_tool_use', name: 'secret-tool', input: { apiKey: 'secret' } }, state)).toMatchObject({ type: 'custom_tool_required', input: { apiKey: '[REDACTED]' } });
|
|
15
|
+
});
|
|
16
|
+
it('opens the stream before kickoff and replays history with processed markers', async () => {
|
|
17
|
+
const order = [];
|
|
18
|
+
const stream = async function* () { yield { id: 'stream-1', type: 'agent.message', text: 'live' }; };
|
|
19
|
+
const events = await consumeClaudeSession({
|
|
20
|
+
openStream: async () => { order.push('open'); return stream(); },
|
|
21
|
+
kickoff: async () => { order.push('kickoff'); },
|
|
22
|
+
history: async () => [{ id: 'history-1', type: 'user.message', processed_at: '2026-08-28T00:00:00Z' }, { id: 'stream-1', type: 'agent.message', text: 'replayed' }],
|
|
23
|
+
});
|
|
24
|
+
expect(order).toEqual(['open', 'kickoff']);
|
|
25
|
+
expect(events.map(event => event.type)).toEqual(['user_event_processed', 'message']);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=claude-events.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-events.test.js","sourceRoot":"","sources":["../../src/__tests__/claude-events.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facade-e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/facade-e2e.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DeliveryLineFacade } from '../facade/delivery-line-facade.js';
|
|
3
|
+
import { MemoryCaseStore } from '../persistence/memory-store.js';
|
|
4
|
+
const identity = (key) => ({ actor: { id: 'owner', kind: 'human', role: 'owner' }, sessionId: 'session-1', executionId: 'execution-1', idempotencyKey: key, contextReceipt: 'ctx-1', turn: 1 });
|
|
5
|
+
describe('delivery line facade', () => {
|
|
6
|
+
it('drives a gated case from clarification to a delivered package', async () => {
|
|
7
|
+
const store = new MemoryCaseStore();
|
|
8
|
+
const calls = [];
|
|
9
|
+
const verificationInputs = [];
|
|
10
|
+
const facade = new DeliveryLineFacade({
|
|
11
|
+
store,
|
|
12
|
+
execution: { startTask: async (raw) => { const input = raw; calls.push(`execute:${input.taskId}`); return { taskId: input.taskId, status: 'started' }; }, cancelTask: async () => { } },
|
|
13
|
+
verification: { verify: async (raw) => { verificationInputs.push(raw); const input = raw; return { id: `verification:${input.taskId ?? 'plan'}`, taskId: input.taskId, commit: 'candidate', lockfileHash: 'lock', runtime: 'node', toolVersion: 'vitest', environment: 'test', commands: [], passed: true, createdAt: Date.now() }; } },
|
|
14
|
+
integration: { integrate: async (raw) => { calls.push(`integrate:${raw.taskId}`); return { candidateRevision: 'candidate-integrated' }; } },
|
|
15
|
+
review: { review: async (raw) => { const input = raw; return { id: `review:${input.kind}`, kind: input.kind, artifactHash: 'review', findings: [], passed: true, createdAt: Date.now() }; } },
|
|
16
|
+
package: { prepare: async () => ({ packageHash: 'package-hash', path: '/tmp/delivery.json' }) }, approval: { approve: async (input) => input, reject: async (input) => input },
|
|
17
|
+
});
|
|
18
|
+
let status = await facade.createCase({ ...identity('create'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'Add feature' });
|
|
19
|
+
expect(status.stage).toBe('clarifying');
|
|
20
|
+
status = await facade.answer(status.caseId, { ...identity('answer'), turn: 1, value: 'Feature with tests' });
|
|
21
|
+
status = await facade.approve(status.caseId, { ...identity('approve-intent'), stage: 'intent' });
|
|
22
|
+
expect(status.stage).toBe('designing');
|
|
23
|
+
status = await facade.compareOptions(status.caseId, { ...identity('compare'), option: 'bounded design' });
|
|
24
|
+
status = await facade.approve(status.caseId, { ...identity('approve-spec'), stage: 'spec' });
|
|
25
|
+
status = await facade.approve(status.caseId, { ...identity('approve-plan'), stage: 'plan' });
|
|
26
|
+
status = await facade.resume(status.caseId, identity('resume'));
|
|
27
|
+
expect(status.stage).toBe('reviewed');
|
|
28
|
+
status = await facade.approve(status.caseId, { ...identity('approve-release'), stage: 'release' });
|
|
29
|
+
status = await facade.export(status.caseId, { ...identity('export'), path: '/tmp/delivery.json' });
|
|
30
|
+
expect(status.stage).toBe('delivered');
|
|
31
|
+
const delivered = await store.get(status.caseId);
|
|
32
|
+
expect(delivered.evidence.some(item => item.hash === 'package-hash')).toBe(true);
|
|
33
|
+
expect(delivered.artifacts.at(-1)).toMatchObject({ type: 'delivery-package', contentHash: expect.any(String) });
|
|
34
|
+
expect(calls).toEqual([`execute:${status.caseId}:task-1`, `integrate:${status.caseId}:task-1`]);
|
|
35
|
+
expect(status.artifactRefs.map(artifact => artifact.type)).toEqual(['intent', 'spec', 'plan', 'delivery-package']);
|
|
36
|
+
expect(status.evidenceRefs.length).toBeGreaterThan(0);
|
|
37
|
+
expect((await store.get(status.caseId)).interaction.summaries?.[0]).toMatchObject({ question: expect.any(String), answer: 'Feature with tests', contextReceipt: 'ctx-1' });
|
|
38
|
+
expect(status.taskSummaries[0]).toMatchObject({ status: 'integrated', attempt: 1 });
|
|
39
|
+
expect(verificationInputs.at(-1)).toMatchObject({ planLevel: true, integrationRef: status.caseId.replace(status.caseId, `refs/devflow/delivery/${status.caseId}`) });
|
|
40
|
+
});
|
|
41
|
+
it('does not replay a running task after recovery and rejects stale approval hashes', async () => {
|
|
42
|
+
const store = new MemoryCaseStore();
|
|
43
|
+
const facade = new DeliveryLineFacade({ store });
|
|
44
|
+
let status = await facade.createCase({ ...identity('create-recovery'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'Recovery task' });
|
|
45
|
+
status = await facade.answer(status.caseId, { ...identity('answer-recovery'), turn: 1, value: 'criteria' });
|
|
46
|
+
await expect(facade.approve(status.caseId, { ...identity('approve-old-turn'), turn: 0, stage: 'intent' })).rejects.toMatchObject({ code: 'STALE_INTERACTION_TURN' });
|
|
47
|
+
await expect(facade.approve(status.caseId, { ...identity('approve-stale'), stage: 'intent', artifactHash: 'stale' })).rejects.toMatchObject({ code: 'ARTIFACT_HASH_MISMATCH' });
|
|
48
|
+
status = await facade.approve(status.caseId, { ...identity('approve-recovery-intent'), stage: 'intent' });
|
|
49
|
+
status = await facade.compareOptions(status.caseId, { ...identity('compare-recovery'), option: 'design' });
|
|
50
|
+
status = await facade.approve(status.caseId, { ...identity('approve-recovery-spec'), stage: 'spec' });
|
|
51
|
+
status = await facade.approve(status.caseId, { ...identity('approve-recovery-plan'), stage: 'plan' });
|
|
52
|
+
const record = await store.get(status.caseId);
|
|
53
|
+
record.tasks[0].status = 'running';
|
|
54
|
+
await store.save(record, record.version);
|
|
55
|
+
const resumed = await facade.resume(status.caseId, identity('resume-recovery'));
|
|
56
|
+
expect(resumed.stage).toBe('held');
|
|
57
|
+
expect(resumed.taskSummaries[0].attempt).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
it('rejects create idempotency reuse with a different request payload', async () => {
|
|
60
|
+
const store = new MemoryCaseStore();
|
|
61
|
+
const facade = new DeliveryLineFacade({ store });
|
|
62
|
+
const first = await facade.createCase({ ...identity('same-create'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'first' });
|
|
63
|
+
await expect(facade.createCase({ ...identity('same-create'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'different' })).rejects.toMatchObject({ code: 'IDEMPOTENCY_CONFLICT' });
|
|
64
|
+
await expect(facade.createCase({ ...identity('same-create'), caseId: first.caseId, projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'first' })).resolves.toMatchObject({ caseId: first.caseId });
|
|
65
|
+
});
|
|
66
|
+
it('routes integration conflicts to a resumable blocked case', async () => {
|
|
67
|
+
const store = new MemoryCaseStore();
|
|
68
|
+
const facade = new DeliveryLineFacade({
|
|
69
|
+
store,
|
|
70
|
+
execution: { startTask: async (input) => ({ taskId: input.taskId, status: 'started' }), cancelTask: async () => { } },
|
|
71
|
+
verification: { verify: async (input) => ({ id: 'v', taskId: input.taskId, commit: 'candidate', lockfileHash: 'lock', runtime: 'node', toolVersion: 'test', environment: 'test', commands: [], passed: true, createdAt: 1 }) },
|
|
72
|
+
integration: { integrate: async () => ({ candidateRevision: '' }) },
|
|
73
|
+
});
|
|
74
|
+
let status = await facade.createCase({ ...identity('create-conflict'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'Conflict' });
|
|
75
|
+
status = await facade.answer(status.caseId, { ...identity('answer-conflict'), turn: 1, value: 'criteria' });
|
|
76
|
+
status = await facade.approve(status.caseId, { ...identity('approve-conflict-intent'), stage: 'intent' });
|
|
77
|
+
status = await facade.compareOptions(status.caseId, { ...identity('compare-conflict'), option: 'design' });
|
|
78
|
+
status = await facade.approve(status.caseId, { ...identity('approve-conflict-spec'), stage: 'spec' });
|
|
79
|
+
status = await facade.approve(status.caseId, { ...identity('approve-conflict-plan'), stage: 'plan' });
|
|
80
|
+
status = await facade.resume(status.caseId, identity('resume-conflict'));
|
|
81
|
+
expect(status.stage).toBe('blocked');
|
|
82
|
+
expect(status.blockers[0]).toMatchObject({ code: 'BLOCKED', taskId: `${status.caseId}:task-1` });
|
|
83
|
+
expect(status.nextActions).toContain('resume');
|
|
84
|
+
});
|
|
85
|
+
it('holds a case when interaction or wall-time budgets are exhausted', async () => {
|
|
86
|
+
const store = new MemoryCaseStore();
|
|
87
|
+
const facade = new DeliveryLineFacade({ store });
|
|
88
|
+
let status = await facade.createCase({ ...identity('create-budget'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'Budget', budgets: { maxInteractionTurns: 1 } });
|
|
89
|
+
status = await facade.answer(status.caseId, { ...identity('answer-budget'), turn: 1, value: 'criteria' });
|
|
90
|
+
expect(status.stage).toBe('held');
|
|
91
|
+
expect(status.blockers[0]?.code).toBe('HELD');
|
|
92
|
+
});
|
|
93
|
+
it('cancels running tasks and protects terminal cases from later writes', async () => {
|
|
94
|
+
const store = new MemoryCaseStore();
|
|
95
|
+
const cancelled = [];
|
|
96
|
+
const facade = new DeliveryLineFacade({ store, execution: { startTask: async (input) => ({ taskId: input.taskId, status: 'started' }), cancelTask: async (input) => { cancelled.push(input.taskId); } } });
|
|
97
|
+
let status = await facade.createCase({ ...identity('create-cancel'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'Cancel' });
|
|
98
|
+
const record = await store.get(status.caseId);
|
|
99
|
+
record.tasks = [{ id: `${status.caseId}:task`, caseId: status.caseId, title: 'task', attempt: 1, version: 0, baseRevision: 'main', dependsOn: [], allowedFiles: [], expectedChanges: [], verificationCommands: [], status: 'running', evidence: [] }];
|
|
100
|
+
record.stage = 'executing';
|
|
101
|
+
await store.save(record, record.version);
|
|
102
|
+
status = await facade.cancel(status.caseId, { ...identity('cancel'), reason: 'stop' });
|
|
103
|
+
expect(status.stage).toBe('cancelled');
|
|
104
|
+
expect(cancelled).toEqual([`${status.caseId}:task`]);
|
|
105
|
+
expect(status.taskSummaries[0].status).toBe('cancelled');
|
|
106
|
+
await expect(facade.answer(status.caseId, { ...identity('late-answer'), turn: 1, value: 'late' })).rejects.toMatchObject({ code: 'CASE_CANCELLED' });
|
|
107
|
+
await expect(facade.cancel(status.caseId, { ...identity('late-cancel'), reason: 'again' })).rejects.toMatchObject({ code: 'CASE_CANCELLED' });
|
|
108
|
+
});
|
|
109
|
+
it('binds every write to the case context receipt and makes retry resumable', async () => {
|
|
110
|
+
const store = new MemoryCaseStore();
|
|
111
|
+
const facade = new DeliveryLineFacade({ store });
|
|
112
|
+
const created = await facade.createCase({ ...identity('create-context'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'Context bound' });
|
|
113
|
+
await expect(facade.answer(created.caseId, { ...identity('wrong-context'), contextReceipt: 'other', turn: 1, value: 'nope' })).rejects.toMatchObject({ code: 'MISSING_PREREQUISITE' });
|
|
114
|
+
await expect(facade.answer(created.caseId, { ...identity('wrong-execution'), executionId: 'other-execution', turn: 1, value: 'nope' })).rejects.toMatchObject({ code: 'MISSING_PREREQUISITE' });
|
|
115
|
+
await expect(facade.answer(created.caseId, { ...identity('wrong-actor'), actor: { id: 'other', kind: 'human' }, turn: 1, value: 'nope' })).rejects.toMatchObject({ code: 'UNAUTHORIZED_ROLE' });
|
|
116
|
+
const record = await store.get(created.caseId);
|
|
117
|
+
record.stage = 'failed';
|
|
118
|
+
record.tasks = [{ id: `${created.caseId}:task`, caseId: created.caseId, title: 'retry', attempt: 1, version: 0, baseRevision: 'main', dependsOn: [], allowedFiles: [], expectedChanges: [], verificationCommands: [], status: 'failed', evidence: [] }];
|
|
119
|
+
await store.save(record, record.version);
|
|
120
|
+
const retried = await facade.retryTask(created.caseId, { ...identity('retry-context'), taskId: `${created.caseId}:task` });
|
|
121
|
+
expect(retried.stage).toBe('executing');
|
|
122
|
+
expect(retried.taskSummaries[0].status).toBe('pending');
|
|
123
|
+
});
|
|
124
|
+
it('promotes security-sensitive cases to high risk and records approval receipts', async () => {
|
|
125
|
+
const store = new MemoryCaseStore();
|
|
126
|
+
const facade = new DeliveryLineFacade({ store, approval: { approve: async (input) => input, reject: async (input) => input } });
|
|
127
|
+
const status = await facade.createCase({ ...identity('create-risk'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'scope guard', riskSignals: ['authorization scope'] });
|
|
128
|
+
expect((await store.get(status.caseId)).riskTier).toBe('high');
|
|
129
|
+
await facade.answer(status.caseId, { ...identity('risk-answer'), turn: 1, value: 'criteria' });
|
|
130
|
+
const approved = await facade.approve(status.caseId, { ...identity('risk-intent'), stage: 'intent' });
|
|
131
|
+
expect(approved.evidenceRefs.some(item => item.kind === 'approval')).toBe(true);
|
|
132
|
+
expect((await store.get(status.caseId)).evidence.some(item => item.kind === 'approval')).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
it('routes execution adapter failures to a failed task without losing the case state', async () => {
|
|
135
|
+
const store = new MemoryCaseStore();
|
|
136
|
+
const facade = new DeliveryLineFacade({ store, execution: { startTask: async () => { throw new Error('worker unavailable'); }, cancelTask: async () => { } }, verification: { verify: async () => { throw new Error('must not run'); } }, integration: { integrate: async () => ({ candidateRevision: 'never' }) } });
|
|
137
|
+
let view = await facade.createCase({ ...identity('create-exec-failure'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'worker failure' });
|
|
138
|
+
view = await facade.answer(view.caseId, { ...identity('answer-exec-failure'), turn: 1, value: 'criteria' });
|
|
139
|
+
view = await facade.approve(view.caseId, { ...identity('intent-exec-failure'), stage: 'intent' });
|
|
140
|
+
view = await facade.compareOptions(view.caseId, { ...identity('compare-exec-failure'), option: 'design' });
|
|
141
|
+
view = await facade.approve(view.caseId, { ...identity('spec-exec-failure'), stage: 'spec' });
|
|
142
|
+
view = await facade.approve(view.caseId, { ...identity('plan-exec-failure'), stage: 'plan' });
|
|
143
|
+
view = await facade.resume(view.caseId, identity('resume-exec-failure'));
|
|
144
|
+
expect(view.stage).toBe('failed');
|
|
145
|
+
expect(view.taskSummaries[0].status).toBe('failed');
|
|
146
|
+
expect(view.nextActions).toContain('retry_task');
|
|
147
|
+
});
|
|
148
|
+
it('keeps high-risk gates open until each required approval role is present', async () => {
|
|
149
|
+
const store = new MemoryCaseStore();
|
|
150
|
+
const roles = { intent: ['product_owner'], spec: ['product_owner', 'tech_lead', 'security_owner'], plan: ['tech_lead', 'security_owner'], release: ['code_owner', 'security_owner'] };
|
|
151
|
+
const approval = { requiredRoles: ({ stage }) => roles[stage] ?? [], approve: async (input) => input, reject: async (input) => input };
|
|
152
|
+
const facade = new DeliveryLineFacade({ store, approval });
|
|
153
|
+
const owner = (role, key) => ({ ...identity(key), actor: { id: role, kind: 'human', role } });
|
|
154
|
+
let view = await facade.createCase({ ...owner('product_owner', 'create-high'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: 'high risk', riskTier: 'high' });
|
|
155
|
+
view = await facade.answer(view.caseId, { ...owner('product_owner', 'answer-high'), turn: 1, value: 'criteria' });
|
|
156
|
+
view = await facade.approve(view.caseId, { ...owner('product_owner', 'intent-high'), stage: 'intent' });
|
|
157
|
+
view = await facade.compareOptions(view.caseId, { ...owner('product_owner', 'compare-high'), option: 'safe' });
|
|
158
|
+
view = await facade.approve(view.caseId, { ...owner('product_owner', 'spec-product'), stage: 'spec' });
|
|
159
|
+
expect(view.stage).toBe('spec_ready');
|
|
160
|
+
expect(view.nextActions).toContain('approve');
|
|
161
|
+
view = await facade.approve(view.caseId, { ...owner('tech_lead', 'spec-tech'), stage: 'spec' });
|
|
162
|
+
expect(view.stage).toBe('spec_ready');
|
|
163
|
+
view = await facade.approve(view.caseId, { ...owner('security_owner', 'spec-security'), stage: 'spec' });
|
|
164
|
+
expect(view.stage).toBe('plan_ready');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
//# sourceMappingURL=facade-e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facade-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/facade-e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend-scenario.e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/frontend-scenario.e2e.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DeliveryLineFacade } from '../facade/delivery-line-facade.js';
|
|
3
|
+
import { MemoryCaseStore } from '../persistence/memory-store.js';
|
|
4
|
+
import { validateTaskDag } from '../domain/task-dag.js';
|
|
5
|
+
const id = (key) => ({ actor: { id: 'frontend-owner', kind: 'human' }, sessionId: 'frontend-session', executionId: 'frontend-execution', idempotencyKey: key, contextReceipt: 'frontend-ctx', turn: 1 });
|
|
6
|
+
describe('frontend developer journey', () => {
|
|
7
|
+
it('keeps scope and verification evidence visible through delivery', async () => {
|
|
8
|
+
const store = new MemoryCaseStore();
|
|
9
|
+
const facade = new DeliveryLineFacade({ store, execution: { startTask: async (input) => ({ taskId: input.taskId, status: 'started' }), cancelTask: async () => { } }, verification: { verify: async (input) => ({ id: 'v', taskId: input.taskId, commit: 'candidate', lockfileHash: 'lock', runtime: 'node', toolVersion: 'vitest', environment: 'dashboard', commands: [], passed: true, createdAt: Date.now() }) }, integration: { integrate: async (input) => ({ candidateRevision: input.candidateRevision }) }, review: { review: async (input) => ({ id: String(input.kind), kind: input.kind, artifactHash: 'review', findings: [], passed: true, createdAt: Date.now() }) }, package: { prepare: async () => ({ packageHash: 'hash', path: '/tmp/frontend-delivery.json' }) } });
|
|
10
|
+
const request = '在 apps/dashboard 的 Runs 页面增加按项目和运行状态筛选;筛选条件写入 URL,保留现有 API 客户端和页面的 loading/empty/error 体验;补充测试,不修改 Server。';
|
|
11
|
+
let view = await facade.createCase({ ...id('create'), projectRoot: '/repo', baseRef: 'main', targetRef: 'main', title: request });
|
|
12
|
+
expect(view.pendingDecisions[0]?.question).toContain('outcome');
|
|
13
|
+
view = await facade.answer(view.caseId, { ...id('answer'), turn: 1, value: 'Project list comes from the existing API; refresh and share preserve query.' });
|
|
14
|
+
view = await facade.approve(view.caseId, { ...id('intent'), stage: 'intent' });
|
|
15
|
+
view = await facade.compareOptions(view.caseId, { ...id('compare'), option: 'URL query state' });
|
|
16
|
+
view = await facade.approve(view.caseId, { ...id('spec'), stage: 'spec' });
|
|
17
|
+
view = await facade.approve(view.caseId, { ...id('plan'), stage: 'plan' });
|
|
18
|
+
view = await facade.resume(view.caseId, id('resume'));
|
|
19
|
+
expect(view.stage).toBe('reviewed');
|
|
20
|
+
expect(view.taskSummaries[0]).toMatchObject({ status: 'integrated', attempt: 1 });
|
|
21
|
+
expect(view.nextActions).toContain('approve');
|
|
22
|
+
const tasks = (await store.get(view.caseId)).tasks;
|
|
23
|
+
expect(tasks.every(task => !task.allowedFiles.some(file => file.startsWith('apps/server/')))).toBe(true);
|
|
24
|
+
const browserEvidence = { kind: 'verification', hash: 'browser-unavailable', id: 'browser-1', createdAt: Date.now(), metadata: { channel: 'browser', status: 'unavailable' } };
|
|
25
|
+
const record = await store.get(view.caseId);
|
|
26
|
+
record.evidence.push(browserEvidence);
|
|
27
|
+
await store.save(record, record.version);
|
|
28
|
+
expect((await store.get(view.caseId)).evidence.find(item => item.id === 'browser-1')?.metadata).toMatchObject({ status: 'unavailable' });
|
|
29
|
+
const manifestRecord = await store.get(view.caseId);
|
|
30
|
+
expect(manifestRecord.artifacts.map(item => item.type)).toEqual(['intent', 'spec', 'plan']);
|
|
31
|
+
expect(manifestRecord.tasks[0].allowedFiles.every(file => !file.startsWith('apps/server/'))).toBe(true);
|
|
32
|
+
expect(() => validateTaskDag([{ ...tasks[0], id: 'f1-task', allowedFiles: ['apps/dashboard/src/**'], status: 'pending' }])).not.toThrow();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=frontend-scenario.e2e.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontend-scenario.e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/frontend-scenario.e2e.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-artifact-store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/git-artifact-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { GitArtifactStore } from '../artifacts/git-artifact-store.js';
|
|
7
|
+
function git(root, args) { return execFileSync('git', ['-C', root, ...args], { encoding: 'utf8' }).trim(); }
|
|
8
|
+
describe('Git artifact store', () => {
|
|
9
|
+
it('writes immutable artifacts under a delivery-only ref without changing HEAD', async () => {
|
|
10
|
+
const root = mkdtempSync(join(tmpdir(), 'delivery-artifact-'));
|
|
11
|
+
git(root, ['init', '-q']);
|
|
12
|
+
git(root, ['config', 'user.email', 'test@example.com']);
|
|
13
|
+
git(root, ['config', 'user.name', 'Delivery Test']);
|
|
14
|
+
writeFileSync(join(root, 'README.md'), 'base\n');
|
|
15
|
+
git(root, ['add', 'README.md']);
|
|
16
|
+
git(root, ['commit', '-qm', 'base']);
|
|
17
|
+
const head = git(root, ['rev-parse', 'HEAD']);
|
|
18
|
+
const store = new GitArtifactStore();
|
|
19
|
+
const artifact = await store.create({ caseId: 'case-1', repoId: 'repo-1', repositoryPath: root, type: 'intent', path: 'intent.md', content: '# Intent\n', parentBlobHashes: [], createdBy: { id: 'u', kind: 'human' } });
|
|
20
|
+
expect(git(root, ['rev-parse', 'HEAD'])).toBe(head);
|
|
21
|
+
expect(git(root, ['rev-parse', artifact.ref])).toBe(artifact.commit);
|
|
22
|
+
expect(git(root, ['show', `${artifact.commit}:${artifact.path}`])).toBe('# Intent');
|
|
23
|
+
expect(() => readFileSync(join(root, 'intent.md'))).toThrow();
|
|
24
|
+
await expect(store.create({ caseId: 'case-1', repoId: 'repo-1', repositoryPath: root, type: 'spec', path: '../escape.md', content: 'bad', parentBlobHashes: [artifact.blobHash], createdBy: { id: 'agent', kind: 'agent', role: 'designer' } })).rejects.toThrow('escapes');
|
|
25
|
+
});
|
|
26
|
+
it('chains parent hashes, rejects dirty or mutable writes, and keeps code worktrees isolated', async () => {
|
|
27
|
+
const root = mkdtempSync(join(tmpdir(), 'delivery-artifact-chain-'));
|
|
28
|
+
git(root, ['init', '-q']);
|
|
29
|
+
git(root, ['config', 'user.email', 'test@example.com']);
|
|
30
|
+
git(root, ['config', 'user.name', 'Delivery Test']);
|
|
31
|
+
writeFileSync(join(root, 'README.md'), 'base\n');
|
|
32
|
+
git(root, ['add', 'README.md']);
|
|
33
|
+
git(root, ['commit', '-qm', 'base']);
|
|
34
|
+
const store = new GitArtifactStore();
|
|
35
|
+
const first = await store.create({ caseId: 'case-2', repoId: 'repo-2', repositoryPath: root, type: 'intent', path: 'intent.md', content: 'one', parentBlobHashes: [], createdBy: { id: 'u', kind: 'human' } });
|
|
36
|
+
const second = await store.create({ caseId: 'case-2', repoId: 'repo-2', repositoryPath: root, type: 'spec', path: 'spec.md', content: 'two', parentBlobHashes: [first.blobHash], createdBy: { id: 'u', kind: 'human' } });
|
|
37
|
+
expect(second.commit).not.toBe(first.commit);
|
|
38
|
+
expect(git(root, ['show', `${second.commit}:intent.md`])).toBe('one');
|
|
39
|
+
await expect(store.create({ caseId: 'case-2', repoId: 'repo-2', repositoryPath: root, type: 'plan', path: 'plan.md', content: 'three', parentBlobHashes: [], createdBy: { id: 'u', kind: 'human' } })).rejects.toThrow('parent blob');
|
|
40
|
+
writeFileSync(join(root, 'dirty.txt'), 'uncommitted');
|
|
41
|
+
await expect(store.create({ caseId: 'case-2', repoId: 'repo-2', repositoryPath: root, type: 'plan', path: 'plan.md', content: 'three', parentBlobHashes: [first.blobHash, second.blobHash], createdBy: { id: 'u', kind: 'human' } })).rejects.toThrow('clean');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=git-artifact-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-artifact-store.test.js","sourceRoot":"","sources":["../../src/__tests__/git-artifact-store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"governance-adapters.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/governance-adapters.test.ts"],"names":[],"mappings":""}
|