@ngocsangairvds/vsaf 5.4.6 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/cli/dist/commands/run.d.ts +2 -0
- package/packages/cli/dist/commands/run.d.ts.map +1 -1
- package/packages/cli/dist/commands/run.js +23 -5
- package/packages/cli/dist/commands/run.js.map +1 -1
- package/packages/cli/dist/commands/validate.d.ts.map +1 -1
- package/packages/cli/dist/commands/validate.js +7 -1
- package/packages/cli/dist/commands/validate.js.map +1 -1
- package/packages/cli/dist/server/app.d.ts.map +1 -1
- package/packages/cli/dist/server/app.js +2 -0
- package/packages/cli/dist/server/app.js.map +1 -1
- package/packages/cli/dist/server/routes/config.d.ts +6 -0
- package/packages/cli/dist/server/routes/config.d.ts.map +1 -0
- package/packages/cli/dist/server/routes/config.js +64 -0
- package/packages/cli/dist/server/routes/config.js.map +1 -0
- package/packages/cli/dist/server/services/execution-manager.d.ts.map +1 -1
- package/packages/cli/dist/server/services/execution-manager.js +10 -6
- package/packages/cli/dist/server/services/execution-manager.js.map +1 -1
- package/packages/core/dist/config/config-loader.d.ts +4 -0
- package/packages/core/dist/config/config-loader.d.ts.map +1 -1
- package/packages/core/dist/config/config-loader.js +5 -0
- package/packages/core/dist/config/config-loader.js.map +1 -1
- package/packages/core/dist/engine/dag-executor.d.ts +4 -0
- package/packages/core/dist/engine/dag-executor.d.ts.map +1 -1
- package/packages/core/dist/engine/dag-executor.js +7 -2
- package/packages/core/dist/engine/dag-executor.js.map +1 -1
- package/packages/core/dist/engine/dag-parser.d.ts.map +1 -1
- package/packages/core/dist/engine/dag-parser.js +8 -0
- package/packages/core/dist/engine/dag-parser.js.map +1 -1
- package/packages/core/dist/engine/expand-workflow.d.ts +8 -0
- package/packages/core/dist/engine/expand-workflow.d.ts.map +1 -0
- package/packages/core/dist/engine/expand-workflow.js +104 -0
- package/packages/core/dist/engine/expand-workflow.js.map +1 -0
- package/packages/core/dist/index.d.ts +1 -0
- package/packages/core/dist/index.d.ts.map +1 -1
- package/packages/core/dist/index.js +4 -2
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/providers/default-adapters.d.ts +2 -1
- package/packages/core/dist/providers/default-adapters.d.ts.map +1 -1
- package/packages/core/dist/providers/default-adapters.js +83 -26
- package/packages/core/dist/providers/default-adapters.js.map +1 -1
- package/packages/core/dist/schema/workflow-schema.d.ts +8 -0
- package/packages/core/dist/schema/workflow-schema.d.ts.map +1 -1
- package/packages/core/dist/schema/workflow-schema.js +9 -6
- package/packages/core/dist/schema/workflow-schema.js.map +1 -1
- package/packages/core/dist/types/dag.d.ts +2 -0
- package/packages/core/dist/types/dag.d.ts.map +1 -1
- package/packages/core/dist/types/provider.d.ts +2 -0
- package/packages/core/dist/types/provider.d.ts.map +1 -1
- package/packages/core/dist/types/workflow.d.ts +4 -0
- package/packages/core/dist/types/workflow.d.ts.map +1 -1
- package/packages/web/dist/web/3rdpartylicenses.txt +52 -52
- package/packages/web/dist/web/browser/chunk-3LPWUDNW.js +1 -0
- package/packages/web/dist/web/browser/chunk-4LHJGOIU.js +1 -0
- package/packages/web/dist/web/browser/chunk-FQUOTTEA.js +4 -0
- package/packages/web/dist/web/browser/chunk-FWLQWWZ3.js +1 -0
- package/packages/web/dist/web/browser/chunk-K4VRVZWF.js +1 -0
- package/packages/web/dist/web/browser/chunk-OBMUVQWL.js +1 -0
- package/packages/web/dist/web/browser/chunk-UJHVWNPU.js +1 -0
- package/packages/web/dist/web/browser/chunk-UXRT7NQK.js +1 -0
- package/packages/web/dist/web/browser/chunk-VJ3UNUDS.js +1 -0
- package/packages/web/dist/web/browser/chunk-W7ZKECPT.js +5 -0
- package/packages/web/dist/web/browser/index.html +1 -1
- package/packages/web/dist/web/browser/main-7FONYLE6.js +1 -0
- package/skills/sdlc/_shared/sonar-viettel/quality-gate.md +7 -0
- package/skills/sdlc/discovery/grill/step.md +6 -1
- package/skills/sdlc/implement/build-run/step.md +1 -1
- package/skills/sdlc/implement/tdd/step.md +9 -0
- package/skills/sdlc/pack.yaml +2 -4
- package/skills/sdlc/review/fix/SKILL.md +10 -0
- package/skills/sdlc/review/fix/step.md +33 -0
- package/skills/sdlc/srs/SKILL.md +129 -0
- package/skills/sdlc/test-design/cases/step.md +7 -0
- package/workflows/sdlc/master-sdlc-headless.yaml +18 -62
- package/workflows/sdlc/sdlc-build-headless.yaml +41 -12
- package/workflows/sdlc/sdlc-design.yaml +4 -21
- package/workflows/sdlc/sdlc-review.yaml +9 -1
- package/workflows/sdlc/sdlc-srs.yaml +4 -25
- package/packages/web/dist/web/browser/chunk-2JHYGCCN.js +0 -1
- package/packages/web/dist/web/browser/chunk-BMQT4ZPK.js +0 -1
- package/packages/web/dist/web/browser/chunk-GWHJRWJM.js +0 -1
- package/packages/web/dist/web/browser/chunk-OZ6BRCXC.js +0 -1
- package/packages/web/dist/web/browser/chunk-Q7N6DAKT.js +0 -1
- package/packages/web/dist/web/browser/chunk-SEBU2SK7.js +0 -1
- package/packages/web/dist/web/browser/chunk-SOS4TH5D.js +0 -4
- package/packages/web/dist/web/browser/chunk-WSAVMPNK.js +0 -5
- package/packages/web/dist/web/browser/main-UPZIVGM6.js +0 -1
- package/skills/sdlc/srs/interface/SKILL.md +0 -10
- package/skills/sdlc/srs/interface/step.md +0 -44
- package/skills/sdlc/srs/prereq/SKILL.md +0 -10
- package/skills/sdlc/srs/prereq/step.md +0 -19
- package/skills/sdlc/srs/validate/SKILL.md +0 -10
- package/skills/sdlc/srs/validate/step.md +0 -23
- package/skills/sdlc/srs/write/SKILL.md +0 -10
- package/skills/sdlc/srs/write/step.md +0 -19
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as S,c as P,e as M,f as O,g as A}from"./chunk-FWLQWWZ3.js";import{Ba as f,La as x,Ma as u,O as d,Oa as C,Pa as v,Qa as s,Ra as n,Rb as j,Sa as o,Sb as L,T as m,Ta as h,U as g,Xa as b,Ya as k,Za as c,fb as i,gb as _,nb as y,qa as a,yb as w}from"./chunk-W7ZKECPT.js";var E=[{path:"",loadComponent:()=>import("./chunk-3LPWUDNW.js").then(e=>e.DashboardComponent)},{path:"workflows",loadComponent:()=>import("./chunk-OBMUVQWL.js").then(e=>e.WorkflowsComponent)},{path:"workflows/builder",loadComponent:()=>import("./chunk-FQUOTTEA.js").then(e=>e.WorkflowBuilderComponent)},{path:"workflows/detail",loadComponent:()=>import("./chunk-4LHJGOIU.js").then(e=>e.WorkflowDetailComponent)},{path:"conversations",loadComponent:()=>import("./chunk-UJHVWNPU.js").then(e=>e.ConversationsComponent)},{path:"conversations/:conversationId",loadComponent:()=>import("./chunk-UJHVWNPU.js").then(e=>e.ConversationsComponent)},{path:"runs/:runId",loadComponent:()=>import("./chunk-UXRT7NQK.js").then(e=>e.RunExecutionComponent)},{path:"settings",loadComponent:()=>import("./chunk-VJ3UNUDS.js").then(e=>e.SettingsComponent)}];var I={providers:[w({eventCoalescing:!0}),A(E),j()]};var R=()=>({exact:!0}),T=(e,r)=>r.id;function V(e,r){if(e&1&&(n(0,"option",14),i(1),o()),e&2){let t=r.$implicit,p=c(2);s("value",t.id)("selected",t.id===p.selectedId()),a(),_(t.name)}}function F(e,r){if(e&1){let t=b();n(0,"select",13),k("change",function(z){m(t);let D=c();return g(D.onProjectChange(z))}),C(1,V,2,3,"option",14,T),o()}if(e&2){let t=c();a(),v(t.projects())}}var l=class e{projectService=d(L);projects=this.projectService.projects;selectedId=this.projectService.selectedId;constructor(){this.projectService.load()}onProjectChange(r){let t=r.target.value;!t||t===this.selectedId()||(this.projectService.select(t),document.location.reload())}static \u0275fac=function(t){return new(t||e)};static \u0275cmp=f({type:e,selectors:[["app-root"]],decls:21,vars:3,consts:[[1,"app-shell"],[1,"app-nav"],["routerLink","/",1,"brand-link"],[1,"brand-icon"],[1,"brand-name"],["routerLink","/","routerLinkActive","border-primary text-primary",1,"nav-link",3,"routerLinkActiveOptions"],["routerLink","/workflows","routerLinkActive","border-primary text-primary",1,"nav-link"],["routerLink","/conversations","routerLinkActive","border-primary text-primary",1,"nav-link"],["routerLink","/settings","routerLinkActive","border-primary text-primary",1,"nav-link"],[1,"nav-spacer"],["aria-label","Project","title","Switch project",1,"project-select"],[1,"version-pill"],[1,"app-main"],["aria-label","Project","title","Switch project",1,"project-select",3,"change"],[3,"value","selected"]],template:function(t,p){t&1&&(n(0,"div",0)(1,"nav",1)(2,"a",2)(3,"span",3),i(4,"V"),o(),n(5,"span",4),i(6,"VSAF"),o()(),n(7,"a",5),i(8," Dashboard "),o(),n(9,"a",6),i(10," Workflows "),o(),n(11,"a",7),i(12," Conversations "),o(),n(13,"a",8),i(14," Settings "),o(),n(15,"div",9),x(16,F,3,0,"select",10),n(17,"span",11),i(18,"v0.2.0"),o()()(),n(19,"main",12),h(20,"router-outlet"),o()()),t&2&&(a(7),s("routerLinkActiveOptions",y(2,R)),a(9),u(p.projects().length>1?16:-1))},dependencies:[P,M,O],styles:["[_nghost-%COMP%]{display:block;min-height:100vh;background:#101114;color:#e7e7ea}.app-shell[_ngcontent-%COMP%]{min-height:100vh;background:#101114;color:#e7e7ea}.app-nav[_ngcontent-%COMP%]{display:flex;align-items:center;gap:4px;height:56px;border-bottom:1px solid #2b2d33;background:#17181d;padding:0 16px;box-sizing:border-box}.brand-link[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:9px;margin-right:18px;color:#f4f4f5;text-decoration:none}.brand-link[_ngcontent-%COMP%]:hover{opacity:.82}.brand-icon[_ngcontent-%COMP%]{display:grid;place-items:center;width:30px;height:30px;border-radius:7px;background:#6366f1;color:#fff;font-size:14px;font-weight:800}.brand-name[_ngcontent-%COMP%]{font-size:14px;font-weight:800}.nav-link[_ngcontent-%COMP%]{display:inline-flex;align-items:center;height:56px;border-bottom:2px solid transparent;padding:0 13px;color:#a1a1aa;font-size:14px;font-weight:650;text-decoration:none;box-sizing:border-box}.nav-link[_ngcontent-%COMP%]:hover{color:#f4f4f5}.nav-link.border-primary[_ngcontent-%COMP%], .nav-link.text-primary[_ngcontent-%COMP%]{border-bottom-color:#6366f1;color:#a5b4fc}.nav-spacer[_ngcontent-%COMP%]{margin-left:auto;display:flex;align-items:center;gap:12px}.project-select[_ngcontent-%COMP%]{border:1px solid #2b2d33;border-radius:7px;background:#20222a;color:#e7e7ea;padding:5px 8px;font:inherit;font-size:12px;max-width:220px;cursor:pointer}.project-select[_ngcontent-%COMP%]:focus{outline:none;border-color:#6366f1}.version-pill[_ngcontent-%COMP%]{border:1px solid #2b2d33;border-radius:7px;background:#20222a;color:#a1a1aa;padding:5px 8px;font-size:12px}.app-main[_ngcontent-%COMP%]{min-height:calc(100vh - 56px);overflow:auto;background:#101114}"]})};S(l,I).catch(e=>console.error(e));
|
|
@@ -72,3 +72,10 @@ Run from this folder (`.claude/skills/_shared/sdlc/sonar-viettel/`). It prints e
|
|
|
72
72
|
## Phase-specific overrides
|
|
73
73
|
|
|
74
74
|
- **Hotfix (`/sdlc-hotfix-implement`, `/sdlc-hotfix-review`):** apply the **New Code** conditions on changed files only, but tighten **Code Smells (new) = 0** — a surgical 1–3 line fix has no excuse to introduce a smell.
|
|
75
|
+
|
|
76
|
+
## Framework correctness (MUST verify — beyond Sonar smells)
|
|
77
|
+
|
|
78
|
+
These are behavioural defects Sonar's generic ruleset misses; the implement phase MUST satisfy them and the review phase MUST confirm them:
|
|
79
|
+
|
|
80
|
+
- **Validation is wired, not just declared.** If a request DTO carries bean-validation constraints (`@NotNull`, `@Min`, `@Size`, `@FutureOrPresent`, …), the controller parameter MUST activate them (Spring `@Valid`/`@Validated` on `@RequestBody`/`@ModelAttribute`) so violations return **HTTP 400**. Constraints without an activating annotation are dead code — treat as a **MUST FIX**. Confirm with a real HTTP-layer test asserting 400 on invalid input (not a service mock).
|
|
81
|
+
- **Concurrency on state transitions.** A check-then-act state change on a shared row (e.g. "if not PAID → set PAID") MUST be race-safe — optimistic locking (`@Version`) or an atomic conditional update — when the PRD/SRS flags a concurrency risk. A bare read-modify-write is a **MUST FIX** for such endpoints.
|
|
@@ -15,7 +15,12 @@ Use `/bmad-advanced-elicitation` — grill questions BASED ON system understandi
|
|
|
15
15
|
- ✅ Correct example: "Route `/api/orders` currently returns 5 fields. What additional fields does the new feature need?"
|
|
16
16
|
- ⛔ Wrong example: "I suggest using event-driven architecture for this feature"
|
|
17
17
|
|
|
18
|
-
**
|
|
18
|
+
**REQUIREMENT FIDELITY (critical — applies especially in headless/self-answer mode):**
|
|
19
|
+
- Every capability explicitly stated in the feature request (`$ARGUMENTS`) is a **binding requirement** and MUST remain in scope. Do NOT answer a grill question in a way that removes, weakens, or narrows an explicit requirement.
|
|
20
|
+
- Example of the failure to avoid: the request says "payment updates status PENDING→PAID on success **or PENDING→FAILED on failure**", and a self-answer decides "mock always succeeds" — this silently DROPS the FAILED path. Not allowed.
|
|
21
|
+
- If a requirement genuinely should be reduced or deferred, do NOT bake the reduction into the design. Instead record it verbatim under a **`## Descoped — needs approval`** section in `01-discovery.md` (original requirement + why proposed for descope) and keep it flagged for a human decision. Absent explicit approval, the requirement stays IN scope and flows to PRD/SRS/implementation.
|
|
22
|
+
|
|
23
|
+
**WAIT** for the user to respond BEFORE continuing to Step 4. In headless/self-answer mode (`VSAF_CI=1`), answer conservatively per the fidelity rule above — never self-approve a descope.
|
|
19
24
|
|
|
20
25
|
```
|
|
21
26
|
[DISCOVERY] [3/6] Grill session — asking detailed questions... ✅
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
- **Recommend Docker**: "Recommend using Docker to ensure a consistent environment. Create a Dockerfile if one does not exist."
|
|
25
25
|
- If Docker available → prefer building in container
|
|
26
26
|
|
|
27
|
-
4. **Test suite run**: `
|
|
27
|
+
4. **Test suite run**: run the **FULL** suite (`mvn test` / `npm test` / equivalent — never a subset) → all tests PASS. Report the runner's real total honestly (e.g. `Tests run: N, Failures: F, Errors: E`) — the number you report MUST match the full-suite run. A red build is a **hard gate**: `Failures+Errors` must be 0, or any genuinely-unrelated pre-existing failure must be explicitly quarantined (recorded with test name + rationale). Do NOT report "all pass" from a partial run.
|
|
28
28
|
|
|
29
29
|
5. **Final SonarQube sweep**: Scan all new code one last time before exiting
|
|
30
30
|
- Confirm both New Code and Overall Code conditions are green per the shared `quality-gate.md` (single source of truth)
|
|
@@ -14,6 +14,15 @@ For each test case in `06-testcases.md`:
|
|
|
14
14
|
- No code smells (long method, deep nesting)
|
|
15
15
|
- No duplicated code
|
|
16
16
|
- Null-safe, proper error handling
|
|
17
|
+
- ⚠️ **Wire request validation, don't just declare it**: if a request DTO carries
|
|
18
|
+
bean-validation constraints (`@NotNull`, `@Min`, `@Size`, `@FutureOrPresent`, …),
|
|
19
|
+
the controller parameter MUST activate them — Spring: `@Valid`/`@Validated` on the
|
|
20
|
+
`@RequestBody`/`@ModelAttribute` param — so a violation returns **HTTP 400**.
|
|
21
|
+
Unwired constraints are dead code and a defect. Prove it with the endpoint's
|
|
22
|
+
invalid-input integration test (expects 400), never a service-level mock.
|
|
23
|
+
- ⚠️ **Controller cases use a real HTTP-layer test** (`@WebMvcTest`/`@SpringBootTest`
|
|
24
|
+
+ `MockMvc`), exercising validation (400) and authz (401/403) through the real
|
|
25
|
+
stack — not Mockito on the service.
|
|
17
26
|
3. **REFACTOR**: Clean code, do not change behavior
|
|
18
27
|
- Refactor MUST improve SonarQube metrics, not degrade them
|
|
19
28
|
|
package/skills/sdlc/pack.yaml
CHANGED
|
@@ -68,10 +68,7 @@ skills:
|
|
|
68
68
|
- architecture/decision
|
|
69
69
|
- architecture/epics
|
|
70
70
|
- architecture/readiness
|
|
71
|
-
- srs
|
|
72
|
-
- srs/write
|
|
73
|
-
- srs/validate
|
|
74
|
-
- srs/interface
|
|
71
|
+
- srs
|
|
75
72
|
- test-design/prereq
|
|
76
73
|
- test-design/cases
|
|
77
74
|
- test-design/boundary
|
|
@@ -89,6 +86,7 @@ skills:
|
|
|
89
86
|
- review/code
|
|
90
87
|
- review/impact
|
|
91
88
|
- review/debate
|
|
89
|
+
- review/fix
|
|
92
90
|
- complete/prereq
|
|
93
91
|
- complete/context
|
|
94
92
|
- complete/knowledge
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
### Step 6: Implement Review Fixes (close the loop)
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
[REVIEW] [6/6] Implementing MUST-FIX findings + re-verifying... ⏳
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
A review that only *reports* defects but leaves them in the shipped code is worthless. This step makes review **binding**: every blocking finding is fixed and re-verified before the feature can advance.
|
|
8
|
+
|
|
9
|
+
1. **Collect blocking findings:**
|
|
10
|
+
- From `08-review.md` §Code Review / §Quality Gate — every finding classified **MUST FIX**.
|
|
11
|
+
- Carry-forward: any still-open **HIGH** adversarial finding from `02-prd.md` (§Adversarial) and `03-adr.md` (e.g. concurrency/race, data-isolation) — treat as MUST FIX.
|
|
12
|
+
2. **Fix each MUST-FIX in the real project source.** Prefer TDD: add/adjust a test that fails on the defect, then fix. Follow the implementation standards — including the framework-correctness rules in `quality-gate.md` (wire `@Valid`/`@Validated` so invalid input returns 400; make check-then-act state transitions race-safe, e.g. `@Version`).
|
|
13
|
+
3. **Re-verify the FULL suite:** `sh $ARTIFACTS_DIR/run-tests.sh 2>&1 | tee $ARTIFACTS_DIR/test-output.log`. The suite MUST be GREEN (0 failures, 0 errors). Fix until it is — never leave the suite red or a MUST-FIX unresolved.
|
|
14
|
+
4. **Re-check the quality gate** (`quality-gate.md`) on the changed files.
|
|
15
|
+
5. **Update `08-review.md`:** mark each finding `✅ Fixed` (name the file/change) or — only with explicit human approval — `⏸ Deferred (rationale)`. An unresolved MUST-FIX blocks the phase.
|
|
16
|
+
|
|
17
|
+
**Headless / `VSAF_CI=1`:** implement every MUST-FIX autonomously; do NOT self-defer a MUST-FIX. If a fix is genuinely impossible, leave it open and report — the phase fails rather than shipping a known MUST-FIX defect.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
[REVIEW] [6/6] Implementing MUST-FIX findings + re-verifying... ✅
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Output
|
|
24
|
+
|
|
25
|
+
- Code changes resolving all MUST-FIX findings; full suite green.
|
|
26
|
+
- `08-review.md` updated with per-finding resolution status.
|
|
27
|
+
|
|
28
|
+
## Rules
|
|
29
|
+
|
|
30
|
+
- A MUST-FIX finding MUST NOT ship unresolved — this node is what makes the review binding, not advisory.
|
|
31
|
+
- Re-run the COMPLETE suite after fixes; green = 0 failures/errors (honor any quarantine entries recorded during implement).
|
|
32
|
+
- Keep fixes minimal and scoped to the findings — do not add unrequested scope.
|
|
33
|
+
- ⛔ DO NOT modify `graphify-out/` or `.gitnexus/`
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sdlc-srs
|
|
3
|
+
description: "Phase 4 — SRS via sdlc-write-srs template + sdlc-validate-srs 8-dimension validation + GitNexus interface verification"
|
|
4
|
+
version: 2.0.0
|
|
5
|
+
author: "@ngocsangairvds/vsaf"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Phase 4: SRS (Software Requirements Specification)
|
|
9
|
+
|
|
10
|
+
You are a Senior Business Analyst. Your mission: write a detailed SRS using the v4 template, validate it across 8 quality dimensions, and verify interface contracts.
|
|
11
|
+
|
|
12
|
+
## Language Rule
|
|
13
|
+
|
|
14
|
+
All output documents (05-srs.md) MUST be written in Vietnamese. Section headers, requirements, use cases, analysis — all in Vietnamese. Only keep English for: IDs (FR-001, NFR-001, UC-XX-01), technical terms (API, database, endpoint), and code references (class names, method signatures).
|
|
15
|
+
|
|
16
|
+
## Index Protection
|
|
17
|
+
|
|
18
|
+
⛔ **DO NOT modify** `graphify-out/` or `.gitnexus/` — READ only, never WRITE.
|
|
19
|
+
|
|
20
|
+
## Phase Entry Protocol
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
[SRS] [1/4] Checking input artifacts... ⏳
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
1. Input:
|
|
27
|
+
- `.vsaf/docs/features/{feature-name}/02-prd.md`
|
|
28
|
+
- `.vsaf/docs/features/{feature-name}/03-adr.md`
|
|
29
|
+
- `.vsaf/docs/features/{feature-name}/04-epics.md`
|
|
30
|
+
2. Verify 02-prd.md AND 03-adr.md exist (04-epics.md optional):
|
|
31
|
+
- Any required file missing → Ask: "File not found. Have you run Phase 2 (PRD) and Phase 3 (Architecture)?"
|
|
32
|
+
3. Read `.vsaf/docs/STATUS.md`
|
|
33
|
+
4. Read `CONTEXT.md`
|
|
34
|
+
5. Query `graph.json` for code structure + business mapping (if exists)
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
[SRS] [1/4] Checking input artifacts... ✅
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Tasks
|
|
41
|
+
|
|
42
|
+
### Step 2: Write SRS
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
[SRS] [2/4] Writing SRS from PRD + ADR + Epics... ⏳
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use `/sdlc-write-srs` — write SRS following the v4 template (12 sections) from PRD + Architecture + Epics.
|
|
49
|
+
|
|
50
|
+
Input:
|
|
51
|
+
- `02-prd.md` as PRD source
|
|
52
|
+
- `03-adr.md` as architecture context
|
|
53
|
+
- `04-epics.md` as epic breakdown (optional)
|
|
54
|
+
- Figma link from `CONTEXT.md` (optional)
|
|
55
|
+
|
|
56
|
+
Output: `.vsaf/docs/features/{feature-name}/05-srs.md`
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
[SRS] [2/4] Writing SRS from PRD + ADR + Epics... ✅
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 3: Validate SRS
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
[SRS] [3/4] Validating SRS (8 dimensions)... ⏳
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `/sdlc-validate-srs` — validate SRS across 8 dimensions with weighted scoring.
|
|
69
|
+
|
|
70
|
+
- Scope: single file (`05-srs.md`)
|
|
71
|
+
- Cross-validate against: `02-prd.md`, `03-adr.md`
|
|
72
|
+
- Output: `.vsaf/docs/features/{feature-name}/05-srs-validation-report.md`
|
|
73
|
+
|
|
74
|
+
**Decision handling:**
|
|
75
|
+
|
|
76
|
+
| Validation Result | Action |
|
|
77
|
+
|---|---|
|
|
78
|
+
| **GO** (≥80%, no CRITICAL) | Print score summary, proceed to Step 4 |
|
|
79
|
+
| **CONDITIONAL GO** (60-79% or HIGH findings) | Print warnings + finding summary. Ask user: "Proceed despite warnings? (Y/N)" |
|
|
80
|
+
| **NO-GO** (<60% or CRITICAL findings) | **STOP.** Print CRITICAL findings. Ask user to fix 05-srs.md and re-run `/sdlc-srs` |
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
[SRS] [3/4] Validating SRS (8 dimensions)... ✅
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Step 4: Interface Verification
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
[SRS] [4/4] Interface verification via GitNexus... ⏳
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use GitNexus `shape_check` — verify code structure matches SRS assumptions.
|
|
93
|
+
Append results to `05-srs.md` §Interface Contracts.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
[SRS] [4/4] Interface verification via GitNexus... ✅
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Gate
|
|
100
|
+
|
|
101
|
+
Human must approve SRS. Write `## Gate: APPROVED` at the end of the file.
|
|
102
|
+
|
|
103
|
+
**Automated mode (opt-in):** when the environment has `VSAF_CI=1` or `VSAF_AUTO_APPROVE=1`, write the Gate yourself as `## Gate: APPROVED (auto · VSAF_CI · rationale: <one line>)` and proceed — the one-line rationale is REQUIRED for audit. Without the env flag, a human must approve (default).
|
|
104
|
+
|
|
105
|
+
## Output
|
|
106
|
+
|
|
107
|
+
- `.vsaf/docs/features/{feature-name}/05-srs.md` — SRS document (v4 template format)
|
|
108
|
+
- `.vsaf/docs/features/{feature-name}/05-srs-validation-report.md` — Validation report with scores
|
|
109
|
+
|
|
110
|
+
## Phase Exit Protocol
|
|
111
|
+
|
|
112
|
+
In PHASE REPORT:
|
|
113
|
+
- Output files: `05-srs.md`, `05-srs-validation-report.md`
|
|
114
|
+
- Validation score: {overall_score}% ({GO/CONDITIONAL GO/NO-GO})
|
|
115
|
+
- Suggestions: [1] Phase 5 Test Design (`/sdlc-test-design`) [2] Save artifacts
|
|
116
|
+
|
|
117
|
+
Update `.vsaf/docs/STATUS.md`.
|
|
118
|
+
|
|
119
|
+
**Notify the user:**
|
|
120
|
+
> Artifacts have been saved. If the session is interrupted, open a new session and run the next phase — data is safe.
|
|
121
|
+
|
|
122
|
+
## Rules
|
|
123
|
+
|
|
124
|
+
- Every FR/NFR MUST have a unique traceability ID
|
|
125
|
+
- DO NOT use vague language: "should", "might", "optionally" → replace with "MUST", "MUST NOT"
|
|
126
|
+
- All terms MUST exist in CONTEXT.md
|
|
127
|
+
- Every FR MUST be testable (have clear pass/fail criteria)
|
|
128
|
+
- SRS MUST follow the v4 template structure (12 sections)
|
|
129
|
+
- ⛔ DO NOT modify `graphify-out/` or `.gitnexus/`
|
|
@@ -17,6 +17,13 @@ For each requirement, ensure ≥1 test case with:
|
|
|
17
17
|
|
|
18
18
|
Prioritize: happy path first, then error paths, then edge cases.
|
|
19
19
|
|
|
20
|
+
**API / controller coverage (MANDATORY — no exceptions):** every REST controller endpoint MUST have at least one **real framework HTTP-layer integration test**, not a service-level mock relabelled "integration". For Spring that means `@WebMvcTest`/`@SpringBootTest` + `MockMvc` (or `WebTestClient`) that sends a real HTTP request and asserts the HTTP response. Each endpoint's cases MUST include:
|
|
21
|
+
1. **Happy path** → 2xx + body shape.
|
|
22
|
+
2. **Invalid input** (null/blank/out-of-range field on the request DTO) → **HTTP 400** (this fails unless bean validation is actually wired — that is the point).
|
|
23
|
+
3. **Authorization** — unauthenticated → 401, wrong-owner/role → 403.
|
|
24
|
+
|
|
25
|
+
A test that only calls the service through Mockito does NOT satisfy the controller/integration requirement. Mark each case's **Type** honestly (`unit` = no framework context; `integration` = real MockMvc/HTTP).
|
|
26
|
+
|
|
20
27
|
```
|
|
21
28
|
[TEST-DESIGN] [2/4] Generating test cases from SRS... ✅
|
|
22
29
|
```
|
|
@@ -1,73 +1,29 @@
|
|
|
1
1
|
name: master-sdlc-headless
|
|
2
|
-
description: Full SDLC pipeline (headless implement) — Discovery → PRD → Architecture → SRS → Test Design → Implement (headless) → Review → Feature Complete → Ship
|
|
2
|
+
description: Full SDLC pipeline (headless implement, single-workspace) — Discovery → PRD → Architecture → SRS → Test Design → Implement (headless) → Review → Feature Complete → Ship
|
|
3
3
|
nodes:
|
|
4
4
|
- id: discovery
|
|
5
|
-
|
|
6
|
-
bash: |
|
|
7
|
-
vsaf run sdlc/sdlc-discovery --args $ARGUMENTS
|
|
8
|
-
workspace: false
|
|
9
|
-
timeout: 5400000
|
|
5
|
+
workflow: sdlc/sdlc-discovery
|
|
10
6
|
- id: prd
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
vsaf run sdlc/sdlc-prd --args $ARGUMENTS
|
|
14
|
-
workspace: false
|
|
15
|
-
timeout: 5400000
|
|
16
|
-
depends_on:
|
|
17
|
-
- discovery
|
|
7
|
+
workflow: sdlc/sdlc-prd
|
|
8
|
+
depends_on: [discovery]
|
|
18
9
|
- id: architecture
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
vsaf run sdlc/sdlc-architecture --args $ARGUMENTS
|
|
22
|
-
workspace: false
|
|
23
|
-
timeout: 5400000
|
|
24
|
-
depends_on:
|
|
25
|
-
- prd
|
|
10
|
+
workflow: sdlc/sdlc-architecture
|
|
11
|
+
depends_on: [prd]
|
|
26
12
|
- id: srs
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
vsaf run sdlc/sdlc-srs --args $ARGUMENTS
|
|
30
|
-
workspace: false
|
|
31
|
-
timeout: 5400000
|
|
32
|
-
depends_on:
|
|
33
|
-
- architecture
|
|
13
|
+
workflow: sdlc/sdlc-srs
|
|
14
|
+
depends_on: [architecture]
|
|
34
15
|
- id: test-design
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
vsaf run sdlc/sdlc-test-design --args $ARGUMENTS
|
|
38
|
-
workspace: false
|
|
39
|
-
timeout: 5400000
|
|
40
|
-
depends_on:
|
|
41
|
-
- srs
|
|
16
|
+
workflow: sdlc/sdlc-test-design
|
|
17
|
+
depends_on: [srs]
|
|
42
18
|
- id: implement
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
vsaf run sdlc/sdlc-build-headless --args $ARGUMENTS
|
|
46
|
-
workspace: false
|
|
47
|
-
timeout: 5400000
|
|
48
|
-
depends_on:
|
|
49
|
-
- test-design
|
|
19
|
+
workflow: sdlc/sdlc-build-headless
|
|
20
|
+
depends_on: [test-design]
|
|
50
21
|
- id: review
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
vsaf run sdlc/sdlc-review --args $ARGUMENTS
|
|
54
|
-
workspace: false
|
|
55
|
-
timeout: 5400000
|
|
56
|
-
depends_on:
|
|
57
|
-
- implement
|
|
22
|
+
workflow: sdlc/sdlc-review
|
|
23
|
+
depends_on: [implement]
|
|
58
24
|
- id: complete
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
vsaf run sdlc/sdlc-feature-complete --args $ARGUMENTS
|
|
62
|
-
workspace: false
|
|
63
|
-
timeout: 5400000
|
|
64
|
-
depends_on:
|
|
65
|
-
- review
|
|
25
|
+
workflow: sdlc/sdlc-feature-complete
|
|
26
|
+
depends_on: [review]
|
|
66
27
|
- id: ship
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
vsaf run sdlc/sdlc-ship --args $ARGUMENTS
|
|
70
|
-
workspace: false
|
|
71
|
-
timeout: 5400000
|
|
72
|
-
depends_on:
|
|
73
|
-
- complete
|
|
28
|
+
workflow: sdlc/sdlc-ship
|
|
29
|
+
depends_on: [complete]
|
|
@@ -12,8 +12,13 @@ nodes:
|
|
|
12
12
|
You are setting up a headless, engine-driven implementation of a feature.
|
|
13
13
|
Feature request: $ARGUMENTS
|
|
14
14
|
|
|
15
|
-
1.
|
|
16
|
-
|
|
15
|
+
1. Locate this feature's design docs from the earlier SDLC phases, then read them.
|
|
16
|
+
They may live under `docs/`, `.vsaf/docs/`, or `.vsaf/docs/features/*/` and the
|
|
17
|
+
filenames vary by project — search both trees for the SRS and the test cases, e.g.:
|
|
18
|
+
`ls -1 docs/**/*srs*.md .vsaf/docs/**/*srs*.md 2>/dev/null`
|
|
19
|
+
`ls -1 docs/**/*test*case*.md docs/**/*test-cases*.md .vsaf/docs/**/*testcase*.md 2>/dev/null`
|
|
20
|
+
Read the newest SRS and test-case files you find. If none exist, derive the cases
|
|
21
|
+
from the feature request above.
|
|
17
22
|
2. Derive an ordered list of concrete, individually-testable cases for THIS feature.
|
|
18
23
|
3. Write them to `$ARTIFACTS_DIR/impl-progress.md`, one line each, exactly:
|
|
19
24
|
`- [ ] TC-01: <short title>`
|
|
@@ -55,9 +60,15 @@ nodes:
|
|
|
55
60
|
4. Write the chosen command to `$ARTIFACTS_DIR/run-tests.sh` — a POSIX sh script that runs the
|
|
56
61
|
FULL test suite from the project root and exits with the test runner's exit code. Make it
|
|
57
62
|
self-contained and re-runnable, then `chmod +x` it.
|
|
58
|
-
5. Verify once: `sh $ARTIFACTS_DIR/run-tests.sh
|
|
59
|
-
written yet) — you are
|
|
60
|
-
|
|
63
|
+
5. Verify once: `sh $ARTIFACTS_DIR/run-tests.sh 2>&1 | tee $ARTIFACTS_DIR/test-baseline.log`.
|
|
64
|
+
It is FINE if tests fail now (the code isn't written yet) — you are proving the toolchain
|
|
65
|
+
runs AND capturing the BASELINE (which tests already fail BEFORE this feature). From that
|
|
66
|
+
log, write `$ARTIFACTS_DIR/test-baseline.txt` with two lines exactly:
|
|
67
|
+
`total=<N>` (total tests the runner executed)
|
|
68
|
+
`failing=<comma-separated fully-qualified failing test names, or empty>`
|
|
69
|
+
(If the runner produced no test summary at all, write `total=0` and note the runner error —
|
|
70
|
+
that itself is a toolchain problem to fix here, not later.) Report one line: host or docker,
|
|
71
|
+
baseline total + failing count, and that run-tests.sh is ready.
|
|
61
72
|
|
|
62
73
|
- id: impl-loop
|
|
63
74
|
description: Implement 3/4 — TDD one case per turn; run tests IN-LOOP; finish only when GREEN
|
|
@@ -79,15 +90,22 @@ nodes:
|
|
|
79
90
|
`<promise>IMPL_COMPLETE</promise>` and stop.
|
|
80
91
|
3. For that one case: write/adjust its test (RED), implement the minimal code to make it pass
|
|
81
92
|
(GREEN). Keep the change scoped to this case.
|
|
82
|
-
4. Run `sh $ARTIFACTS_DIR/run-tests.sh 2>&1 | tee $ARTIFACTS_DIR/test-output.log
|
|
83
|
-
fails (this case OR a regression in a
|
|
84
|
-
|
|
93
|
+
4. Run the COMPLETE suite: `sh $ARTIFACTS_DIR/run-tests.sh 2>&1 | tee $ARTIFACTS_DIR/test-output.log`
|
|
94
|
+
(never a hand-picked subset). If ANY test fails (this case OR a regression in a
|
|
95
|
+
previously-passing test), FIX it now and re-run until this case passes and nothing else is
|
|
96
|
+
broken. Never move on with a failing suite.
|
|
85
97
|
5. Tick the line to `- [x]` in `$ARTIFACTS_DIR/impl-progress.md` and append a one-line note.
|
|
86
98
|
|
|
87
99
|
Work on the real project files. Match the project's existing structure, language, and test
|
|
88
|
-
framework. Do only the current case — the loop calls you again for the next.
|
|
89
|
-
|
|
90
|
-
suite
|
|
100
|
+
framework. Do only the current case — the loop calls you again for the next.
|
|
101
|
+
|
|
102
|
+
COMPLETION BAR — the full suite MUST be GREEN (0 failures, 0 errors) before you emit the
|
|
103
|
+
signal. A test that was ALREADY failing before this feature (see `$ARTIFACTS_DIR/test-baseline.txt`)
|
|
104
|
+
is NOT an excuse to ship red: either fix it, or — if it is genuinely unrelated and out of scope —
|
|
105
|
+
record it under a `## Pre-existing failures (quarantined)` section in impl-progress.md with the
|
|
106
|
+
exact test name + one-line rationale AND exclude it from the run so the suite is green. Do NOT
|
|
107
|
+
leave the suite red without an explicit quarantine entry. CRITICAL: NEVER emit
|
|
108
|
+
`<promise>IMPL_COMPLETE</promise>` while any test is failing; the signal means the whole suite is green.
|
|
91
109
|
loop:
|
|
92
110
|
until: IMPL_COMPLETE
|
|
93
111
|
interactive: false
|
|
@@ -105,9 +123,20 @@ nodes:
|
|
|
105
123
|
bash: |
|
|
106
124
|
RT=$ARTIFACTS_DIR/run-tests.sh
|
|
107
125
|
LOG=$ARTIFACTS_DIR/test-output.log
|
|
126
|
+
BASE=$ARTIFACTS_DIR/test-baseline.txt
|
|
108
127
|
if [ ! -f "$RT" ]; then echo "[vsaf] test-gate: run-tests.sh missing (env-setup did not run)"; exit 1; fi
|
|
109
|
-
echo "[vsaf] test-gate: final verification via run-tests.sh"
|
|
128
|
+
echo "[vsaf] test-gate: final verification via run-tests.sh (FULL suite)"
|
|
110
129
|
sh "$RT" > "$LOG" 2>&1; code=$?
|
|
111
130
|
tail -40 "$LOG" 2>/dev/null || true
|
|
131
|
+
# Print the runner's real execution summary so the report is honest (never a subset count).
|
|
132
|
+
SUMMARY=$(grep -iE "Tests run: [0-9]+|[0-9]+ (passed|failed)|BUILD (SUCCESS|FAILURE)|failures=|[0-9]+ tests" "$LOG" 2>/dev/null | tail -6)
|
|
133
|
+
echo "[vsaf] test-gate summary:"; printf '%s\n' "$SUMMARY"
|
|
134
|
+
if [ -f "$BASE" ]; then echo "[vsaf] baseline (pre-feature): $(tr '\n' ' ' < "$BASE")"; fi
|
|
135
|
+
# Anti-false-green: refuse a 0 exit that shows NO evidence the suite actually ran
|
|
136
|
+
# (e.g. a runner that no-op'd or reused stale state). A green must be a proven green.
|
|
137
|
+
if [ "$code" -eq 0 ] && ! printf '%s' "$SUMMARY" | grep -qiE "Tests run: [0-9]+|[0-9]+ passed|[0-9]+ tests|BUILD SUCCESS"; then
|
|
138
|
+
echo "[vsaf] test-gate: FAIL — exit 0 but no test-run summary found; cannot confirm the suite executed. Refusing to false-green."
|
|
139
|
+
exit 1
|
|
140
|
+
fi
|
|
112
141
|
echo "[vsaf] test-gate exit=$code"
|
|
113
142
|
exit "$code"
|
|
@@ -35,27 +35,10 @@ nodes:
|
|
|
35
35
|
- architecture-epics
|
|
36
36
|
workspace: false
|
|
37
37
|
command: sdlc-architecture-readiness
|
|
38
|
-
- id: srs
|
|
39
|
-
description:
|
|
38
|
+
- id: srs
|
|
39
|
+
description: Phase 4 — SRS specification, validation, interface verification (single cohesive skill)
|
|
40
|
+
model: sonnet
|
|
40
41
|
workspace: false
|
|
41
|
-
command: sdlc-srs
|
|
42
|
+
command: sdlc-srs
|
|
42
43
|
depends_on:
|
|
43
44
|
- architecture-readiness
|
|
44
|
-
- id: srs-write
|
|
45
|
-
description: SRS 2/4 — write SRS
|
|
46
|
-
depends_on:
|
|
47
|
-
- srs-prereq
|
|
48
|
-
workspace: false
|
|
49
|
-
command: sdlc-srs-write
|
|
50
|
-
- id: srs-validate
|
|
51
|
-
description: SRS 3/4 — validate SRS
|
|
52
|
-
depends_on:
|
|
53
|
-
- srs-write
|
|
54
|
-
workspace: false
|
|
55
|
-
command: sdlc-srs-validate
|
|
56
|
-
- id: srs-interface
|
|
57
|
-
description: SRS 4/4 — interface verification + gate
|
|
58
|
-
depends_on:
|
|
59
|
-
- srs-validate
|
|
60
|
-
workspace: false
|
|
61
|
-
command: sdlc-srs-interface
|
|
@@ -28,9 +28,17 @@ nodes:
|
|
|
28
28
|
workspace: false
|
|
29
29
|
command: sdlc-review-impact
|
|
30
30
|
- id: review-debate
|
|
31
|
-
description: Review 5/
|
|
31
|
+
description: Review 5/6 — debate + gate
|
|
32
32
|
model: haiku
|
|
33
33
|
depends_on:
|
|
34
34
|
- review-impact
|
|
35
35
|
workspace: false
|
|
36
36
|
command: sdlc-review-debate
|
|
37
|
+
- id: review-fix
|
|
38
|
+
description: Review 6/6 — implement MUST-FIX findings, then re-verify the full suite
|
|
39
|
+
model: sonnet
|
|
40
|
+
permission_mode: bypassPermissions
|
|
41
|
+
depends_on:
|
|
42
|
+
- review-debate
|
|
43
|
+
workspace: false
|
|
44
|
+
command: sdlc-review-fix
|
|
@@ -1,29 +1,8 @@
|
|
|
1
1
|
name: sdlc-srs
|
|
2
|
-
description: 'Phase workflow: SRS
|
|
2
|
+
description: 'Phase workflow: SRS — single cohesive skill (write + 8-dimension validation + interface verification)'
|
|
3
3
|
nodes:
|
|
4
|
-
- id: srs
|
|
5
|
-
description:
|
|
4
|
+
- id: srs
|
|
5
|
+
description: Phase 4 — SRS specification, validation, interface verification
|
|
6
6
|
model: sonnet
|
|
7
7
|
workspace: false
|
|
8
|
-
command: sdlc-srs
|
|
9
|
-
- id: srs-write
|
|
10
|
-
description: SRS 2/4 — write SRS
|
|
11
|
-
model: haiku
|
|
12
|
-
depends_on:
|
|
13
|
-
- srs-prereq
|
|
14
|
-
workspace: false
|
|
15
|
-
command: sdlc-srs-write
|
|
16
|
-
- id: srs-validate
|
|
17
|
-
description: SRS 3/4 — validate SRS
|
|
18
|
-
model: haiku
|
|
19
|
-
depends_on:
|
|
20
|
-
- srs-write
|
|
21
|
-
workspace: false
|
|
22
|
-
command: sdlc-srs-validate
|
|
23
|
-
- id: srs-interface
|
|
24
|
-
description: SRS 4/4 — interface verification + gate
|
|
25
|
-
model: haiku
|
|
26
|
-
depends_on:
|
|
27
|
-
- srs-validate
|
|
28
|
-
workspace: false
|
|
29
|
-
command: sdlc-srs-interface
|
|
8
|
+
command: sdlc-srs
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as X,b as Z,e as ee,g as te,h as ne,i as ie,j as oe,k as re}from"./chunk-OZ6BRCXC.js";import{$ as _,Aa as G,C as o,Ca as H,Da as J,G as w,Ha as K,Ja as Q,K as f,Ka as U,N as y,O as F,P as L,Q as A,R as S,S as E,T as s,U as n,V as t,W as B,X as j,Y as $,_ as b,aa as p,da as v,f as N,fa as i,ga as c,h as V,ha as u,i as z,ia as Y,ja as h,ka as M,la as P,oa as I,p as C,pa as D,q as g,qa as R,r as x,v as k,za as q}from"./chunk-WSAVMPNK.js";var me=(r,a)=>a.name;function ge(r,a){if(r&1&&(n(0,"option",9),i(1),t()),r&2){let e=a.$implicit;s("value",e.name)("disabled",!e.available),o(),Y(" ",e.name,"",e.available?"":" \u2014 not available on this machine"," ")}}function xe(r,a){if(r&1&&(n(0,"option",10),i(1),t()),r&2){let e=a.$implicit;s("value",e),o(),c(e)}}function _e(r,a){if(r&1){let e=b();n(0,"div",1),_("click",function(){g(e);let l=p();return x(l.close())}),n(1,"div",2),_("click",function(l){return l.stopPropagation()}),n(2,"h3",3),i(3),t(),n(4,"label",4),i(5,"Arguments"),t(),n(6,"input",5),P("ngModelChange",function(l){g(e);let m=p();return M(m.args,l)||(m.args=l),x(l)}),_("keydown.enter",function(){g(e);let l=p();return x(l.submit())}),t(),n(7,"div",6)(8,"div")(9,"label",4),i(10,"Provider"),t(),n(11,"select",7),P("ngModelChange",function(l){g(e);let m=p();return M(m.provider,l)||(m.provider=l),x(l)}),_("ngModelChange",function(){g(e);let l=p();return x(l.onProviderChange())}),n(12,"option",8),i(13,"(workflow default)"),t(),S(14,ge,2,4,"option",9,me),t()(),n(16,"div")(17,"label",4),i(18,"Model"),t(),n(19,"select",7),P("ngModelChange",function(l){g(e);let m=p();return M(m.model,l)||(m.model=l),x(l)}),n(20,"option",8),i(21,"(workflow default)"),t(),S(22,xe,2,2,"option",10,A),t()()(),n(24,"div",11)(25,"button",12),_("click",function(){g(e);let l=p();return x(l.close())}),i(26,"Cancel"),t(),n(27,"button",13),_("click",function(){g(e);let l=p();return x(l.submit())}),i(28,"Start \u25B6"),t()()()()}if(r&2){let e=p();o(3),u("Run ",e.workflowName),o(3),h("ngModel",e.args),o(5),h("ngModel",e.provider),o(3),E(e.providers),o(5),h("ngModel",e.model),o(3),E(e.modelOptions)}}var O=class r{visible=!1;workflowName="";providers=[];run=new k;closed=new k;args="";provider="";model="";get modelOptions(){return this.provider?this.providers.find(a=>a.name===this.provider)?.defaultModels??[]:[...new Set(this.providers.flatMap(a=>a.defaultModels))]}onProviderChange(){this.model&&!this.modelOptions.includes(this.model)&&(this.model="")}submit(){this.run.emit({args:this.args,provider:this.provider||void 0,model:this.model||void 0}),this.reset()}close(){this.visible=!1,this.reset(),this.closed.emit()}reset(){this.args="",this.provider="",this.model=""}static \u0275fac=function(e){return new(e||r)};static \u0275cmp=w({type:r,selectors:[["app-run-dialog"]],inputs:{visible:"visible",workflowName:"workflowName",providers:"providers"},outputs:{run:"run",closed:"closed"},decls:1,vars:1,consts:[[1,"fixed","inset-0","bg-black/60","flex","items-center","justify-center","z-50"],[1,"fixed","inset-0","bg-black/60","flex","items-center","justify-center","z-50",3,"click"],[1,"bg-slate-800","rounded-lg","p-6","w-full","max-w-md","border","border-slate-700",3,"click"],[1,"text-lg","font-semibold","text-slate-100","mb-4"],[1,"block","text-sm","text-slate-400","mb-1"],["placeholder",'e.g. "fix #42"',1,"w-full","bg-slate-900","border","border-slate-600","rounded","px-3","py-2","text-slate-100","text-sm","focus:outline-none","focus:border-blue-500","mb-4",3,"ngModelChange","keydown.enter","ngModel"],[1,"grid","grid-cols-2","gap-3","mb-4"],[1,"w-full","bg-slate-900","border","border-slate-600","rounded","px-3","py-2","text-slate-100","text-sm","focus:outline-none","focus:border-blue-500",3,"ngModelChange","ngModel"],["value",""],[3,"value","disabled"],[3,"value"],[1,"flex","justify-end","gap-3"],[1,"px-4","py-2","text-sm","text-slate-400","hover:text-slate-200",3,"click"],[1,"px-4","py-2","text-sm","bg-blue-600","text-white","rounded","hover:bg-blue-500",3,"click"]],template:function(e,d){e&1&&F(0,_e,29,4,"div",0),e&2&&L(d.visible?0:-1)},dependencies:[oe,ne,ie,X,te,Z,ee],encapsulation:2})};function ae(r,a,e={}){let d=r.provider||a.provider||e.provider||"default",l=r.model||a.model||e.model||"";return{name:l?`${d}/${l}`:d,inherited:!r.provider&&!r.model}}var fe=r=>({edit:r}),ue=r=>["/runs",r];function be(r,a){if(r&1&&(n(0,"div",4),i(1),t()),r&2){let e=p();o(),c(e.error)}}function ve(r,a){r&1&&(n(0,"div",5),i(1,"Loading workflow definition..."),t())}function Ce(r,a){if(r&1&&(n(0,"span"),i(1),t()),r&2){let e=p(3);o(),c(e.runs.length)}}function we(r,a){if(r&1){let e=b();n(0,"button",24),_("click",function(){let l=g(e).$implicit,m=p(2);return x(m.tab=l.id)}),i(1),f(2,Ce,2,1,"span",3),t()}if(r&2){let e=a.$implicit,d=p(2);v("active",d.tab===e.id),o(),u(" ",e.label," "),o(),s("ngIf",e.id==="runs")}}function ye(r,a){if(r&1){let e=b();n(0,"button",30),_("click",function(){let l=g(e).$implicit,m=p(3);return x(m.selectedNode=l)}),n(1,"span",31),i(2),t(),n(3,"span",32),i(4),t(),n(5,"span",33)(6,"strong"),i(7),n(8,"span",34),i(9),t()(),n(10,"small"),i(11),t()(),n(12,"span",35),i(13),t()()}if(r&2){let e=a.$implicit,d=a.index,l=p(3);v("selected",(l.selectedNode==null?null:l.selectedNode.id)===e.id),o(2),c(d+1),o(),y("data-type",e.type),o(),c(l.typeLabel(e.type)),o(3),u("",e.id," "),o(),v("inherited",l.nodeProvider(e).inherited),o(),c(l.providerBadgeLabel(e)),o(2),c(e.content),o(2),c(e.dependsOn.length?"After "+e.dependsOn.join(", "):"Entry point")}}function he(r,a){if(r&1&&(n(0,"aside",36)(1,"div",37),i(2,"Node inspector"),t(),n(3,"h3"),i(4),t(),n(5,"span",38),i(6),t(),n(7,"dl")(8,"div")(9,"dt"),i(10,"Depends on"),t(),n(11,"dd"),i(12),t()(),n(13,"div")(14,"dt"),i(15,"Trigger"),t(),n(16,"dd"),i(17),t()(),n(18,"div")(19,"dt"),i(20,"Context"),t(),n(21,"dd"),i(22),t()(),n(23,"div")(24,"dt"),i(25,"Condition"),t(),n(26,"dd"),i(27),t()(),n(28,"div")(29,"dt"),i(30,"Provider"),t(),n(31,"dd"),i(32),t()(),n(33,"div")(34,"dt"),i(35,"Model"),t(),n(36,"dd"),i(37),t()()(),n(38,"label"),i(39,"Content"),t(),n(40,"pre"),i(41),t()()),r&2){let e=p(3);o(4),c(e.selectedNode.id),o(2),c(e.selectedNode.type),o(6),c(e.selectedNode.dependsOn.join(", ")||"None"),o(5),c(e.selectedNode.triggerRule),o(5),c(e.selectedNode.context),o(5),c(e.selectedNode.when||"Always"),o(5),c(e.selectedNode.provider||e.workflow.defaultProvider||"Default"),o(5),c(e.selectedNode.model||e.workflow.defaultModel||"Default"),o(4),c(e.selectedNode.content)}}function Me(r,a){if(r&1&&(n(0,"div",25)(1,"main",26)(2,"div",27)(3,"div")(4,"h2"),i(5,"Workflow steps"),t(),n(6,"p"),i(7,"Execution order and node configuration."),t()()(),f(8,ye,14,11,"button",28),t(),f(9,he,42,9,"aside",29),t()),r&2){let e=p(2);o(8),s("ngForOf",e.workflow.nodes),o(),s("ngIf",e.selectedNode)}}function Pe(r,a){if(r&1&&(n(0,"article")(1,"div")(2,"span",32),i(3),t(),n(4,"strong"),i(5),t()(),n(6,"span",34),i(7),t()()),r&2){let e=a.$implicit,d=p(4);o(2),y("data-type",e.type),o(),c(d.typeLabel(e.type)),o(2),c(e.id),o(),v("inherited",d.nodeProvider(e).inherited),o(),c(d.providerBadgeLabel(e))}}function Oe(r,a){if(r&1&&(n(0,"div",44)(1,"span",45),i(2),t(),f(3,Pe,8,6,"article",46),t()),r&2){let e=a.$implicit,d=a.index;o(2),u("Stage ",d+1),o(),s("ngForOf",e)}}function ke(r,a){if(r&1&&(n(0,"div",39)(1,"div",40)(2,"div")(3,"h2"),i(4,"DAG topology"),t(),n(5,"p"),i(6,"Dependencies flow from left to right."),t()(),n(7,"span"),i(8),t()(),n(9,"div",41)(10,"div",42),f(11,Oe,4,2,"div",43),t()()()),r&2){let e=p(2);o(8),u("",e.dagLayers.length," stages"),o(3),s("ngForOf",e.dagLayers)}}function Se(r,a){if(r&1){let e=b();n(0,"div",47)(1,"div",48)(2,"span"),i(3,"Read-only YAML definition"),t(),n(4,"button",24),_("click",function(){g(e);let l=p(2);return x(l.copyYaml())}),i(5),t()(),n(6,"pre"),i(7),t()()}if(r&2){let e=p(2);o(5),c(e.copied?"Copied":"Copy"),o(2),c(e.workflow.yaml)}}function Ee(r,a){r&1&&(n(0,"span",53),i(1,"history"),t())}function Ie(r,a){if(r&1&&(n(0,"a",54),i(1,"Open"),t()),r&2){let e=p().$implicit;s("routerLink",I(1,ue,e.runId))}}function De(r,a){if(r&1&&(n(0,"tr")(1,"td")(2,"code"),i(3),t()(),n(4,"td")(5,"span",50),i(6),t(),f(7,Ee,2,0,"span",51),t(),n(8,"td"),i(9),D(10,"date"),t(),n(11,"td"),i(12),D(13,"date"),t(),n(14,"td"),f(15,Ie,2,3,"a",52),t()()),r&2){let e=a.$implicit;o(3),c(e.runId),o(2),y("data-status",e.status),o(),c(e.status),o(),s("ngIf",e.source==="history"),o(2),c(R(10,7,e.startedAt,"medium")),o(3),c(e.completedAt?R(13,10,e.completedAt,"medium"):"In progress"),o(3),s("ngIf",e.source!=="history")}}function Re(r,a){r&1&&(n(0,"tr")(1,"td",55),i(2,"This workflow has not been run yet."),t()())}function We(r,a){if(r&1&&(n(0,"div",49)(1,"table")(2,"thead")(3,"tr")(4,"th"),i(5,"Run ID"),t(),n(6,"th"),i(7,"Status"),t(),n(8,"th"),i(9,"Started"),t(),n(10,"th"),i(11,"Completed"),t(),B(12,"th"),t()(),n(13,"tbody"),f(14,De,16,13,"tr",46)(15,Re,3,0,"tr",3),t()()()),r&2){let e=p(2);o(14),s("ngForOf",e.runs),o(),s("ngIf",!e.runs.length)}}function Te(r,a){if(r&1){let e=b();j(0),n(1,"nav",6)(2,"a",7),i(3,"Workflows"),t(),n(4,"span"),i(5,"/"),t(),n(6,"span"),i(7),t()(),n(8,"header",8)(9,"div",9),i(10,"Y"),t(),n(11,"div",10)(12,"span",11),i(13),t(),n(14,"h1"),i(15),t(),n(16,"p"),i(17),t()(),n(18,"div",12)(19,"a",13),i(20,"Edit"),t(),n(21,"button",14),_("click",function(){g(e);let l=p();return x(l.showRunDialog=!0)}),i(22,"Run workflow"),t()()(),n(23,"div",15)(24,"div")(25,"span"),i(26,"Nodes"),t(),n(27,"strong"),i(28),t()(),n(29,"div")(30,"span"),i(31,"Dependencies"),t(),n(32,"strong"),i(33),t()(),n(34,"div")(35,"span"),i(36,"Provider"),t(),n(37,"strong"),i(38),t()(),n(39,"div")(40,"span"),i(41,"Model"),t(),n(42,"strong"),i(43),t()(),n(44,"div")(45,"span"),i(46,"Runs"),t(),n(47,"strong"),i(48),t()()(),n(49,"div",16)(50,"span"),i(51,"Definition"),t(),n(52,"code"),i(53),t()(),n(54,"nav",17),f(55,we,3,4,"button",18),t(),f(56,Me,10,2,"div",19)(57,ke,12,2,"div",20)(58,Se,8,2,"div",21)(59,We,16,2,"div",22),n(60,"app-run-dialog",23),_("run",function(l){g(e);let m=p();return x(m.onStartRun(l))})("closed",function(){g(e);let l=p();return x(l.showRunDialog=!1)}),t(),$()}if(r&2){let e=p();o(7),c(e.workflow.id),o(6),c(e.workflow.id),o(2),c(e.workflow.name),o(2),c(e.workflow.description||"No description provided."),o(2),s("queryParams",I(19,fe,e.workflow.id)),o(9),c(e.workflow.nodeCount),o(5),c(e.dependencyCount),o(5),c(e.workflow.defaultProvider||"default"),o(5),c(e.workflow.defaultModel||"default"),o(5),c(e.runs.length),o(5),u(".vsaf/workflows/",e.workflow.file),o(2),s("ngForOf",e.tabs),o(),s("ngIf",e.tab==="overview"),o(),s("ngIf",e.tab==="dag"),o(),s("ngIf",e.tab==="yaml"),o(),s("ngIf",e.tab==="runs"),o(),s("visible",e.showRunDialog)("workflowName",e.workflow.id)("providers",e.providers)}}var le=class r{route=C(K);api=C(re);router=C(Q);workflow;runs=[];providers=[];selectedNode;tab="overview";showRunDialog=!1;loading=!0;error="";copied=!1;tabs=[{id:"overview",label:"Overview"},{id:"dag",label:"DAG"},{id:"yaml",label:"YAML"},{id:"runs",label:"Runs"}];ngOnInit(){let a=this.route.snapshot.queryParamMap.get("id");if(!a){this.loading=!1,this.error="Missing workflow id.";return}V({workflow:this.api.getWorkflow(a),runs:this.api.getRuns(),providers:this.api.getProviders().pipe(z(()=>N({providers:[]})))}).subscribe({next:e=>{this.workflow=e.workflow,this.selectedNode=e.workflow.nodes[0],this.runs=e.runs.filter(d=>d.workflow===a).reverse(),this.providers=e.providers.providers??[],this.loading=!1},error:e=>{this.loading=!1,this.error=e?.error?.error??"Failed to load workflow."}})}nodeProvider(a){return ae(a,{provider:this.workflow?.defaultProvider,model:this.workflow?.defaultModel})}providerBadgeLabel(a){let e=this.nodeProvider(a);return e.inherited?`\u2192 ${e.name}`:e.name}get dependencyCount(){return this.workflow?.nodes.reduce((a,e)=>a+e.dependsOn.length,0)??0}get dagLayers(){if(!this.workflow)return[];let a=new Map,e=(l,m=new Set)=>{if(a.has(l.id))return a.get(l.id);if(m.has(l.id)||!l.dependsOn.length)return 0;m.add(l.id);let W=1+Math.max(...l.dependsOn.map(de=>{let T=this.workflow.nodes.find(ce=>ce.id===de);return T?e(T,m):0}));return m.delete(l.id),a.set(l.id,W),W};for(let l of this.workflow.nodes)a.set(l.id,e(l));let d=[];for(let l of this.workflow.nodes)(d[a.get(l.id)??0]??=[]).push(l);return d}typeLabel(a){return a==="command"?"CMD":a==="prompt"?"AI":"SH"}onStartRun(a){this.showRunDialog=!1,this.api.startRun(this.workflow.id,a.args,{provider:a.provider,model:a.model}).subscribe(e=>this.router.navigate(["/runs",e.runId]))}copyYaml(){this.workflow&&navigator.clipboard.writeText(this.workflow.yaml).then(()=>{this.copied=!0,setTimeout(()=>this.copied=!1,1500)})}static \u0275fac=function(e){return new(e||r)};static \u0275cmp=w({type:r,selectors:[["app-workflow-detail"]],decls:4,vars:3,consts:[[1,"detail-page"],["class","state error",4,"ngIf"],["class","state",4,"ngIf"],[4,"ngIf"],[1,"state","error"],[1,"state"],[1,"breadcrumb"],["routerLink","/workflows"],[1,"hero"],[1,"workflow-icon"],[1,"hero-copy"],[1,"namespace"],[1,"hero-actions"],["routerLink","/workflows/builder",1,"button","secondary",3,"queryParams"],["type","button",1,"button","primary",3,"click"],[1,"stats"],[1,"file-strip"],["aria-label","Workflow detail sections",1,"tabs"],["type","button",3,"active","click",4,"ngFor","ngForOf"],["class","tab-content",4,"ngIf"],["class","dag-panel",4,"ngIf"],["class","yaml-panel",4,"ngIf"],["class","runs-panel",4,"ngIf"],[3,"run","closed","visible","workflowName","providers"],["type","button",3,"click"],[1,"tab-content"],[1,"node-list"],[1,"section-heading"],["type","button","class","node-row",3,"selected","click",4,"ngFor","ngForOf"],["class","inspector",4,"ngIf"],["type","button",1,"node-row",3,"click"],[1,"step-index"],[1,"type-icon"],[1,"node-main"],[1,"provider-badge"],[1,"dependency"],[1,"inspector"],[1,"inspector-title"],[1,"badge"],[1,"dag-panel"],[1,"dag-toolbar"],[1,"dag-scroll"],[1,"dag-canvas"],["class","dag-layer",4,"ngFor","ngForOf"],[1,"dag-layer"],[1,"layer-label"],[4,"ngFor","ngForOf"],[1,"yaml-panel"],[1,"yaml-toolbar"],[1,"runs-panel"],[1,"status"],["class","history-tag",4,"ngIf"],[3,"routerLink",4,"ngIf"],[1,"history-tag"],[3,"routerLink"],["colspan","5",1,"empty"]],template:function(e,d){e&1&&(n(0,"section",0),f(1,be,2,1,"div",1)(2,ve,2,0,"div",2)(3,Te,61,21,"ng-container",3),t()),e&2&&(o(),s("ngIf",d.error),o(),s("ngIf",d.loading),o(),s("ngIf",d.workflow))},dependencies:[J,q,G,U,O,H],styles:["[_nghost-%COMP%]{display:block;min-height:calc(100vh - 56px);background:#101114;color:#e7e7ea}.detail-page[_ngcontent-%COMP%]{max-width:1320px;margin:auto;padding:24px 28px 60px}.breadcrumb[_ngcontent-%COMP%]{display:flex;gap:7px;color:#737a87;font-size:12px;margin-bottom:18px}.breadcrumb[_ngcontent-%COMP%] a[_ngcontent-%COMP%]{color:#9ca3af;text-decoration:none}.hero[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:15px}.workflow-icon[_ngcontent-%COMP%]{display:grid;place-items:center;flex:0 0 46px;height:52px;border:1px solid #454957;border-radius:6px;color:#a5b4fc;font-weight:900}.hero-copy[_ngcontent-%COMP%]{min-width:0}.namespace[_ngcontent-%COMP%]{font:11px ui-monospace,monospace;color:#818cf8}.hero[_ngcontent-%COMP%] h1[_ngcontent-%COMP%]{font-size:26px;margin:4px 0}.hero[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{color:#9ba1ad;margin:5px 0;line-height:1.5;max-width:800px}.hero-actions[_ngcontent-%COMP%]{display:flex;gap:8px;margin-left:auto}.button[_ngcontent-%COMP%]{padding:9px 13px;border-radius:5px;text-decoration:none;font:inherit;font-size:12px;font-weight:700;cursor:pointer}.button.secondary[_ngcontent-%COMP%]{border:1px solid #3b3e48;background:#1c1e24;color:#e5e7eb}.button.primary[_ngcontent-%COMP%]{border:1px solid #6366f1;background:#6366f1;color:#fff}.stats[_ngcontent-%COMP%]{display:grid;grid-template-columns:repeat(5,1fr);border:1px solid #2d3038;border-radius:7px;margin-top:24px;background:#16171c}.stats[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{padding:13px 16px;border-right:1px solid #2d3038}.stats[_ngcontent-%COMP%] div[_ngcontent-%COMP%]:last-child{border:0}.stats[_ngcontent-%COMP%] span[_ngcontent-%COMP%], .file-strip[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{display:block;color:#777e8b;font-size:10px;text-transform:uppercase;font-weight:700}.stats[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{display:block;margin-top:5px;font-size:14px}.file-strip[_ngcontent-%COMP%]{display:flex;align-items:center;gap:14px;padding:10px 14px;border:1px solid #2d3038;border-top:0;background:#131419}.file-strip[_ngcontent-%COMP%] code[_ngcontent-%COMP%]{color:#aab0bb;font-size:11px}.tabs[_ngcontent-%COMP%]{display:flex;border-bottom:1px solid #2d3038;margin-top:24px}.tabs[_ngcontent-%COMP%] button[_ngcontent-%COMP%]{border:0;background:transparent;color:#858c98;padding:10px 14px;font-weight:700;cursor:pointer}.tabs[_ngcontent-%COMP%] button.active[_ngcontent-%COMP%]{color:#fff;box-shadow:inset 0 -2px #6366f1}.tabs[_ngcontent-%COMP%] button[_ngcontent-%COMP%] span[_ngcontent-%COMP%]{background:#292c34;padding:1px 5px;border-radius:8px;font-size:10px}.tab-content[_ngcontent-%COMP%]{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:16px;margin-top:18px}.section-heading[_ngcontent-%COMP%] h2[_ngcontent-%COMP%], .dag-toolbar[_ngcontent-%COMP%] h2[_ngcontent-%COMP%]{font-size:15px;margin:0}.section-heading[_ngcontent-%COMP%] p[_ngcontent-%COMP%], .dag-toolbar[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{font-size:12px;color:#7f8692;margin:4px 0 12px}.node-row[_ngcontent-%COMP%]{display:grid;grid-template-columns:28px 38px minmax(180px,1fr) minmax(120px,.5fr);align-items:center;gap:11px;width:100%;border:1px solid #292c34;border-bottom:0;background:#16171c;color:#e5e7eb;padding:11px;text-align:left;cursor:pointer}.node-row[_ngcontent-%COMP%]:first-of-type{border-radius:6px 6px 0 0}.node-row[_ngcontent-%COMP%]:last-child{border-bottom:1px solid #292c34;border-radius:0 0 6px 6px}.node-row[_ngcontent-%COMP%]:hover, .node-row.selected[_ngcontent-%COMP%]{background:#1e2027;border-color:#494d5a}.step-index[_ngcontent-%COMP%]{color:#676e7a;font-size:11px;text-align:center}.type-icon[_ngcontent-%COMP%]{display:grid;place-items:center;width:34px;height:28px;border-radius:4px;background:#242630;color:#c4b5fd;font-size:9px;font-weight:900;text-transform:uppercase}.type-icon[data-type=bash][_ngcontent-%COMP%]{color:#fbbf24}.type-icon[data-type=command][_ngcontent-%COMP%]{color:#67e8f9}.node-main[_ngcontent-%COMP%]{min-width:0}.node-main[_ngcontent-%COMP%] strong[_ngcontent-%COMP%], .node-main[_ngcontent-%COMP%] small[_ngcontent-%COMP%]{display:block}.node-main[_ngcontent-%COMP%] small[_ngcontent-%COMP%]{color:#777e8b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:4px}.dependency[_ngcontent-%COMP%]{color:#858c98;font-size:11px;overflow-wrap:anywhere}.inspector[_ngcontent-%COMP%]{border:1px solid #2d3038;background:#16171c;border-radius:6px;padding:15px}.inspector-title[_ngcontent-%COMP%]{color:#777e8b;text-transform:uppercase;font-size:10px;font-weight:800}.inspector[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{margin:8px 0;font-size:16px}.badge[_ngcontent-%COMP%]{display:inline-block;border:1px solid #4b4f5c;padding:2px 6px;border-radius:4px;font-size:10px;color:#a5b4fc}.inspector[_ngcontent-%COMP%] dl[_ngcontent-%COMP%]{margin:14px 0}.inspector[_ngcontent-%COMP%] dl[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{display:flex;justify-content:space-between;gap:12px;padding:7px 0;border-bottom:1px solid #282a31;font-size:11px}.inspector[_ngcontent-%COMP%] dt[_ngcontent-%COMP%]{color:#777e8b}.inspector[_ngcontent-%COMP%] dd[_ngcontent-%COMP%]{margin:0;text-align:right;overflow-wrap:anywhere}.inspector[_ngcontent-%COMP%] label[_ngcontent-%COMP%]{font-size:10px;color:#777e8b;text-transform:uppercase}.inspector[_ngcontent-%COMP%] pre[_ngcontent-%COMP%]{white-space:pre-wrap;overflow-wrap:anywhere;background:#0d0e11;padding:10px;border-radius:5px;font-size:11px;max-height:240px;overflow:auto}.dag-panel[_ngcontent-%COMP%], .yaml-panel[_ngcontent-%COMP%], .runs-panel[_ngcontent-%COMP%]{margin-top:18px;border:1px solid #2d3038;border-radius:7px;overflow:hidden}.dag-toolbar[_ngcontent-%COMP%], .yaml-toolbar[_ngcontent-%COMP%]{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid #2d3038;background:#16171c}.dag-toolbar[_ngcontent-%COMP%] p[_ngcontent-%COMP%]{margin-bottom:0}.dag-toolbar[_ngcontent-%COMP%] > span[_ngcontent-%COMP%], .yaml-toolbar[_ngcontent-%COMP%]{font-size:11px;color:#858c98}.dag-scroll[_ngcontent-%COMP%]{overflow-x:auto;background-color:var(--background)}.dag-canvas[_ngcontent-%COMP%]{display:flex;align-items:flex-start;gap:20px;width:max-content;min-width:100%;box-sizing:border-box;padding:14px 16px;background-image:radial-gradient(#30333b 1px,transparent 1px);background-size:16px 16px}.dag-layer[_ngcontent-%COMP%]{display:flex;flex:0 0 auto;min-width:200px;flex-direction:column;gap:8px}.layer-label[_ngcontent-%COMP%]{font-size:10px;color:var(--text-tertiary);text-transform:uppercase}.dag-layer[_ngcontent-%COMP%] article[_ngcontent-%COMP%]{border:1px solid var(--border-bright);background:var(--surface);border-radius:6px;padding:8px 10px}.dag-layer[_ngcontent-%COMP%] article[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;min-width:0}.dag-layer[_ngcontent-%COMP%] article[_ngcontent-%COMP%] strong[_ngcontent-%COMP%]{font-size:12px;overflow-wrap:anywhere}.dag-layer[_ngcontent-%COMP%] article[_ngcontent-%COMP%] .provider-badge[_ngcontent-%COMP%]{margin:7px 0 0}.yaml-toolbar[_ngcontent-%COMP%] button[_ngcontent-%COMP%]{border:1px solid #3a3d47;background:#22242b;color:#ddd;border-radius:4px;padding:5px 9px}.yaml-panel[_ngcontent-%COMP%] > pre[_ngcontent-%COMP%]{margin:0;padding:18px;min-height:420px;max-height:65vh;overflow:auto;background:#0d0e11;color:#cdd6f4;font:12px/1.6 ui-monospace,monospace;white-space:pre-wrap}.runs-panel[_ngcontent-%COMP%] table[_ngcontent-%COMP%]{width:100%;border-collapse:collapse;background:#16171c;font-size:12px}.runs-panel[_ngcontent-%COMP%] th[_ngcontent-%COMP%], .runs-panel[_ngcontent-%COMP%] td[_ngcontent-%COMP%]{padding:12px;text-align:left;border-bottom:1px solid #292c34}.runs-panel[_ngcontent-%COMP%] th[_ngcontent-%COMP%]{color:#777e8b;text-transform:uppercase;font-size:10px}.runs-panel[_ngcontent-%COMP%] a[_ngcontent-%COMP%]{color:#a5b4fc}.status[_ngcontent-%COMP%]{font-size:10px;text-transform:uppercase}.status[data-status=completed][_ngcontent-%COMP%]{color:#34d399}.status[data-status=failed][_ngcontent-%COMP%], .status[data-status=cancelled][_ngcontent-%COMP%]{color:#f87171}.status[data-status=running][_ngcontent-%COMP%]{color:#60a5fa}.provider-badge[_ngcontent-%COMP%]{display:inline-block;border:1px solid var(--border-bright);border-radius:4px;background:var(--surface-elevated);color:#c4b5fd;font:10px ui-monospace,monospace;padding:1px 6px;margin-left:8px;vertical-align:1px}.provider-badge.inherited[_ngcontent-%COMP%]{color:var(--text-tertiary);border-style:dashed;background:transparent}.history-tag[_ngcontent-%COMP%]{border:1px dashed var(--border-bright);color:var(--text-tertiary);padding:1px 5px;border-radius:4px;font-size:9px;text-transform:uppercase;margin-left:6px}.empty[_ngcontent-%COMP%], .state[_ngcontent-%COMP%]{padding:24px;text-align:center;color:#858c98}.state.error[_ngcontent-%COMP%]{color:#fca5a5}@media(max-width:900px){.detail-page[_ngcontent-%COMP%]{padding:18px 14px}.hero[_ngcontent-%COMP%]{flex-wrap:wrap}.hero-actions[_ngcontent-%COMP%]{width:100%;margin-left:61px}.stats[_ngcontent-%COMP%]{grid-template-columns:repeat(2,1fr)}.stats[_ngcontent-%COMP%] div[_ngcontent-%COMP%]{border-bottom:1px solid #2d3038}.tab-content[_ngcontent-%COMP%]{grid-template-columns:1fr}.node-row[_ngcontent-%COMP%]{grid-template-columns:28px 38px 1fr}.dependency[_ngcontent-%COMP%]{grid-column:3}.inspector[_ngcontent-%COMP%]{position:static}}@media(max-width:600px){.stats[_ngcontent-%COMP%]{grid-template-columns:1fr}.hero-actions[_ngcontent-%COMP%]{margin-left:0}.tabs[_ngcontent-%COMP%]{overflow:auto}.node-row[_ngcontent-%COMP%]{grid-template-columns:28px 1fr}.type-icon[_ngcontent-%COMP%]{display:none}.node-main[_ngcontent-%COMP%], .dependency[_ngcontent-%COMP%]{grid-column:2}.dag-canvas[_ngcontent-%COMP%]{padding:18px}}"]})};export{le as WorkflowDetailComponent};
|