PageSourceSearch

https://innogist.ai/assets/PromoteA3ToSpecsPage-2kUgIrae.js

js innogist.ai collected 2026-10-05 15:16:56 UTC 18,608 bytes, 1 lines download raw bytes

1import{j as e,bc as w,cf as G,cg as O,bd as C,bq as _,L as V,be as v,r as f,c3 as U,av as K,aq as Y,a9 as Z,Z as J,e as M,d as B,c as S,U as X,f as ee,u as te,ce as ae,b as T,ao as re,aj as se,I as ne}from"./index-sy1_3QS7.js";import{P as de}from"./PageHeader-CmmBonTW.js";import{S as ie}from"./SpecWizard-Dz3bk-lQ.js";import"./Breadcrumbs-qrbEhwT8.js";import"./useSpecs-D4vE-qaU.js";import"./fieldsForKind-G51OdjSY.js";function oe({a3Title:t,a3Background:s,a3TargetCondition:c}){return e.jsxs(w,{children:[e.jsx(G,{children:e.jsxs(O,{className:"text-h5",children:["A3: ",t]})}),e.jsxs(C,{className:"space-y-4 text-sm",children:[s&&e.jsxs("div",{className:"space-y-1",children:[e.jsx("div",{className:"text-xs font-semibold uppercase tracking-wide text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Background"}),e.jsx("p",{className:"whitespace-pre-line leading-relaxed text-brand-text-primary dark:text-brand-dark-text-primary",children:s})]}),c&&e.jsxs("div",{className:"space-y-1",children:[e.jsx("div",{className:"text-xs font-semibold uppercase tracking-wide text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Target condition"}),e.jsx("p",{className:"whitespace-pre-line leading-relaxed text-brand-text-primary dark:text-brand-dark-text-primary",children:c})]})]})]})}function ce(t){return t<.4?"error":t<.6?"warning":"success"}function le({readiness:t,onProceed:s,onBack:c}){const{score:m,can_promote:i,missing_fields:n,existing_specs:r}=t,u=!i,g=!u&&m<.6,b=r.length>0;return e.jsxs(w,{children:[e.jsx(G,{children:e.jsxs(O,{className:"flex items-center gap-3",children:["Readiness check",e.jsxs(_,{variant:ce(m),dot:!0,children:["Score ",m.toFixed(2)]}),b&&e.jsxs(_,{variant:"info",children:[r.length," existing spec",r.length===1?"":"s"]})]})}),e.jsxs(C,{className:"space-y-4",children:[u&&e.jsx("p",{className:"text-sm text-status-error dark:text-red-300",children:"This A3 cannot be promoted yet. The countermeasures section must contain at least 20 characters."}),g&&e.jsx("p",{className:"text-sm text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"This A3 scored below 0.6. You can still proceed, but specs generated from a weak A3 are usually weak too. Consider filling in the items below first."}),!u&&!g&&b&&e.jsx("p",{className:"text-sm text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"This A3 has already been promoted. Re-promoting will create additional specs alongside the existing ones — slugs will be auto-suffixed to avoid collision."}),n.length>0&&e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium mb-2",children:"To improve spec quality:"}),e.jsx("ul",{className:"text-sm list-disc pl-5 space-y-1 text-brand-text-secondary dark:text-brand-dark-text-secondary",children:n.map((h,o)=>e.jsx("li",{children:h},o))})]}),b&&e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium mb-2",children:"Existing specs from this A3:"}),e.jsx("ul",{className:"text-sm list-disc pl-5 space-y-1",children:r.map(h=>e.jsxs("li",{children:[e.jsx(V,{to:`/specs/${h.id}`,className:"text-brand-primary hover:underline",children:h.title}),e.jsx("span",{className:"text-brand-text-secondary dark:text-brand-dark-text-secondary ml-2 font-mono text-xs",children:h.slug})]},h.id))})]}),e.jsxs("div",{className:"flex gap-2 pt-2",children:[!u&&e.jsx(v,{variant:"secondary",onClick:s,children:b?"Promote again":"Proceed anyway"}),e.jsx(v,{variant:"primary",onClick:c,children:"Back to A3"})]})]})]})}function xe({initial:t,onConfirm:s,onCancel:c,isSubmitting:m}){const[i,n]=f.useState(t),r=(o,a)=>{n(i.map((d,p)=>p===o?{...d,...a}:d))},u=o=>{n(i.filter((a,d)=>d!==o).map((a,d)=>({...a,display_order:d+1})))},g=()=>{n([...i,{display_order:i.length+1,title:"",description:"",ai_confidence:null}])},b=i.length>0&&i.every(o=>o.title.trim()&&o.description.trim()),h=()=>{const o=i.map((a,d)=>({display_order:d+1,title:a.title.trim(),description:a.description.trim()}));s(o)};return e.jsxs("div",{className:"space-y-4",children:[e.jsx("h2",{className:"text-h4",children:"Confirm countermeasures"}),e.jsxs("p",{className:"text-sm text-brand-text-secondary dark:text-brand-dark-text-secondary",children:["The AI found ",t.length," countermeasure",t.length===1?"":"s"," in this A3. Edit, remove, or add items before generating spec drafts. One spec will be generated per row."]}),e.jsx("div",{className:"space-y-3",children:i.map((o,a)=>e.jsx("div",{className:"rounded-lg border border-brand-border dark:border-brand-dark-border bg-brand-bg-card dark:bg-brand-dark-bg-card p-4",children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(U,{className:"w-4 h-4 text-brand-text-muted dark:text-brand-dark-text-muted mt-3","aria-hidden":!0}),e.jsxs("div",{className:"flex-1 space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs text-brand-text-muted dark:text-brand-dark-text-muted",children:["#",a+1]}),typeof o.ai_confidence=="number"&&o.ai_confidence<.5&&e.jsx(_,{variant:"warning",children:"Low confidence — please review"})]}),e.jsx("input",{type:"text",className:"w-full text-sm font-medium rounded border border-brand-border dark:border-brand-dark-border bg-white dark:bg-brand-dark-input text-gray-900 dark:text-brand-dark-text-primary px-2 py-1",placeholder:"Title",value:o.title,onChange:d=>r(a,{title:d.target.value})}),e.jsx("textarea",{className:"w-full text-sm rounded border border-brand-border dark:border-brand-dark-border bg-white dark:bg-brand-dark-input text-gray-900 dark:text-brand-dark-text-primary px-2 py-1 min-h-[60
1px]",placeholder:"Description",value:o.description,onChange:d=>r(a,{description:d.target.value})})]}),e.jsx("button",{type:"button",onClick:()=>u(a),"aria-label":`Remove countermeasure ${a+1}`,className:"p-1 text-brand-text-muted dark:text-brand-dark-text-muted hover:text-status-danger",children:e.jsx(K,{className:"w-4 h-4"})})]})},a))}),e.jsx(v,{variant:"ghost",leftIcon:e.jsx(Y,{className:"w-4 h-4"}),onClick:g,children:"Add countermeasure"}),e.jsxs("div",{className:"flex justify-end gap-2 pt-4 border-t border-brand-border",children:[e.jsx(v,{variant:"secondary",onClick:c,disabled:m,children:"Cancel"}),e.jsxs(v,{variant:"primary",onClick:h,disabled:!b||m,isLoading:m,children:["Generate ",i.length," spec draft",i.length===1?"":"s"]})]})]})}const F={code:"Code",process:"Process",hybrid:"Hybrid"},me={code:"A coding agent can build it (PR-shaped work).",process:"Organizational/human action — no agent involvement (hiring, policy, meeting cadence).",hybrid:"Both. Will be split before dispatch."},ue={code:"info",process:"warning",hybrid:"default"};function pe({draft:t,index:s,startExpanded:c,skipped:m,onChange:i,onToggleSkip:n}){const[r,u]=f.useState(c),g=(a,d)=>i({...t,[a]:d}),b=(a,d)=>{const p=t.acceptance_criteria.map((P,I)=>I===a?{...P,...d}:P);i({...t,acceptance_criteria:p})},h=()=>{i({...t,acceptance_criteria:[...t.acceptance_criteria,{display_order:t.acceptance_criteria.length+1,given_text:"",when_text:"",then_text:"",is_blocking:!0,eval_type:"manual"}]})},o=a=>{i({...t,acceptance_criteria:t.acceptance_criteria.filter((d,p)=>p!==a).map((d,p)=>({...d,display_order:p+1}))})};return e.jsxs("div",{className:`rounded-lg border ${m?"border-dashed opacity-50":"border-brand-border dark:border-brand-dark-border"} bg-brand-bg-card dark:bg-brand-dark-bg-card`,children:[e.jsxs("button",{type:"button",className:"w-full flex items-center gap-2 px-4 py-3 text-left",onClick:()=>u(!r),"aria-expanded":r,children:[r?e.jsx(Z,{className:"w-4 h-4 shrink-0"}):e.jsx(J,{className:"w-4 h-4 shrink-0"}),e.jsxs("span",{className:"text-xs text-brand-text-muted dark:text-brand-dark-text-muted",children:["#",s+1]}),t.source_title&&e.jsxs(_,{variant:"default",children:["from: ",t.source_title]}),e.jsx("span",{className:"flex-1 font-medium truncate",children:t.title||"(untitled)"}),e.jsx(_,{variant:ue[t.kind],children:F[t.kind]}),typeof t.ai_confidence=="number"&&e.jsxs(_,{variant:t.ai_confidence>=.7?"success":"warning",children:[Math.round(t.ai_confidence*100),"%"]}),e.jsxs("label",{className:"flex items-center gap-1 text-xs shrink-0",onClick:a=>a.stopPropagation(),children:[e.jsx("input",{type:"checkbox",checked:m,onChange:n}),"Skip"]})]}),r&&!m&&e.jsxs("div",{className:"px-4 pb-4 space-y-3 border-t border-brand-border",children:[e.jsx(N,{label:"Title",value:t.title,onChange:a=>g("title",a)}),e.jsxs("div",{className:"block space-y-1",children:[e.jsx("span",{className:"text-xs font-semibold uppercase text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Execution kind"}),e.jsx("div",{className:"flex gap-1 rounded border border-brand-border dark:border-brand-dark-border p-0.5 w-fit",children:["code","process","hybrid"].map(a=>e.jsx("button",{type:"button",onClick:()=>g("kind",a),className:`px-3 py-1 text-xs rounded transition-colors ${t.kind===a?"bg-amber-700 dark:bg-ai-accent text-white dark:text-brand-dark-text-inverse":"text-brand-text-secondary dark:text-brand-dark-text-secondary hover:bg-brand-bg-subtle dark:hover:bg-brand-dark-bg-subtle"}`,children:F[a]},a))}),e.jsx("p",{className:"text-xs text-brand-text-secondary dark:text-brand-dark-text-secondary mt-1",children:me[t.kind]})]}),e.jsx(N,{label:"Slug",value:t.slug,onChange:a=>g("slug",a),mono:!0}),e.jsx(N,{label:"Problem statement",value:t.problem_statement,onChange:a=>g("problem_statement",a),multiline:!0}),e.jsx(N,{label:"Desired outcome",value:t.desired_outcome,onChange:a=>g("desired_outcome",a),multiline:!0}),e.jsx(q,{label:"Invariants",values:t.invariants,onChange:a=>g("invariants",a)}),e.jsx(q,{label:"Non-goals",values:t.non_goals,onChange:a=>g("non_goals",a)}),e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:"text-xs font-semibold uppercase text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Acceptance criteria"}),t.acceptance_criteria.map((a,d)=>e.jsxs("div",{className:"rounded border border-brand-border p-2 space-y-1",children:[e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsxs("span",{className:"text-xs text-brand-text-muted dark:text-brand-dark-text-muted",children:["#",a.display_order]}),e.jsx("button",{type:"button",onClick:()=>o(d),"aria-label":`Remove AC ${d+1}`,className:"text-brand-text-muted dark:text-brand-dark-text-muted hover:text-status-error transition-colors",children:e.jsx(K,{className:"w-3 h-3"})})]}),e.jsx(N,{small:!0,label:"Given",value:a.given_text,onChange:p=>b(d,{given_text:p})}),e.jsx(N,{small:!0,label:"When",value:a.when_text,onChange:p=>b(d,{when_text:p})}),e.jsx(N,{small:!0,label:"Then",value:a.then_text,onChange:p=>b(d,{then_text:p})})]},d)),e.jsx(v,{variant:"ghost",onClick:h,children:"+ Add criterion"})]})]})]})}function N({label:t,value:s,onChange:c,multiline:m,mono:i,small:n}){const r=`w-full rounded border border-brand-border dark:border-brand-dark-border bg-white dark:bg-brand-dark-input text-gray-900 dark:text-brand-dark-text-primary focus:outline-none focus:ring-1 focus:ring-focus ${n?"px-2 py-1 text-xs":"px-3 py-2 text-sm"} ${i?"font-mono":""}`;return e.jsxs("label",{className:"block",children:[e.jsx("span",{className:"text-xs font-semibold uppercase text-brand-text-secondary dark:text-brand-dark-text-secondary",children:t}),m?e.jsx("textarea",{className:`${r} min-h-[80px] mt-1`,value:s,onChange:u=>c(u.target.value)}):e.jsx("input",{type:"text",className:`${r} mt-1`,value:s,onChange:u=>c(u.target.value)})]})}function q({label:t,values:s,onChange:c}){return e.jsxs("div",{className:"block space-y-1",children:[e.jsx("span",{className:"text-xs font-semibold uppercase text-brand-text-secondary dark:text-brand-dark-text-secondary",children:t}),s.map((m,i)=>e.jsxs("div",{className:"flex gap-1",children:[e.jsx("input",{type:"text",className:"flex-1 rounded border border-brand-border dark:border-brand-dark-border bg-white dark:bg-brand-dark-input text-gray-900 dark:text-brand-dark-text-primary px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-focus",value:m,onChange:n=>c(s.map((r,u)=>u===i?n.target.value:r))}),e.jsx("button",{type:"button",onClick:()=>c(s.filter((n,r)=>r!==i)),"aria-label":`Remove ${t.toLowerCase()} ${i+1}`,className:"px-2 text-brand-text-muted dark:text-brand-dark-text-muted hover:text-status-error transition-colors",children:"×"})]},i)),e.jsx("button",{type:"button",onClick:()=>c([...s,""]),className:"text-xs text-brand-primary hover:underline",children:"+ Add"})]})}function be(t){return B({mutationFn:async()=>{const{data:s}=await S.post(`/a3-documents/${t}/promote-to-specs/extract-countermeasures`,{});return s}})}function he(t){return B({mutationFn:async s=>{const{data:c}=await S.post(`/a3-documents/${t}/promote-to-specs/preview`,s);return c}})}function ge(t){return B({mutationFn:async s=>{const{data:c}=await S.post(`/a3-documents/${t}/promote-to-specs/commit`,s);return c}})}function ke(t){return M({queryKey:["a3-promotion-readiness",t],queryFn:async()=>{const{data:s}=await S.get(`/a3-documents/${t}/promotion-readiness`);return s},enabled:!!t,staleTime:0})}function Ce(){const{a3Id:t}=X(),s=ee(),{showToast:c}=te(),m=ae(l=>l.currentOrganization),i=(m==null?void 0:m.id)??"",[n,r]=f.useState("readiness"),[u,g]=f.useState(null),[b,h]=f.useState([]),[o,a]=f.useState(new Set),[d,p]=f.useState(!1),[P,I]=f.useState(!1),[L,z]=f.useState(null),R=M({queryKey:["a3-document",t],queryFn:async()=>(await S.get(`/a3-documents/${t}`)).data,enabled:!!t}),j=ke(t),A=be(t),$=he(t),D=ge(t);f.useEffect(()=>{if(!t||n!=="readiness"||!j.data)return;const l=j.data;l.can_promote&&l.score>=.6&&l.existing_specs.length===0&&r("extract")},[t,n,j.data]),f.useEffect(()=>{!t||n!=="extract"||A.data||A.isPending||A.mutate(void 0,{onSuccess:()=>r("confirm"),onError:l=>T(l,"Could not extract countermeasures from this A3.")})},[t,n]);const W=l=>{g(l),r("preview"),$.mutate({countermeasures:l,user_note:null},{onSuccess:x=>{h(x.drafts),a(new Set),r("review")},onError:x=>{T(x,"Could not generate spec drafts from the confirmed countermeasures."),r("confirm")}})},H=()=>{const l=b.filter((x,k)=>!o.has(k));l.length!==0&&(z(null),r("saving"),D.mutate({drafts:l,force:P},{onSuccess:x=>{var k;c({message:`${x.count} spec${x.count===1?"":"s"} created from A3 ${((k=R.data)==null?void 0:k.title)??""}`,variant:"success"}
1),x.renamed_slugs.forEach(E=>{c({message:`Slug "${E.requested}" was renamed to "${E.saved}" because it already existed`,variant:"info"})}),s(`/specs?source_a3_id=${t}`)},onError:x=>{var k;r("review"),ne(x)&&((k=x.response)==null?void 0:k.status)===409?z('This A3 already has promoted specs. Go back to the readiness step and click "Promote again" to proceed.'):T(x,"Could not save the specs.")}}))};if(!t)return e.jsx("div",{className:"p-8 text-status-danger",children:"Missing A3 ID."});if(R.isLoading)return e.jsx("div",{className:"p-8",children:"Loading A3…"});const y=R.data;return e.jsxs("div",{className:"w-full",children:[e.jsx("header",{className:"bg-white dark:bg-brand-dark-bg-card border-b border-gray-200 dark:border-brand-dark-border px-3 py-2 sm:px-6 sm:py-4",children:e.jsx(de,{title:"Promote A3 to Specs",subtitle:"Generate first-class spec drafts from the countermeasures in this A3. One spec per countermeasure; you can edit, skip, or regenerate before saving.",icon:e.jsx(re,{className:"w-8 h-8 text-brand-primary"}),backButton:{label:"Back to A3",onClick:()=>s(-1)},actions:e.jsx(v,{variant:"secondary",onClick:()=>p(!0),children:"Draft with AI wizard"})})}),d&&e.jsx(se,{isOpen:!0,onClose:()=>p(!1),size:"xl",title:"Draft a spec from this A3",children:e.jsx(ie,{organizationId:i,seed:{a3_id:t,contextChips:[`A3: ${(y==null?void 0:y.title)??"(untitled)"}`]},onCreated:l=>{p(!1),s(`/specs/${l}`)},onClose:()=>p(!1)})}),e.jsxs("div",{className:"w-full px-4 py-6 space-y-6",children:[e.jsx(oe,{a3Title:(y==null?void 0:y.title)??"(untitled)",a3Background:y==null?void 0:y.background,a3TargetCondition:y==null?void 0:y.target_condition}),n==="readiness"&&e.jsxs(e.Fragment,{children:[j.isLoading&&e.jsx(w,{children:e.jsx(C,{children:e.jsx("p",{className:"text-sm text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Checking A3 readiness…"})})}),j.error&&e.jsx(w,{children:e.jsxs(C,{children:[e.jsx("p",{className:"text-sm text-status-danger",children:"Could not check readiness. "}),e.jsx(v,{variant:"secondary",onClick:()=>s(-1),children:"Back to A3"})]})}),j.data&&!(j.data.can_promote&&j.data.score>=.6&&j.data.existing_specs.length===0)&&e.jsx(le,{readiness:j.data,a3Id:t,onProceed:()=>{j.data.existing_specs.length>0&&I(!0),r("extract")},onBack:()=>s(-1)})]}),n==="extract"&&e.jsx(w,{children:e.jsx(C,{children:e.jsx("p",{className:"text-sm text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Reading countermeasures… usually 5–15 seconds."})})}),n==="confirm"&&A.data&&e.jsx(w,{children:e.jsx(C,{children:e.jsx(xe,{initial:u??A.data.countermeasures,onConfirm:W,onCancel:()=>s(-1),isSubmitting:$.isPending})})}),n==="preview"&&e.jsx(w,{children:e.jsx(C,{children:e.jsx("p",{className:"text-sm text-brand-text-secondary dark:text-brand-dark-text-secondary",children:"Generating spec drafts… usually 10–30 seconds."})})}),(n==="review"||n==="saving")&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"space-y-3",children:b.map((l,x)=>e.jsx(pe,{draft:l,index:x,startExpanded:x===0,skipped:o.has(x),onChange:k=>h(b.map((E,Q)=>Q===x?k:E)),onToggleSkip:()=>{const k=new Set(o);k.has(x)?k.delete(x):k.add(x),a(k)}},x))}),L&&e.jsx("div",{role:"alert",className:"rounded-md bg-status-error-bg dark:bg-status-error/10 border border-status-error text-status-error dark:text-red-300 px-4 py-3 text-sm",children:L}),e.jsxs("div",{className:"sticky bottom-0 bg-brand-bg-page dark:bg-brand-dark-bg-page p-4 border-t border-brand-border dark:border-brand-dark-border flex justify-between",children:[e.jsxs("div",{className:"flex gap-2",children:[e.jsx(v,{variant:"secondary",onClick:()=>{h([]),r("confirm")},disabled:n==="saving",children:"Back to countermeasures"}),e.jsx(v,{variant:"ghost",onClick:()=>{u&&(r("preview"),$.mutate({countermeasures:u,user_note:null},{onSuccess:l=>{h(l.drafts),a(new Set),r("review")},onError:l=>{T(l,"Could not regenerate the spec drafts."),r("review")}}))},disabled:n==="saving"||$.isPending,children:"Regenerate drafts"})]}),e.jsxs(v,{variant:"primary",onClick:H,disabled:n==="saving"||D.isPending||b.length-o.size===0,isLoading:n==="saving"||D.isPending,children:["Save ",b.length-o.size," spec",b.length-o.size===1?"":"s"]})]})]})]})]})}export{Ce as PromoteA3ToSpecsPage,Ce as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.