scrum-skills 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ .tests-page>.card[data-v-d69ddc53],.tests-page>.kpis[data-v-d69ddc53]{margin-bottom:12px;scroll-margin-top:96px}.layers[data-v-d69ddc53],.page-anchor[data-v-d69ddc53],.tests-page>.callout[data-v-d69ddc53]{margin-bottom:12px}.page-anchor[data-v-d69ddc53]{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.anchor-label[data-v-d69ddc53]{color:var(--ink-4);font-size:11px}.anchor-chip[data-v-d69ddc53]{border:1px solid var(--line);height:26px;color:var(--ink-2);background:#fff;border-radius:999px;align-items:center;padding:0 10px;font-size:11.2px;display:inline-flex}.anchor-chip[data-v-d69ddc53]:hover{border-color:var(--violet-line);background:var(--violet-soft);color:var(--violet-ink)}.layers[data-v-d69ddc53]{grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;display:grid}.layer-card[data-v-d69ddc53]{padding:12px 14px}.layer-hd[data-v-d69ddc53]{align-items:center;gap:7px;font-size:12.6px;display:flex}.layer-dot[data-v-d69ddc53]{border-radius:999px;width:8px;height:8px}.layer-body[data-v-d69ddc53]{flex-wrap:wrap;gap:10px;margin-top:8px;font-size:11.4px;display:flex}.ok[data-v-d69ddc53]{color:var(--green)}.bad[data-v-d69ddc53]{color:var(--rose)}.panel[data-v-d69ddc53]{margin:0}.cover-grid[data-v-d69ddc53]{grid-template-columns:minmax(0,1.4fr) minmax(0,1fr);gap:18px;display:grid}.cover-big[data-v-d69ddc53]{letter-spacing:-.03em;font-size:30px;font-weight:700}.cover-big span[data-v-d69ddc53]{color:var(--ink-4);font-size:14px;font-weight:600}.cover-main .bar[data-v-d69ddc53]{background:var(--sunken-2);border-radius:999px;height:6px;margin:8px 0;overflow:hidden}.cover-main .bar i[data-v-d69ddc53]{background:var(--grad);height:100%;display:block}.cover-sub[data-v-d69ddc53]{color:var(--ink-2);font-size:12px}.cover-side[data-v-d69ddc53]{border-left:1px dashed var(--line-2);align-content:start;gap:6px;padding-left:18px;display:grid}.stat-row[data-v-d69ddc53]{align-items:center;gap:7px;font-size:11.8px;display:flex}.stat-row b[data-v-d69ddc53]{font-family:var(--font-mono);margin-left:auto}.dot[data-v-d69ddc53]{border-radius:999px;width:7px;height:7px}.cases[data-v-d69ddc53],.matrix-wrap[data-v-d69ddc53]{overflow-x:auto}.ttl[data-v-d69ddc53]{margin-left:7px;font-size:12px}.zero[data-v-d69ddc53]{color:var(--ink-4)}.case-search[data-v-d69ddc53]{width:250px;height:30px;margin-left:auto}.filters[data-v-d69ddc53]{flex-wrap:wrap;margin-bottom:10px;display:flex}.chip[data-v-d69ddc53],.filters[data-v-d69ddc53]{align-items:center;gap:5px}.chip[data-v-d69ddc53]{border:1px solid var(--line);height:26px;color:var(--ink-2);background:#fff;border-radius:999px;padding:0 10px;font-size:11.2px;display:inline-flex}.chip.on[data-v-d69ddc53]{background:var(--violet-soft);border-color:var(--violet-line);color:var(--violet-ink);font-weight:650}.chip .n[data-v-d69ddc53]{font-family:var(--font-mono);color:var(--ink-4);font-size:10px}.filters.inline[data-v-d69ddc53]{margin:0 0 0 auto}.sep[data-v-d69ddc53]{background:var(--line-2);width:1px;height:16px;margin:0 4px}.case-row[data-v-d69ddc53]{cursor:pointer}.case-row[data-v-d69ddc53]:hover{background:var(--sunken-2)}.code[data-v-d69ddc53]{color:var(--ink-4);margin-top:2px;font-size:10.2px;display:block}.lv[data-v-d69ddc53]{margin-left:5px;font-size:10.6px}.covers[data-v-d69ddc53]{flex-wrap:wrap;gap:3px;max-width:220px;display:flex}.cov-ref[data-v-d69ddc53]{background:var(--violet-soft);color:var(--violet-ink);border-radius:5px;padding:0 5px;font-size:10px}.detail-row td[data-v-d69ddc53],tr.last td[data-v-d69ddc53]{background:var(--sunken-2)}.detail[data-v-d69ddc53]{gap:5px;padding:4px 0 6px;font-size:11.6px;line-height:1.6;display:grid}.steps[data-v-d69ddc53]{color:var(--ink-2);margin:0;padding-left:18px}.gap-grid[data-v-d69ddc53]{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;display:grid}.h3[data-v-d69ddc53]{color:var(--ink-2);align-items:center;gap:5px;font-weight:650;display:flex}.gap-list[data-v-d69ddc53],.h3[data-v-d69ddc53]{font-size:11.6px}.gap-list[data-v-d69ddc53]{gap:6px;max-height:260px;margin:8px 0 0;padding:0;list-style:none;display:grid;overflow-y:auto}.gap-list li[data-v-d69ddc53]{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.gap-text[data-v-d69ddc53]{color:var(--ink-3)}.cov-cell[data-v-d69ddc53]{gap:8px;display:flex}.link[data-v-d69ddc53]{font-weight:640}.link[data-v-d69ddc53]:hover{color:var(--violet-ink)}.foot[data-v-d69ddc53]{margin-top:7px;font-size:10.6px}@media (width<=1080px){.cover-grid[data-v-d69ddc53],.gap-grid[data-v-d69ddc53],.layers[data-v-d69ddc53]{grid-template-columns:minmax(0,1fr)}.cover-side[data-v-d69ddc53]{border-left:0;padding-left:0}}
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/pIF8vEtK.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dx3CNA--.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/pIF8vEtK.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/pIF8vEtK.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"4a212bd2-438f-4b26-b8f3-57db47ca0799",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793371120775,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640995,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/pIF8vEtK.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dx3CNA--.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/pIF8vEtK.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/pIF8vEtK.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"4a212bd2-438f-4b26-b8f3-57db47ca0799",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793371120776,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640996,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/pIF8vEtK.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dx3CNA--.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/pIF8vEtK.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/pIF8vEtK.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"4a212bd2-438f-4b26-b8f3-57db47ca0799",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793371120777,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640996,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/pIF8vEtK.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dx3CNA--.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/pIF8vEtK.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/pIF8vEtK.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"4a212bd2-438f-4b26-b8f3-57db47ca0799",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793371120778,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640997,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/pIF8vEtK.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dx3CNA--.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/pIF8vEtK.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/pIF8vEtK.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"4a212bd2-438f-4b26-b8f3-57db47ca0799",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793371120777,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640997,false]</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/pIF8vEtK.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.Dx3CNA--.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/pIF8vEtK.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/pIF8vEtK.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"4a212bd2-438f-4b26-b8f3-57db47ca0799",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793371120777,false]</script></body></html>
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640997,false]</script></body></html>
@@ -0,0 +1 @@
1
+ <!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>敏捷看板</title><script type="importmap">{"imports":{"#entry":"/_nuxt/DkI1Qxpp.js"}}</script><link rel="stylesheet" href="/_nuxt/entry.CKHePurH.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DkI1Qxpp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DK3Fl9T5.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D2O8Q8x9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DZo3dYLJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/51OWHj8c.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B3jkABw9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/6gFtTBwm.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DSaWoFHS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Csqgy925.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C7K0R7F-.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/ChG76uh9.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CJ2mz0WY.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DEfLc7p3.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DLkAKotw.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Vo4wMbnS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CuJUHLl8.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/CK9_veoz.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bb3Limos.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Dtsa-1NS.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Ci0qbkgh.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BuOKDFiJ.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/D3YCAmfu.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/B_SlFcWp.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/Bi9Axj3V.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/rlePnzpc.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/DfR3IZN_.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/EYTShbt4.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BgLeKwx0.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/C9StZL3D.js"><link rel="modulepreload" as="script" crossorigin href="/_nuxt/BPTrRhcw.js"><script type="module" src="/_nuxt/DkI1Qxpp.js" crossorigin></script><link rel="prefetch" as="script" crossorigin href="/_nuxt/CSSb154I.js"><link rel="prefetch" as="script" crossorigin href="/_nuxt/DHihWhf5.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-404.BFmXOnCT.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/B5n2_jZN.js"><link rel="prefetch" as="style" crossorigin href="/_nuxt/error-500.BZ-DdjQ6.css"><link rel="prefetch" as="script" crossorigin href="/_nuxt/ZVTqot-_.js"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="apple-touch-icon" href="/favicon.svg"></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/",buildId:"8e23aec9-5b67-4569-b8bf-d4f07e50ffdf",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1793377640997,false]</script></body></html>
@@ -66,12 +66,14 @@ function init() {
66
66
  // 交付说明的写入工具:纪律要求「写之前先看 git」,工具得跟着装过去
67
67
  cpSync(join(PKG_ROOT, 'tools/last-change.mjs'), join(dir, 'tools/last-change.mjs'));
68
68
 
69
- // 数据骨架:schema v2 三文件结构(已有文件不覆盖,避免踩坏真实数据)
69
+ // 数据骨架:schema v3(已有文件不覆盖,避免踩坏真实数据)
70
70
  mkdirSync(join(dir, 'agile'), { recursive: true });
71
71
  const skeleton = {
72
72
  'backlog.json': { schemaVersion: 3, epics: [], stories: [] },
73
73
  'sprints.json': { schemaVersion: 3, sprints: [], milestones: [] },
74
74
  'journal.json': { schemaVersion: 3, journals: [] },
75
+ // 测试资产:页面空状态也认这个文件,先给空壳,agent 收口时往里加用例
76
+ 'tests.json': { schemaVersion: 3, suites: [], runs: [] },
75
77
  };
76
78
  const created = [];
77
79
  for (const [name, body] of Object.entries(skeleton)) {
@@ -85,7 +87,7 @@ function init() {
85
87
  - .agents/skills/ 四个敏捷 skill(planning / sprint / tdd / report)
86
88
  - AGENTS.md agent 入口纪律文件${backed ? `(旧文件已备份为 ${basename(backed)})` : ''}
87
89
  - tools/validate.mjs 数据校验脚本
88
- - agile/ 三文件结构${created.length ? `(新建 ${created.join('、')})` : '(已存在,未覆盖)'}
90
+ - agile/ 数据骨架${created.length ? `(新建 ${created.join('、')})` : '(已存在,未覆盖)'}
89
91
 
90
92
  下一步:
91
93
  1. 在目标项目里让 AI 读 AGENTS.md,用 scrum-planning 建第一个 story
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrum-skills",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "结构化敏捷开发管理 skills + CLI:AI 对话产出结构化敏捷数据,TDD 驱动实施,Nuxt 看板可视化",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -12,7 +12,11 @@
12
12
  - 看板/燃尽图/追溯链可视化 → **scrum-report**
13
13
  3. **对话产出必须落库**。凡是确认要做的事,必须写进 `agile/backlog.json`(结构见 `.agents/skills/scrum-planning/references/schema.md`),禁止只停留在对话里。每条故事必须带 `type` / `priority` / `value`,验收标准必须带 `verifyBy`;依赖写 `dependsOn`,被卡住写 `blocked`,中途插入或拆分出来的写 `scopeChange`——这些不写进 JSON,看板和甘特图就读不到。
14
14
  4. **TDD 铁律**:任务实施走 red → green → refactor 循环,没写过失败测试不许写实现;每步过程必须当场追加到 `agile/journal.json` 里对应 task 的 `entries`(不许事后补记)。
15
- 5. **每次修改敏捷数据后必须运行校验**,校验不过不许继续:
15
+ 5. **测试资产随任务收口登记**。task 推到 `done`、story 送进 `review` 之前,必须把本次新增 / 修改的用例与执行结果写进 **`agile/tests.json`**(结构见 schema 第 5 节):
16
+ - 用例挂在 suite 下,`covers` 指向验收(`S-001-A1`),`expect` 写清预期结果;手工验证也要登记成 `automated: false` 的用例,`steps` 逐步写;
17
+ - 每次跑完测试追加一条 `runs`(命令 / 环境 / 总数 / 通过 / 失败 / 跳过 / 失败用例),并刷新对应用例的 `status` 与 `lastRun`;
18
+ - **没真跑过不许写 `pass`**;story 到 `review` 时每条验收至少被一条用例覆盖(校验器会查,缺口在 `/tests` 页的「缺口与风险」里)。
19
+ 6. **每次修改敏捷数据后必须运行校验**,校验不过不许继续:
16
20
 
17
21
  ```bash
18
22
  node tools/validate.mjs
@@ -24,13 +28,13 @@
24
28
  pnpm test
25
29
  ```
26
30
 
27
- 6. **可视化**:
31
+ 7. **可视化**:
28
32
 
29
33
  ```bash
30
- node tools/serve.mjs # http://localhost:4173 看板 / 燃尽图 / story 追溯链
34
+ node tools/serve.mjs # http://localhost:4173 看板 / 燃尽图 / 测试用例 / story 追溯链
31
35
  ```
32
36
 
33
- 7. **每次功能开发完毕必须交工汇报**。task 推到 `done` 之后、向用户回话之前,把本次交付写进 **`agile/last-change.json`**(结构见 `.agents/skills/scrum-planning/references/schema.md` 第 4 节)。**写之前先查 git**:文件已提交就整份覆盖;还有没提交的改动就把本次结果并进旧记录,别冲掉还没入库的那一次。用 `node tools/last-change.mjs write <记录.json>` 写,它自己判状态、合并、写后校验、失败回滚。然后按同一顺序告诉用户:
37
+ 8. **每次功能开发完毕必须交工汇报**。task 推到 `done` 之后、向用户回话之前,把本次交付写进 **`agile/last-change.json`**(结构见 `.agents/skills/scrum-planning/references/schema.md` 第 4 节)。**写之前先查 git**:文件已提交就整份覆盖;还有没提交的改动就把本次结果并进旧记录,别冲掉还没入库的那一次。用 `node tools/last-change.mjs write <记录.json>` 写,它自己判状态、合并、写后校验、失败回滚。然后按同一顺序告诉用户:
34
38
  1. 本次涉及的故事与任务:新增还是状态变更、变更内容、对应涉及文件(用表格);
35
39
  2. 额外做了什么事情(计划外的修复、文档、重构);
36
40
  3. 验证了哪些结果(命令 / 手工核对的结论,含没验到的边界);
@@ -55,15 +59,16 @@
55
59
  | `agile/backlog.json` | 史诗 epics + 用户故事 stories(story 内联 tasks) |
56
60
  | `agile/sprints.json` | 迭代 sprints + 里程碑 milestones(MVP 节点 = `kind: "mvp"`) |
57
61
  | `agile/journal.json` | 每个 task 一条 TDD 实施日志(内联 entries) |
62
+ | `agile/tests.json` | 测试资产:suites(层 × 级别 → cases,含 covers 映射与最近状态)+ runs(执行记录与代码覆盖率);可选文件 |
58
63
  | `agile/last-change.json` | 最近一次交付的汇报(已提交才覆盖,未提交则合并):变更清单 / 额外工作 / 验证 / 入口与账号 / 测试前准备 / 测试场景 / 说明 |
59
- | `tools/validate.mjs` | 数据校验(引用完整性 / 状态机 / TDD 纪律 / 里程碑达成 / 交付说明) |
64
+ | `tools/validate.mjs` | 数据校验(引用完整性 / 状态机 / TDD 纪律 / 测试资产 / 里程碑达成 / 交付说明) |
60
65
  | `tools/last-change.mjs` | 交付说明的写入工具:`check` 判 git 状态、`write` 自动覆盖或合并 + 写后校验回滚 |
61
66
  | `tools/serve.mjs` | 看板 server(`/api/agile` + 托管 Nuxt 静态产物) |
62
- | `apps/board/` | Nuxt 看板源码(看板 / 最后更新 / 甘特图 / 里程碑 / 燃尽图 / 故事追溯) |
67
+ | `apps/board/` | Nuxt 看板源码(看板 / 最后更新 / 甘特图 / 里程碑 / 燃尽图 / 测试用例 / 故事追溯) |
63
68
  | `assets/board/` | 看板静态产物,由 `serve` 托管;**不进 git**,用 `pnpm board:build` 重建(`npm publish` 的 prepack 会自动跑一次) |
64
69
  | `scripts/build-board.mjs` | 构建看板:`apps/board` → `assets/board` |
65
70
  | `tests/` | 校验器回归测试(`pnpm test`,18 个用例) |
66
71
  | `docs/` | README 配图 |
67
72
  | `README.md` | 包说明(npm 页面) |
68
73
 
69
- `agile/` 三个文件里目前是一整套演示数据(3 史诗 / 16 故事 / 4 迭代 / 5 里程碑 / 39 任务),正式使用前清空三文件的数组即可。
74
+ `agile/` 数据文件里目前是一整套演示数据(3 史诗 / 16 故事 / 4 迭代 / 5 里程碑 / 39 任务),正式使用前清空这些文件的数组即可。
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * AI 助手手上的两件家伙事:检索当前项目的敏捷数据、按路径打补丁改它。
5
5
  * 这是全项目唯一「机器自动写业务数据」的入口,所以三层护栏缺一不可:
6
- * 1. 只认 agile/backlog.json | sprints.json | journal.json 三个文件,别的路径一律拒;
6
+ * 1. 只认 agile/backlog.json | sprints.json | journal.json | tests.json 四个文件,别的路径一律拒;
7
7
  * 2. 写完立刻跑 tools/validate.mjs,校验不过就把改前内容原样写回去;
8
8
  * 3. 手动审核策略下根本不落盘,只产待审变更(propose / applyPending / rejectPending)。
9
9
  *
@@ -25,7 +25,7 @@ import { fileURLToPath } from 'node:url';
25
25
  const validator = fileURLToPath(new URL('./validate.mjs', import.meta.url));
26
26
 
27
27
  /** 允许 AI 写回去的文件白名单——路径拼接只从这里来,杜绝越界 */
28
- export const AGILE_FILES = ['backlog', 'sprints', 'journal'];
28
+ export const AGILE_FILES = ['backlog', 'sprints', 'journal', 'tests'];
29
29
 
30
30
  /** 默认校验器:直接调本仓库的 validate.mjs,与 serve 启动前的口径完全一致 */
31
31
  export function runValidate(root) {
@@ -342,6 +342,12 @@ function recordsOf(file, doc) {
342
342
  (doc.milestones ?? []).forEach(m => add(`milestones.${m.id}`, m));
343
343
  } else if (file === 'journal') {
344
344
  (doc.journals ?? []).forEach(j => add(`journals.${j.taskId}`, j));
345
+ } else if (file === 'tests') {
346
+ (doc.suites ?? []).forEach(su => {
347
+ add(`suites.${su.id}`, su);
348
+ (su.cases ?? []).forEach(c => add(`suites.${su.id}.cases.${c.id}`, c));
349
+ });
350
+ (doc.runs ?? []).forEach(r => add(`runs.${r.id}`, r));
345
351
  }
346
352
  return out;
347
353
  }
@@ -385,7 +391,7 @@ export function createAgileTools({ root, validate = runValidate } = {}) {
385
391
 
386
392
  const fileOf = file => {
387
393
  if (!AGILE_FILES.includes(file)) {
388
- throw new Error(`只允许写 agile/ 下的三个文件(${AGILE_FILES.join(' / ')}.json),收到的是 ${file}`);
394
+ throw new Error(`只允许写 agile/ 下的数据文件(${AGILE_FILES.join(' / ')}.json),收到的是 ${file}`);
389
395
  }
390
396
  return join(currentRoot(), 'agile', `${file}.json`);
391
397
  };
package/tools/ai.mjs CHANGED
@@ -37,8 +37,8 @@ export const PROVIDER_PRESETS = [
37
37
  */
38
38
  export const BASE_SYSTEM = [
39
39
  '你在一个敏捷看板里工作:只能通过工具访问当前项目 agile/ 下的数据。',
40
- '- search_agile:检索 backlog / sprints / journal',
41
- '- update_agile:按路径改数据(路径形如 stories.S-011.status);写前自动校验,手动审核时先出待审变更',
40
+ '- search_agile:检索 backlog / sprints / journal / tests',
41
+ '- update_agile:按路径改数据(路径形如 stories.S-011.status、suites.TS-001.cases.TC-001.status);写前自动校验,手动审核时先出待审变更',
42
42
  '规则:',
43
43
  '1. 涉及具体故事 / 任务 / 迭代的问题,先查再答;查不到就说没有。',
44
44
  '2. 不要凭空编 id、状态、日期、人名,也不要造测试数据。',
@@ -67,13 +67,13 @@ export const TOOL_SPECS = [
67
67
  type: 'function',
68
68
  function: {
69
69
  name: 'search_agile',
70
- description: '在当前项目的 agile/ 数据里检索(backlog / sprints / journal)。多个关键词用空格分隔,需同时命中同一条记录。',
70
+ description: '在当前项目的 agile/ 数据里检索(backlog / sprints / journal / tests)。多个关键词用空格分隔,需同时命中同一条记录。',
71
71
  parameters: {
72
72
  type: 'object',
73
73
  properties: {
74
74
  query: { type: 'string', description: '检索关键词,例如「S-011 阻塞」' },
75
75
  limit: { type: 'integer', description: '最多返回多少条,默认 20' },
76
- file: { type: 'string', enum: ['backlog', 'sprints', 'journal'], description: '只搜某个文件(可选)' },
76
+ file: { type: 'string', enum: ['backlog', 'sprints', 'journal', 'tests'], description: '只搜某个文件(可选)' },
77
77
  },
78
78
  required: ['query'],
79
79
  },
@@ -89,7 +89,7 @@ export const TOOL_SPECS = [
89
89
  parameters: {
90
90
  type: 'object',
91
91
  properties: {
92
- file: { type: 'string', enum: ['backlog', 'sprints', 'journal'], description: '要改哪个文件' },
92
+ file: { type: 'string', enum: ['backlog', 'sprints', 'journal', 'tests'], description: '要改哪个文件' },
93
93
  reason: { type: 'string', description: '为什么改,写给人看的一句话' },
94
94
  patches: {
95
95
  type: 'array',
@@ -87,10 +87,16 @@ function snapshot(root) {
87
87
  try {
88
88
  lastChange = read('last-change');
89
89
  } catch {}
90
+ // tests.json 同样可选:没登记用例的项目就是空数组,页面给空状态。
91
+ // 坏文件交给 validate 报,这里吞掉异常,别让整个 /api/agile 挂掉。
92
+ let tests = null;
93
+ try {
94
+ tests = read('tests');
95
+ } catch {}
90
96
  // 页面开头要说明「已提交才覆盖、未提交要合并」,所以状态跟着快照一起给出去
91
97
  const { state, advice, hash, date, subject } = checkLastChange(root);
92
98
  return {
93
- backlog: read('backlog'), sprints: read('sprints'), journal: read('journal'), lastChange,
99
+ backlog: read('backlog'), sprints: read('sprints'), journal: read('journal'), lastChange, tests,
94
100
  meta: { root, servedAt: new Date().toISOString(), lastChangeGit: { state, advice, hash, date, subject } },
95
101
  };
96
102
  }
@@ -491,6 +491,167 @@ for (const [tid, { task }] of taskById) {
491
491
  }
492
492
  }
493
493
 
494
+ // ---- tests.json(测试资产,可选文件) ----
495
+ // 不存在 = 这个项目还没登记测试用例,合法(老项目升级不受影响);
496
+ // 存在就必须能被 /tests 页原样渲染,且能支撑「覆盖率缺口」的判断。
497
+ {
498
+ const p = join(AGILE, 'tests.json');
499
+ if (existsSync(p)) {
500
+ const LAYER = ['frontend', 'backend', 'e2e'];
501
+ const LEVEL = ['unit', 'integration', 'e2e'];
502
+ const CASE_STATUS = ['pass', 'fail', 'skipped', 'blocked', 'not-run'];
503
+ const COVER_KEYS = ['lines', 'branches', 'functions', 'statements'];
504
+ const COVER_REF = /^(S-\d+)-A(\d+)$/;
505
+ let doc = null;
506
+ try {
507
+ doc = JSON.parse(readFileSync(p, 'utf8'));
508
+ } catch (e) {
509
+ err(`agile/tests.json: JSON 解析失败 - ${e.message}`);
510
+ }
511
+ if (doc) {
512
+ if (doc.schemaVersion !== 3) err(`agile/tests.json: schemaVersion 应为 3,实际 ${doc.schemaVersion}`);
513
+ const suites = Array.isArray(doc.suites) ? doc.suites : [];
514
+ const runs = Array.isArray(doc.runs) ? doc.runs : [];
515
+ if (!Array.isArray(doc.suites)) err('tests: suites 必须是数组(没有用例就写空数组)');
516
+ if (!Array.isArray(doc.runs)) err('tests: runs 必须是数组(没跑过就写空数组)');
517
+
518
+ const caseById = new Map();
519
+ const cases = [];
520
+ const coveredRefs = new Set();
521
+ const registeredStories = new Set();
522
+
523
+ for (const su of suites) {
524
+ claim(su.id, 'tests suite');
525
+ if (!su.name) err(`tests suite ${su.id ?? '?'}: 缺少 name`);
526
+ if (!LAYER.includes(su.layer)) err(`tests suite ${su.id}: layer 必须是 ${LAYER.join('/')},实际 ${su.layer}`);
527
+ if (!LEVEL.includes(su.level)) err(`tests suite ${su.id}: level 必须是 ${LEVEL.join('/')},实际 ${su.level}`);
528
+ if (su.files !== undefined && (!Array.isArray(su.files) || su.files.some(f => typeof f !== 'string')))
529
+ err(`tests suite ${su.id}: files 必须是字符串数组`);
530
+ if (!Array.isArray(su.cases) || su.cases.length === 0) {
531
+ warn(`tests suite ${su.id}: 没有用例(空套件不该留在清单里)`);
532
+ }
533
+ for (const c of su.cases ?? []) {
534
+ claim(c.id, `tests suite ${su.id} 的 case`);
535
+ if (!c.title) err(`tests case ${c.id ?? '?'}: 缺少 title`);
536
+ if (typeof c.automated !== 'boolean') err(`tests case ${c.id}: automated 必须是布尔值(手工用例也要登记)`);
537
+ if (!CASE_STATUS.includes(c.status))
538
+ err(`tests case ${c.id}: status 必须是 ${CASE_STATUS.join('/')},实际 ${c.status}`);
539
+ if (!c.expect) err(`tests case ${c.id}: 缺少 expect(预期结果,QA 交付物不能只有标题)`);
540
+ if (c.status !== 'not-run') {
541
+ if (!DATE_RE.test(c.lastRun ?? '')) err(`tests case ${c.id}: lastRun 格式应为 YYYY-MM-DD(没跑过就写 not-run)`);
542
+ else if (c.lastRun > today) warn(`tests case ${c.id}: lastRun ${c.lastRun} 在未来`);
543
+ }
544
+ if (c.durationMs !== undefined && (!Number.isFinite(c.durationMs) || c.durationMs < 0))
545
+ err(`tests case ${c.id}: durationMs 必须是非负数字`);
546
+ if (c.covers !== undefined && !Array.isArray(c.covers)) err(`tests case ${c.id}: covers 必须是数组`);
547
+ const covers = Array.isArray(c.covers) ? c.covers : [];
548
+ for (const ref of covers) {
549
+ const m = COVER_REF.exec(typeof ref === 'string' ? ref : '');
550
+ if (!m) { err(`tests case ${c.id}: covers 的 ${ref} 格式应为 S-xxx-A<n>`); continue; }
551
+ const st = storyById.get(m[1]);
552
+ if (!st) { err(`tests case ${c.id}: covers 指向不存在的故事 ${m[1]}`); continue; }
553
+ const total = (st.acceptance ?? []).length;
554
+ if (Number(m[2]) < 1 || Number(m[2]) > total)
555
+ err(`tests case ${c.id}: covers ${ref} 越界(${st.id} 只有 ${total} 条验收)`);
556
+ else { coveredRefs.add(ref); registeredStories.add(st.id); }
557
+ }
558
+ if (c.taskId) {
559
+ const hit = taskById.get(c.taskId);
560
+ if (!hit) err(`tests case ${c.id}: task ${c.taskId} 不存在`);
561
+ else {
562
+ const storyIds = new Set(covers.map(r => String(r).replace(/-A\d+$/, '')).filter(id => storyById.has(id)));
563
+ if (storyIds.size && !storyIds.has(hit.story.id))
564
+ err(`tests case ${c.id}: task ${c.taskId} 属于 ${hit.story.id},与 covers 指向的故事不一致`);
565
+ }
566
+ }
567
+ caseById.set(c.id, c);
568
+ cases.push(c);
569
+ }
570
+ }
571
+
572
+ // 执行记录:时间正序(最后一条 = 最近一次),汇总要自洽
573
+ let prev = '';
574
+ for (const [i, r] of runs.entries()) {
575
+ if (!stampOk(r.at)) err(`tests.runs[${i}]: at 格式应为 YYYY-MM-DD HH:mm:ss`);
576
+ else {
577
+ if (prev && r.at < prev) err(`tests.runs[${i}]: 必须按时间正序排(最新一条放最后),${prev} 之后出现了 ${r.at}`);
578
+ prev = r.at;
579
+ }
580
+ if (!r.command) warn(`tests.runs[${i}]: 缺少 command(这次是怎么跑起来的)`);
581
+ const t = r.totals;
582
+ if (!t || typeof t !== 'object') err(`tests.runs[${i}]: 缺少 totals`);
583
+ else {
584
+ for (const k of ['total', 'pass', 'fail', 'skipped']) {
585
+ if (!Number.isFinite(t[k])) err(`tests.runs[${i}].totals.${k}: 必须是数字`);
586
+ }
587
+ if (Number.isFinite(t.total) && (t.pass ?? 0) + (t.fail ?? 0) + (t.skipped ?? 0) > t.total)
588
+ err(`tests.runs[${i}].totals: 通过 + 失败 + 跳过 加总超过总数 ${t.total},执行记录自相矛盾`);
589
+ }
590
+ if (r.failedCases !== undefined && !Array.isArray(r.failedCases))
591
+ err(`tests.runs[${i}]: failedCases 必须是数组`);
592
+ for (const id of r.failedCases ?? []) {
593
+ if (!caseById.has(id)) err(`tests.runs[${i}]: failedCases 里的 ${id} 不在用例清单里`);
594
+ }
595
+ if (r.coverage !== undefined) {
596
+ if (!r.coverage || typeof r.coverage !== 'object' || Array.isArray(r.coverage))
597
+ err(`tests.runs[${i}].coverage: 必须是按层的对象(frontend / backend)`);
598
+ for (const [layer, cov] of Object.entries(r.coverage ?? {})) {
599
+ if (!LAYER.includes(layer)) err(`tests.runs[${i}].coverage: 层的键只能是 ${LAYER.join('/')},实际 ${layer}`);
600
+ if (!cov || typeof cov !== 'object') { err(`tests.runs[${i}].coverage.${layer}: 必须是对象`); continue; }
601
+ if (!cov.tool) warn(`tests.runs[${i}].coverage.${layer}: 缺少 tool(覆盖率是哪个工具出的)`);
602
+ for (const k of COVER_KEYS) {
603
+ if (cov[k] === undefined) continue;
604
+ if (!Number.isFinite(cov[k]) || cov[k] < 0 || cov[k] > 100)
605
+ err(`tests.runs[${i}].coverage.${layer}.${k}: 百分比必须在 0~100 之间,实际 ${cov[k]}`);
606
+ }
607
+ }
608
+ }
609
+ }
610
+
611
+ const latest = runs[runs.length - 1];
612
+ for (const id of latest?.failedCases ?? []) {
613
+ const c = caseById.get(id);
614
+ if (c && c.status !== 'fail')
615
+ err(`tests: 最新一次执行的 failedCases 里的 ${id} 状态是 ${c.status},应为 fail(执行记录与用例清单不一致)`);
616
+ }
617
+ if (latest?.totals?.total > cases.length) {
618
+ warn(`tests.json 只登记了 ${cases.length} 条用例,最近一次执行共 ${latest.totals.total} 条:还有 ${latest.totals.total - cases.length} 条没登记`);
619
+ }
620
+
621
+ // 收口纪律:登记过用例的故事,不许漏 test 任务、不许漏验收
622
+ for (const s of stories) {
623
+ if (!registeredStories.has(s.id)) continue;
624
+ for (const t of s.tasks ?? []) {
625
+ if (t.type === 'test' && t.status === 'done' && !cases.some(c => c.taskId === t.id))
626
+ err(`task ${t.id}: 已 done 的 test 型任务没有登记用例(story ${s.id} 已经登记了用例,别漏这一条)`);
627
+ }
628
+ if (['review', 'done'].includes(s.status)) {
629
+ (s.acceptance ?? []).forEach((_, i) => {
630
+ const ref = `${s.id}-A${i + 1}`;
631
+ if (!coveredRefs.has(ref))
632
+ err(`story ${s.id}: 验收 ${ref} 没有任何用例覆盖(故事已到 ${s.status})`);
633
+ });
634
+ }
635
+ for (const c of cases) {
636
+ if (!c.taskId || !c.lastRun) continue;
637
+ const hit = taskById.get(c.taskId);
638
+ if (!hit || hit.story.id !== s.id) continue;
639
+ const passes = (journalByTask.get(c.taskId)?.entries ?? []).filter(e => /^pass/i.test(e.result ?? ''));
640
+ const lastPass = passes[passes.length - 1]?.date;
641
+ if (lastPass && c.lastRun < lastPass)
642
+ warn(`tests case ${c.id}: lastRun ${c.lastRun} 早于 task ${c.taskId} 最后一次通过 ${lastPass}(结果该刷新了)`);
643
+ }
644
+ }
645
+
646
+ const doneStories = stories.filter(s => s.status === 'done');
647
+ const missing = doneStories.filter(s => !registeredStories.has(s.id));
648
+ if (missing.length) {
649
+ warn(`tests.json 只登记了 ${registeredStories.size} 个故事的用例,还有 ${missing.length} 个已完成故事完全没有用例`);
650
+ }
651
+ }
652
+ }
653
+ }
654
+
494
655
  // ---- 输出 ----
495
656
  for (const w of warns) console.warn(` WARN ${w}`);
496
657
  if (errors.length === 0) {
@@ -1 +0,0 @@
1
- {"id":"4a212bd2-438f-4b26-b8f3-57db47ca0799","timestamp":1793371116856,"prerendered":[]}