cabloy 5.1.248 → 5.1.250
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/.cabloy-version +1 -1
- package/.github/workflows/create-cabloy.yml +50 -3
- package/.vscode/settings.json +1 -2
- package/CHANGELOG.md +31 -0
- package/package.json +1 -7
- package/repo-agent-governance/managed-assets.json +12 -12
- package/repo-agent-governance/skills/cabloy-spec-execution/references/status-and-evidence.md +1 -1
- package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md +1 -1
- package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md +2 -2
- package/repo-agent-governance/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +1 -1
- package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.mjs +73 -22
- package/repo-agent-governance/tools/spec-charts/generate-implementation-charts.test.mjs +98 -7
- package/repo-docs/ai/playbook-spec-execution.md +1 -1
- package/repo-docs/backend/crud-workflow.md +92 -1
- package/repo-docs/backend/dto-guide.md +16 -1
- package/repo-docs/backend/dto-infer-generation.md +23 -5
- package/repo-docs/backend/orm-aggregate-group-guide.md +32 -0
- package/repo-docs/backend/validation-guide.md +38 -2
- package/repo-e2e/specs/cabloy-basic.spec.ts +9 -2
- package/vona/package.original.json +1 -7
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/aggregate/src/dto/{{resourceName}}AggregateRes.tsx_ +11 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/common/src/lib/{{resourceName}}Analysis.ts_ +14 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/common/test/{{resourceName}}Analysis.test.ts_ +81 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudAnalysis/group/src/dto/{{resourceName}}GroupResItem.tsx_ +11 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/test/{{resourceName}}.test.ts_ +4 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/3-en-us.ts +2 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +2 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/controller/{{resourceName}}.ts_ +18 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectReq.tsx_ +2 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/service/{{resourceName}}.ts_ +14 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/test/{{resourceName}}.test.ts_ +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/3-en-us.ts +2 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +2 -0
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crudBasic.ts +2 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.crudStart.ts +2 -0
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.tools.masterDetail.ts +9 -1
- package/vona/packages-cli/cli-set-api/src/lib/crudAnalysis.ts +11 -0
- package/vona/packages-cli/cli-set-api/test/crudAnalysis.test.ts +385 -0
- package/vona/packages-utils/babel-plugin-vona-bean-module/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/pnpm-lock.yaml +76 -1008
- package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +18 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +28 -1
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentAggregateRes.tsx +11 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentGroupResItem.tsx +11 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectReq.tsx +2 -0
- package/vona/src/suite/a-training/modules/training-student/src/lib/studentAnalysis.ts +12 -0
- package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +22 -1
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +4 -0
- package/vona/src/suite/a-training/modules/training-student/test/studentAnalysis.test.ts +422 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-captcha/modules/a-captcha/package.json +1 -1
- package/vona/src/suite-vendor/a-captcha/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-hmr/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-locale/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/src/lib/utils.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/.metadata/index.ts +26 -26
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/aggregateResult.ts +53 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/controls.ts +163 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/execute.ts +49 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/index.ts +3 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/analysis/policy.ts +140 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_cache.ts +4 -2
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/bean.model/bean.model_crud_inner.ts +8 -3
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dto.ts +3 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAggregate.ts +4 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/dto/dtoAnalysis.ts +34 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/analysis.ts +137 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/src/types/relationsAggr.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-orm/test/aggregateResult.test.ts +81 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/test/analysis.test.ts +208 -0
- package/vona/src/suite-vendor/a-vona/modules/a-orm/test/analysis.types.test.ts +112 -0
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-queue/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-ratelimit/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-user/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/.metadata/index.ts +40 -40
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/bean/pipe.filter.ts +44 -3
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/analysisFilter.ts +69 -0
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/decorator/arguments.ts +6 -4
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/lib/index.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/types/dto.ts +2 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/types/filter.ts +7 -2
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/package.original.json +0 -6
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/package.json +2 -2
- package/zova/packages-utils/zova-jsx/package.json +2 -2
- package/zova/packages-utils/zova-vite/package.json +1 -1
- package/zova/packages-zova/zova/package.json +3 -3
- package/zova/packages-zova/zova-core/package.json +1 -1
- package/zova/pnpm-lock.yaml +11 -963
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.header.tsx +4 -1
- package/zova/src/suite/a-home/modules/home-layoutadmin/src/component/layoutAdmin/render.tabs.tsx +2 -2
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +16 -2
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +201 -7
- package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +42 -0
- package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +10 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssrserver/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +5 -5
- package/vona/.textlintrc.json +0 -19
package/.cabloy-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.1.
|
|
1
|
+
5.1.250
|
|
@@ -96,13 +96,60 @@ jobs:
|
|
|
96
96
|
$log = Join-Path $redisDirectory 'redis.log'
|
|
97
97
|
if (Test-Path $log) { Get-Content $log }
|
|
98
98
|
throw 'Redis did not become ready'
|
|
99
|
+
- name: wait for published Cabloy package
|
|
100
|
+
shell: bash
|
|
101
|
+
run: |
|
|
102
|
+
node --input-type=module <<'NODE'
|
|
103
|
+
import { appendFileSync, readFileSync } from 'node:fs';
|
|
104
|
+
|
|
105
|
+
const expectedVersion = JSON.parse(readFileSync('package.json', 'utf8')).version;
|
|
106
|
+
const latestUrl = 'https://registry.npmjs.org/cabloy/latest';
|
|
107
|
+
const deadline = Date.now() + 20 * 60 * 1000;
|
|
108
|
+
let lastStatus = 'not checked';
|
|
109
|
+
|
|
110
|
+
while (Date.now() < deadline) {
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch(latestUrl, { cache: 'no-store' });
|
|
113
|
+
lastStatus = `${response.status} ${response.statusText}`;
|
|
114
|
+
if (response.ok) {
|
|
115
|
+
const metadata = await response.json();
|
|
116
|
+
if (metadata.version === expectedVersion) break;
|
|
117
|
+
lastStatus = `latest is ${metadata.version ?? 'unknown'}`;
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
lastStatus = error instanceof Error ? error.message : String(error);
|
|
121
|
+
}
|
|
122
|
+
console.log(`Waiting for cabloy@${expectedVersion} to become npm latest (${lastStatus})...`);
|
|
123
|
+
await new Promise(resolve => setTimeout(resolve, 10_000));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (Date.now() >= deadline) {
|
|
127
|
+
throw new Error(`cabloy@${expectedVersion} was not npm latest within 20 minutes (last status: ${lastStatus})`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
appendFileSync(process.env.GITHUB_ENV, `CABLOY_VERSION=${expectedVersion}\n`);
|
|
131
|
+
NODE
|
|
99
132
|
- name: create project
|
|
100
133
|
run: npm create cabloy@latest cabloy-app
|
|
101
134
|
- name: verify project initialization
|
|
102
135
|
working-directory: cabloy-app
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
136
|
+
shell: bash
|
|
137
|
+
run: |
|
|
138
|
+
node --input-type=module <<'NODE'
|
|
139
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
140
|
+
|
|
141
|
+
const required = ['.cabloy-version', 'package.json', 'vona/package.json', 'zova/package.json', 'node_modules', 'vona/node_modules', 'zova/node_modules', 'zova/dist'];
|
|
142
|
+
const missing = required.filter(file => !existsSync(file));
|
|
143
|
+
const packageVersion = JSON.parse(readFileSync('package.json', 'utf8')).version;
|
|
144
|
+
if (packageVersion !== process.env.CABLOY_VERSION) {
|
|
145
|
+
console.error(`Expected cabloy@${process.env.CABLOY_VERSION}, got cabloy@${packageVersion}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
if (missing.length) {
|
|
149
|
+
console.error('Project initialization is incomplete:', missing.join(', '));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
NODE
|
|
106
153
|
- name: reset database
|
|
107
154
|
working-directory: cabloy-app
|
|
108
155
|
run: npm run db:reset
|
package/.vscode/settings.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.1.250
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- Add Japanese (`ja-jp`) and Traditional Chinese (`zh-tw`) locales.
|
|
8
|
+
- Introduce locale providers and locale specifications.
|
|
9
|
+
- Add grouping and aggregation capabilities.
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
- Distinguish planning closure from unfinished work breakdown structure (WBS) scope.
|
|
14
|
+
- Correct target decorator DTO resolution for OpenAPI.
|
|
15
|
+
- Keep Cabloy Locale delivery confined to its worktree.
|
|
16
|
+
|
|
17
|
+
### Improvements
|
|
18
|
+
|
|
19
|
+
- Refactor CLI CRUD functionality.
|
|
20
|
+
- Enhance grouping and aggregation.
|
|
21
|
+
- Clarify locale provider examples and metadata commands.
|
|
22
|
+
- Update locale documentation and synchronize OpenAPI guidance.
|
|
23
|
+
|
|
24
|
+
## 5.1.249
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
- Update existing functionality.
|
|
29
|
+
|
|
30
|
+
### Improvements
|
|
31
|
+
|
|
32
|
+
- Remove textlint.
|
|
33
|
+
|
|
3
34
|
## 5.1.248
|
|
4
35
|
|
|
5
36
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.250",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "A Node.js fullstack framework",
|
|
6
6
|
"keywords": [
|
|
@@ -69,7 +69,6 @@
|
|
|
69
69
|
"docs:dev": "pnpm --dir repo-docs docs:dev",
|
|
70
70
|
"docs:build": "pnpm --dir repo-docs docs:build",
|
|
71
71
|
"docs:preview": "pnpm --dir repo-docs docs:preview",
|
|
72
|
-
"docs:fix": "textlint --fix ./repo-docs/**/*.md",
|
|
73
72
|
"release-patch": "node scripts/release.ts patch",
|
|
74
73
|
"release-minor": "node scripts/release.ts minor",
|
|
75
74
|
"release-major": "node scripts/release.ts major",
|
|
@@ -101,11 +100,6 @@
|
|
|
101
100
|
"oxfmt": "^0.74.0",
|
|
102
101
|
"oxlint": "^1.89.0",
|
|
103
102
|
"tar": "^7.5.22",
|
|
104
|
-
"textlint": "^15.7.0",
|
|
105
|
-
"textlint-rule-en-no-mixed-period": "^3.0.2",
|
|
106
|
-
"textlint-rule-ja-no-mixed-period": "^3.0.2",
|
|
107
|
-
"textlint-rule-ja-space-between-half-and-full-width": "^2.4.2",
|
|
108
|
-
"textlint-rule-period-in-list-item": "^1.0.1",
|
|
109
103
|
"typescript": "^5.9.3"
|
|
110
104
|
},
|
|
111
105
|
"engines": {
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
{
|
|
187
187
|
"adapter": "codex",
|
|
188
188
|
"category": "skill",
|
|
189
|
-
"sha256": "
|
|
189
|
+
"sha256": "a7375a23f4112d16407e867334781856367d041a956cb0009244881b3f88dcff",
|
|
190
190
|
"source": "skills/cabloy-spec-execution/references/status-and-evidence.md",
|
|
191
191
|
"target": ".agents/skills/cabloy-spec-execution/references/status-and-evidence.md"
|
|
192
192
|
},
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
{
|
|
222
222
|
"adapter": "codex",
|
|
223
223
|
"category": "skill",
|
|
224
|
-
"sha256": "
|
|
224
|
+
"sha256": "6cb948d8c061c1abf96045f3edf5a24319c2cc22d9f1f6c5f1811b6face0333c",
|
|
225
225
|
"source": "skills/cabloy-spec-generation/references/canonical-spec-input.md",
|
|
226
226
|
"target": ".agents/skills/cabloy-spec-generation/references/canonical-spec-input.md"
|
|
227
227
|
},
|
|
@@ -235,14 +235,14 @@
|
|
|
235
235
|
{
|
|
236
236
|
"adapter": "codex",
|
|
237
237
|
"category": "skill",
|
|
238
|
-
"sha256": "
|
|
238
|
+
"sha256": "0a8c64f3b0c055790d9fb2b267954dbbd0c2473a2ea0e5b2c8be2348a9956459",
|
|
239
239
|
"source": "skills/cabloy-spec-generation/references/repo-specs-document-set.md",
|
|
240
240
|
"target": ".agents/skills/cabloy-spec-generation/references/repo-specs-document-set.md"
|
|
241
241
|
},
|
|
242
242
|
{
|
|
243
243
|
"adapter": "codex",
|
|
244
244
|
"category": "skill",
|
|
245
|
-
"sha256": "
|
|
245
|
+
"sha256": "6698717c1e20519fa13c2961de7e863fe5c2ff9f2f420f3f289f96a9533a190e",
|
|
246
246
|
"source": "skills/cabloy-spec-generation/references/traceability-and-status-rules.md",
|
|
247
247
|
"target": ".agents/skills/cabloy-spec-generation/references/traceability-and-status-rules.md"
|
|
248
248
|
},
|
|
@@ -522,7 +522,7 @@
|
|
|
522
522
|
{
|
|
523
523
|
"adapter": "claude",
|
|
524
524
|
"category": "skill",
|
|
525
|
-
"sha256": "
|
|
525
|
+
"sha256": "a7375a23f4112d16407e867334781856367d041a956cb0009244881b3f88dcff",
|
|
526
526
|
"source": "skills/cabloy-spec-execution/references/status-and-evidence.md",
|
|
527
527
|
"target": ".claude/skills/cabloy-spec-execution/references/status-and-evidence.md"
|
|
528
528
|
},
|
|
@@ -557,7 +557,7 @@
|
|
|
557
557
|
{
|
|
558
558
|
"adapter": "claude",
|
|
559
559
|
"category": "skill",
|
|
560
|
-
"sha256": "
|
|
560
|
+
"sha256": "6cb948d8c061c1abf96045f3edf5a24319c2cc22d9f1f6c5f1811b6face0333c",
|
|
561
561
|
"source": "skills/cabloy-spec-generation/references/canonical-spec-input.md",
|
|
562
562
|
"target": ".claude/skills/cabloy-spec-generation/references/canonical-spec-input.md"
|
|
563
563
|
},
|
|
@@ -571,14 +571,14 @@
|
|
|
571
571
|
{
|
|
572
572
|
"adapter": "claude",
|
|
573
573
|
"category": "skill",
|
|
574
|
-
"sha256": "
|
|
574
|
+
"sha256": "0a8c64f3b0c055790d9fb2b267954dbbd0c2473a2ea0e5b2c8be2348a9956459",
|
|
575
575
|
"source": "skills/cabloy-spec-generation/references/repo-specs-document-set.md",
|
|
576
576
|
"target": ".claude/skills/cabloy-spec-generation/references/repo-specs-document-set.md"
|
|
577
577
|
},
|
|
578
578
|
{
|
|
579
579
|
"adapter": "claude",
|
|
580
580
|
"category": "skill",
|
|
581
|
-
"sha256": "
|
|
581
|
+
"sha256": "6698717c1e20519fa13c2961de7e863fe5c2ff9f2f420f3f289f96a9533a190e",
|
|
582
582
|
"source": "skills/cabloy-spec-generation/references/traceability-and-status-rules.md",
|
|
583
583
|
"target": ".claude/skills/cabloy-spec-generation/references/traceability-and-status-rules.md"
|
|
584
584
|
},
|
|
@@ -872,7 +872,7 @@
|
|
|
872
872
|
{
|
|
873
873
|
"adapter": "cursor",
|
|
874
874
|
"category": "skill",
|
|
875
|
-
"sha256": "
|
|
875
|
+
"sha256": "a7375a23f4112d16407e867334781856367d041a956cb0009244881b3f88dcff",
|
|
876
876
|
"source": "skills/cabloy-spec-execution/references/status-and-evidence.md",
|
|
877
877
|
"target": ".cursor/skills/cabloy-spec-execution/references/status-and-evidence.md"
|
|
878
878
|
},
|
|
@@ -907,7 +907,7 @@
|
|
|
907
907
|
{
|
|
908
908
|
"adapter": "cursor",
|
|
909
909
|
"category": "skill",
|
|
910
|
-
"sha256": "
|
|
910
|
+
"sha256": "6cb948d8c061c1abf96045f3edf5a24319c2cc22d9f1f6c5f1811b6face0333c",
|
|
911
911
|
"source": "skills/cabloy-spec-generation/references/canonical-spec-input.md",
|
|
912
912
|
"target": ".cursor/skills/cabloy-spec-generation/references/canonical-spec-input.md"
|
|
913
913
|
},
|
|
@@ -921,14 +921,14 @@
|
|
|
921
921
|
{
|
|
922
922
|
"adapter": "cursor",
|
|
923
923
|
"category": "skill",
|
|
924
|
-
"sha256": "
|
|
924
|
+
"sha256": "0a8c64f3b0c055790d9fb2b267954dbbd0c2473a2ea0e5b2c8be2348a9956459",
|
|
925
925
|
"source": "skills/cabloy-spec-generation/references/repo-specs-document-set.md",
|
|
926
926
|
"target": ".cursor/skills/cabloy-spec-generation/references/repo-specs-document-set.md"
|
|
927
927
|
},
|
|
928
928
|
{
|
|
929
929
|
"adapter": "cursor",
|
|
930
930
|
"category": "skill",
|
|
931
|
-
"sha256": "
|
|
931
|
+
"sha256": "6698717c1e20519fa13c2961de7e863fe5c2ff9f2f420f3f289f96a9533a190e",
|
|
932
932
|
"source": "skills/cabloy-spec-generation/references/traceability-and-status-rules.md",
|
|
933
933
|
"target": ".cursor/skills/cabloy-spec-generation/references/traceability-and-status-rules.md"
|
|
934
934
|
},
|
package/repo-agent-governance/skills/cabloy-spec-execution/references/status-and-evidence.md
CHANGED
|
@@ -102,7 +102,7 @@ The normal path is:
|
|
|
102
102
|
not-started -> in-progress -> implementation-complete -> verified
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
A genuinely documentary/design-only task can instead follow `not-started -> in-progress -> planning-complete`, but only when its formal WBS declaration contains `Completion mode: planning-only.`, its own checks have revision-scoped documentary evidence, and a named reviewer explicitly records the planning-closure disposition. `authority-only` traceability is not this opt-in. `planning-complete` satisfies a predecessor dependency edge but never grants successor execution approval, accepts another ADR, completes source, or passes a linked prospective runtime ATP.
|
|
105
|
+
A genuinely documentary/design-only task can instead follow `not-started -> in-progress -> planning-complete`, but only when its formal WBS declaration contains `Completion mode: planning-only.`, its own checks have revision-scoped documentary evidence, and a named reviewer explicitly records the planning-closure disposition. `authority-only` traceability is not this opt-in. `planning-complete` satisfies a predecessor dependency edge but never grants successor execution approval, accepts another ADR, completes source, or passes a linked prospective runtime ATP. Charts distinguish its documentary closure from verified acceptance. Verified totals count only `verified`; the burndown subtracts verified and eligible planning-complete counts from active scope for its unfinished remainder. Deferred scope is excluded; all other active statuses remain unfinished. `implementation-complete`, `waived` and `deferred` are not completed prerequisites. `blocked`, `waived`, and `deferred` are explicit states, not shortcuts to completion. Mark an implementation task `in-progress` only after approved implementation or verification actually begins. A generated file, successful scaffold, passing typecheck, code review, manual walkthrough, screenshot, or unrelated broad test pass cannot by itself produce `verified`.
|
|
106
106
|
|
|
107
107
|
A task may be `implementation-complete` when source work is complete but an ATP, browser check, paired build, release gate, redaction decision, or durable artifact remains. A phase can be verified without implying that a separately defined integration or release phase is verified.
|
|
108
108
|
|
package/repo-agent-governance/skills/cabloy-spec-generation/references/canonical-spec-input.md
CHANGED
|
@@ -111,7 +111,7 @@ Setup, Procedure, Expected result, Minimum proof, and Traceability must each be
|
|
|
111
111
|
| `WBS-DEMO-10-01` | `not-started` | None; execution has not begun. | Confirm the bounded execution dossier. |
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
Resolve columns by header names (`WBS ID` and `Status`), never fixed cell positions. Additional/reordered columns are allowed. Require one row for every formal WBS task when constructing the complete chart model. Preserve the established status vocabulary and explain blockers/waivers precisely. `planning-complete` is valid only for a task declaring `Completion mode: planning-only.` in its WBS body; it satisfies a predecessor dependency edge but does not grant successor execution approval
|
|
114
|
+
Resolve columns by header names (`WBS ID` and `Status`), never fixed cell positions. Additional/reordered columns are allowed. Require one row for every formal WBS task when constructing the complete chart model. Preserve the established status vocabulary and explain blockers/waivers precisely. `planning-complete` is valid only for a task declaring `Completion mode: planning-only.` in its WBS body; it satisfies a predecessor dependency edge but does not grant successor execution approval or count as verified. The burndown reports verified and eligible planning-complete counts separately; remaining = active scope − verified − eligible planning-complete. Deferred scope is separate; all other active statuses remain unfinished.
|
|
115
115
|
|
|
116
116
|
## Three gates, not one success signal
|
|
117
117
|
|
package/repo-agent-governance/skills/cabloy-spec-generation/references/repo-specs-document-set.md
CHANGED
|
@@ -41,7 +41,7 @@ When records disagree, update authority first, then downstream mappings and deri
|
|
|
41
41
|
|
|
42
42
|
Generate/check charts only with complete supported README/WBS/ATP/progress inputs. Regenerate after `pdp-wbs.md`, `test-plan.md`, `progress.md`, or README title/language changes. An incomplete lightweight/legacy set reports missing chart inputs rather than inventing business definitions to make the generator run.
|
|
43
43
|
|
|
44
|
-
The implementation charts are generated records, not authorities. The Gantt reads formal WBS phases/tasks/dependencies and progress status, with ATP labels only when they resolve in `test-plan.md`; absent authoritative dates or estimates must be shown as relative/illustrative order. The burndown reads active/deferred scope and
|
|
44
|
+
The implementation charts are generated records, not authorities. The Gantt reads formal WBS phases/tasks/dependencies and progress status, with ATP labels only when they resolve in `test-plan.md`; absent authoritative dates or estimates must be shown as relative/illustrative order. The burndown reads active/deferred scope, verified acceptance, and eligible planning-complete closure separately; without immutable dated snapshots it must be a scope-count reference rather than a calendar trend or forecast. Neither chart may add scope, dependencies, evidence, or completion claims. Both SVGs use the README-derived language consistently, including visible labels, accessibility text, and metadata.
|
|
45
45
|
|
|
46
46
|
## README template contract
|
|
47
47
|
|
|
@@ -201,7 +201,7 @@ Initialize a new suite with statuses such as `not-started`, `deferred`, or expli
|
|
|
201
201
|
| `waived` | A temporary exception with owner, reason, and expiry; expiry makes it a release blocker. |
|
|
202
202
|
| `deferred` | Explicitly postponed scope, not completed scope. |
|
|
203
203
|
|
|
204
|
-
A `planning-complete` predecessor satisfies its dependency edge, not the next task's design/approval gates; keep a blocked successor blocked. Verified
|
|
204
|
+
A `planning-complete` predecessor satisfies its dependency edge, not the next task's design/approval gates; keep a blocked successor blocked. Verified totals count only `verified`; the burndown separately counts eligible `planning-complete` and calculates remaining = active scope − verified − eligible planning-complete. Deferred scope is separate; all other active statuses remain unfinished. Progress must remain a derived register. It may point to evidence but must not become a second requirements document. Include a decision-register entry for each material site strategy and show strategy/identifier deferral as a blocker only on affected frontend/site WBS branches; source reading, planning, or a selected strategy alone is not implementation evidence.
|
|
205
205
|
|
|
206
206
|
## ADR 0001 template contract
|
|
207
207
|
|
|
@@ -84,7 +84,7 @@ Downstream records do not silently override authority. Charts cannot authorize s
|
|
|
84
84
|
| `waived` | A temporary exception explicitly approved with owner, reason, and expiry. |
|
|
85
85
|
| `deferred` | Explicitly postponed scope; it is not complete or verified. |
|
|
86
86
|
|
|
87
|
-
For a newly created plan, initialize delivery rows as `not-started`, `deferred`, or `blocked` as appropriate. Creating Markdown files never makes a task `planning-complete`, implementation `implementation-complete`, or ATP `verified`. Only a formally declared `Completion mode: planning-only.` task with documentary acceptance checks, revision-scoped evidence and an explicit named-reviewer closure disposition may reach `planning-complete`; `authority-only` traceability does not establish this eligibility. A planning-complete predecessor satisfies its WBS dependency edge, but blocked/deferred/waived successors stay gated and require their own decisions and approvals. `implementation-complete`, `waived` and `deferred` do not satisfy predecessor completion. Only `verified` contributes to verified totals
|
|
87
|
+
For a newly created plan, initialize delivery rows as `not-started`, `deferred`, or `blocked` as appropriate. Creating Markdown files never makes a task `planning-complete`, implementation `implementation-complete`, or ATP `verified`. Only a formally declared `Completion mode: planning-only.` task with documentary acceptance checks, revision-scoped evidence and an explicit named-reviewer closure disposition may reach `planning-complete`; `authority-only` traceability does not establish this eligibility. A planning-complete predecessor satisfies its WBS dependency edge, but blocked/deferred/waived successors stay gated and require their own decisions and approvals. `implementation-complete`, `waived` and `deferred` do not satisfy predecessor completion. Only `verified` contributes to verified totals. The burndown separately reports eligible `planning-complete` closure and subtracts both from active scope to calculate unfinished work; deferred scope is excluded, and all other active statuses remain unfinished.
|
|
88
88
|
|
|
89
89
|
### README and ADR decision-status consistency
|
|
90
90
|
|
|
@@ -49,9 +49,14 @@ const COPY = {
|
|
|
49
49
|
'Dependency readiness is not execution approval; descriptive gates still require review.',
|
|
50
50
|
ganttDescription: (count, reviewed) =>
|
|
51
51
|
`${count} formal WBS tasks. Current derived status reviewed ${reviewed}.`,
|
|
52
|
-
burndownDescription: (total, verified, remaining) =>
|
|
53
|
-
`${total} active WBS items; ${verified} verified; ${remaining} remaining.`,
|
|
54
|
-
phaseSummary: (number, verified,
|
|
52
|
+
burndownDescription: (total, verified, planningComplete, remaining) =>
|
|
53
|
+
`${total} active WBS items; ${verified} verified; ${planningComplete} planning complete; ${remaining} remaining.`,
|
|
54
|
+
phaseSummary: (number, total, verified, planningComplete, remaining) =>
|
|
55
|
+
`Phase ${number}: ${total} active; ${verified} verified; ${planningComplete} planning complete; ${remaining} remaining`,
|
|
56
|
+
planningComplete: 'planning complete',
|
|
57
|
+
completed: 'Completed',
|
|
58
|
+
completionCounts: (verified, planningComplete) =>
|
|
59
|
+
`${verified} verified · ${planningComplete} planning complete`,
|
|
55
60
|
statuses: {
|
|
56
61
|
'not-started': 'Not started',
|
|
57
62
|
'in-progress': 'In progress',
|
|
@@ -65,14 +70,14 @@ const COPY = {
|
|
|
65
70
|
source: 'Sources',
|
|
66
71
|
reviewed: 'Last reviewed',
|
|
67
72
|
scopeReference: 'Scope-count reference',
|
|
68
|
-
notTime: '
|
|
73
|
+
notTime: 'Completed WBS items (verified + planning complete) — not time',
|
|
69
74
|
remainingAxis: 'WBS items remaining',
|
|
70
75
|
logicalBaseline: 'Logical recorded-scope baseline',
|
|
71
76
|
currentSnapshot: 'Current snapshot',
|
|
72
77
|
remainingShort: 'remaining',
|
|
73
78
|
emptyScope: 'No active WBS items remain in scope.',
|
|
74
79
|
noHistory: 'No observed intermediate status points are available.',
|
|
75
|
-
formula: 'remaining = active scope − verified',
|
|
80
|
+
formula: 'remaining = active scope − verified − planning complete',
|
|
76
81
|
phaseScope: 'Scope by phase',
|
|
77
82
|
active: 'Active WBS items',
|
|
78
83
|
deferred: 'Deferred',
|
|
@@ -107,9 +112,14 @@ const COPY = {
|
|
|
107
112
|
candidateCaveat: '依赖就绪不代表实施批准;描述性门槛仍需审查。',
|
|
108
113
|
ganttDescription: (count, reviewed) =>
|
|
109
114
|
`${count} 项正式 WBS 工作。当前派生状态的审查日期:${reviewed}。`,
|
|
110
|
-
burndownDescription: (total, verified, remaining) =>
|
|
111
|
-
`${total} 项活跃 WBS 工作;${verified} 项已核验;${remaining} 项剩余。`,
|
|
112
|
-
phaseSummary: (number, verified,
|
|
115
|
+
burndownDescription: (total, verified, planningComplete, remaining) =>
|
|
116
|
+
`${total} 项活跃 WBS 工作;${verified} 项已核验;${planningComplete} 项规划完成;${remaining} 项剩余。`,
|
|
117
|
+
phaseSummary: (number, total, verified, planningComplete, remaining) =>
|
|
118
|
+
`阶段 ${number}:${total} 项活跃;${verified} 项已核验;${planningComplete} 项规划完成;${remaining} 项剩余`,
|
|
119
|
+
planningComplete: '规划完成',
|
|
120
|
+
completed: '已完成',
|
|
121
|
+
completionCounts: (verified, planningComplete) =>
|
|
122
|
+
`${verified} 已核验 · ${planningComplete} 规划完成`,
|
|
113
123
|
statuses: {
|
|
114
124
|
'not-started': '未开始',
|
|
115
125
|
'in-progress': '进行中',
|
|
@@ -123,14 +133,14 @@ const COPY = {
|
|
|
123
133
|
source: '来源',
|
|
124
134
|
reviewed: '最后审查日期',
|
|
125
135
|
scopeReference: '范围计数参考',
|
|
126
|
-
notTime: '
|
|
136
|
+
notTime: '已完成 WBS 项(已核验 + 规划完成)——不是时间',
|
|
127
137
|
remainingAxis: '剩余 WBS 项',
|
|
128
138
|
logicalBaseline: '逻辑上的记录范围基线',
|
|
129
139
|
currentSnapshot: '当前快照',
|
|
130
140
|
remainingShort: '剩余',
|
|
131
141
|
emptyScope: '当前范围内没有活跃 WBS 项。',
|
|
132
142
|
noHistory: '没有可用的中间历史状态点。',
|
|
133
|
-
formula: '剩余 = 活跃范围 − 已核验',
|
|
143
|
+
formula: '剩余 = 活跃范围 − 已核验 − 规划完成',
|
|
134
144
|
phaseScope: '按阶段统计范围',
|
|
135
145
|
active: '活跃 WBS 项',
|
|
136
146
|
deferred: '已延期',
|
|
@@ -236,6 +246,15 @@ function languageFromReadme(readme) {
|
|
|
236
246
|
return cjk > latin / 3 ? 'zh' : 'en';
|
|
237
247
|
}
|
|
238
248
|
|
|
249
|
+
function completionCounts(tasks) {
|
|
250
|
+
const verified = tasks.filter(task => task.status === 'verified').length;
|
|
251
|
+
const planningComplete = tasks.filter(
|
|
252
|
+
task => task.status === 'planning-complete' && isPlanningCompleteEligible(task),
|
|
253
|
+
).length;
|
|
254
|
+
const completed = verified + planningComplete;
|
|
255
|
+
return { verified, planningComplete, completed, remaining: tasks.length - completed };
|
|
256
|
+
}
|
|
257
|
+
|
|
239
258
|
export function createChartModel({ readme, wbs, progress, testPlan }) {
|
|
240
259
|
const language = languageFromReadme(readme);
|
|
241
260
|
const atpIds = parseAtpIds(testPlan);
|
|
@@ -275,7 +294,7 @@ export function createChartModel({ readme, wbs, progress, testPlan }) {
|
|
|
275
294
|
}
|
|
276
295
|
const activeTasks = tasks.filter(task => !task.deferred && task.status !== 'deferred');
|
|
277
296
|
const deferredTasks = tasks.filter(task => !activeTasks.includes(task));
|
|
278
|
-
const
|
|
297
|
+
const counts = completionCounts(activeTasks);
|
|
279
298
|
return {
|
|
280
299
|
language,
|
|
281
300
|
copy: COPY[language],
|
|
@@ -284,7 +303,7 @@ export function createChartModel({ readme, wbs, progress, testPlan }) {
|
|
|
284
303
|
activeTasks,
|
|
285
304
|
deferredTasks,
|
|
286
305
|
expandedEdges,
|
|
287
|
-
|
|
306
|
+
...counts,
|
|
288
307
|
reviewed: reviewed === 'not recorded' ? COPY[language].notRecorded : reviewed,
|
|
289
308
|
};
|
|
290
309
|
}
|
|
@@ -441,9 +460,19 @@ export function renderGantt(model, suiteName) {
|
|
|
441
460
|
}
|
|
442
461
|
|
|
443
462
|
export function renderBurndown(model, suiteName) {
|
|
444
|
-
const {
|
|
463
|
+
const {
|
|
464
|
+
copy: t,
|
|
465
|
+
phases,
|
|
466
|
+
activeTasks,
|
|
467
|
+
deferredTasks,
|
|
468
|
+
verified,
|
|
469
|
+
planningComplete,
|
|
470
|
+
completed,
|
|
471
|
+
remaining,
|
|
472
|
+
reviewed,
|
|
473
|
+
language,
|
|
474
|
+
} = model;
|
|
445
475
|
const total = activeTasks.length;
|
|
446
|
-
const remaining = total - verified;
|
|
447
476
|
const phaseBottom = 836 + Math.max(0, phases.length - 1) * 28;
|
|
448
477
|
const footerY = Math.max(976, phaseBottom + 50);
|
|
449
478
|
const height = footerY + 74;
|
|
@@ -451,7 +480,7 @@ export function renderBurndown(model, suiteName) {
|
|
|
451
480
|
const x1 = 910;
|
|
452
481
|
const y0 = 410;
|
|
453
482
|
const y1 = 650;
|
|
454
|
-
const completion = total ?
|
|
483
|
+
const completion = total ? completed / total : 0;
|
|
455
484
|
const currentX = x0 + completion * (x1 - x0);
|
|
456
485
|
const currentY = y0 + completion * (y1 - y0);
|
|
457
486
|
const currentMarker =
|
|
@@ -461,10 +490,31 @@ export function renderBurndown(model, suiteName) {
|
|
|
461
490
|
const phaseRows = phases
|
|
462
491
|
.map((phase, index) => {
|
|
463
492
|
const phaseTasks = phase.tasks.filter(task => !task.deferred && task.status !== 'deferred');
|
|
464
|
-
const
|
|
493
|
+
const counts = completionCounts(phaseTasks);
|
|
494
|
+
const summary = t.phaseSummary(
|
|
495
|
+
phase.number,
|
|
496
|
+
phaseTasks.length,
|
|
497
|
+
counts.verified,
|
|
498
|
+
counts.planningComplete,
|
|
499
|
+
counts.remaining,
|
|
500
|
+
);
|
|
501
|
+
const status = !phaseTasks.length
|
|
502
|
+
? 'deferred'
|
|
503
|
+
: counts.remaining
|
|
504
|
+
? 'in-progress'
|
|
505
|
+
: counts.planningComplete
|
|
506
|
+
? 'planning-complete'
|
|
507
|
+
: 'verified';
|
|
508
|
+
const label = !phaseTasks.length
|
|
509
|
+
? t.emptyScope
|
|
510
|
+
: counts.remaining
|
|
511
|
+
? `${counts.completed}/${phaseTasks.length}`
|
|
512
|
+
: counts.planningComplete && counts.verified
|
|
513
|
+
? t.completed
|
|
514
|
+
: statusLabel(status, language);
|
|
465
515
|
const y = 832 + index * 28;
|
|
466
|
-
const width = phaseTasks.length ? (
|
|
467
|
-
return `<g class="row" tabindex="0" aria-label="${escapeXml(
|
|
516
|
+
const width = phaseTasks.length ? (counts.completed / phaseTasks.length) * 210 : 0;
|
|
517
|
+
return `<g class="row" tabindex="0" aria-label="${escapeXml(summary)}"><title>${escapeXml(`${summary} · ${phase.title}`)}</title><text class="task" x="96" y="${y}">${escapeXml(`${t.phase} ${phase.number} · ${phase.title}`)}</text><text class="num" x="650" y="${y}" text-anchor="end">${counts.verified}</text><text class="num" x="750" y="${y}" text-anchor="end">${counts.planningComplete}</text><text class="num" x="850" y="${y}" text-anchor="end">${counts.remaining}</text><rect x="930" y="${y - 11}" width="210" height="12" rx="6" fill="var(--blue-wash)"/><rect x="930" y="${y - 11}" width="${width}" height="12" rx="6" fill="var(--aqua)"/>${statusIcon(1215, y - 5, status)}<text class="small" x="1228" y="${y}">${escapeXml(label)}</text></g>`;
|
|
468
518
|
})
|
|
469
519
|
.join('\n');
|
|
470
520
|
let explanationY = 412;
|
|
@@ -478,19 +528,20 @@ export function renderBurndown(model, suiteName) {
|
|
|
478
528
|
.join('');
|
|
479
529
|
const body = `<rect class="canvas" width="1600" height="${height}"/><rect class="card" x="32" y="28" width="1536" height="${height - 56}" rx="14"/>
|
|
480
530
|
<text class="title" x="72" y="84">${escapeXml(`${suiteName} ${t.burndownTitle}`)}</text><text class="subtitle" x="72" y="110">${escapeXml(t.burndownSubtitle)}</text><text class="small" x="1500" y="84" text-anchor="end">${escapeXml(`${t.reviewed}: ${reviewed}`)}</text>
|
|
481
|
-
<rect x="72" y="142" width="1428" height="116" rx="8" fill="var(--aqua-wash)"/><text class="heading" x="96" y="170">${escapeXml(t.current)}</text><text x="96" y="231" class="title" style="font-size:52px">${remaining}</text><text class="heading" x="155" y="215">${escapeXml(t.remaining)}</text><text class="caption" x="155" y="237">${escapeXml(
|
|
531
|
+
<rect x="72" y="142" width="1428" height="116" rx="8" fill="var(--aqua-wash)"/><text class="heading" x="96" y="170">${escapeXml(t.current)}</text><text x="96" y="231" class="title" style="font-size:52px">${remaining}</text><text class="heading" x="155" y="215">${escapeXml(t.remaining)}</text><text class="caption" x="155" y="237">${escapeXml(t.completionCounts(verified, planningComplete))}</text><text class="caption" x="700" y="205">${escapeXml(t.approved)}</text><text class="heading" x="700" y="230">${total}</text><text class="caption" x="1030" y="205">${escapeXml(t.deferred)}</text><text class="heading" x="1030" y="230">${deferredTasks.length}</text>
|
|
482
532
|
<text class="heading" x="72" y="300">${escapeXml(t.scopeReference)}</text><text class="caption" x="72" y="321">${escapeXml(t.noHistory)}</text><rect class="card" x="72" y="340" width="940" height="390" rx="9"/><text class="small" x="116" y="372">${escapeXml(t.remainingAxis)}</text><text class="small" x="550" y="704" text-anchor="middle">${escapeXml(t.notTime)}</text>
|
|
483
533
|
<g class="grid"><line x1="${x0}" y1="${y0}" x2="${x1}" y2="${y0}"/><line x1="${x0}" y1="${y0 + 80}" x2="${x1}" y2="${y0 + 80}"/><line x1="${x0}" y1="${y0 + 160}" x2="${x1}" y2="${y0 + 160}"/><line x1="${x0}" y1="${y1}" x2="${x1}" y2="${y1}"/><line x1="${x0}" y1="${y0}" x2="${x0}" y2="${y1}"/><line x1="${x1}" y1="${y0}" x2="${x1}" y2="${y1}"/></g><line class="axis" x1="${x0}" y1="${y1}" x2="${x1}" y2="${y1}"/><line class="axis" x1="${x0}" y1="${y0}" x2="${x0}" y2="${y1}"/>
|
|
484
|
-
<line x1="${x0}" y1="${y0}" x2="${currentX.toFixed(1)}" y2="${currentY.toFixed(1)}" stroke="var(--blue)" stroke-width="2"/><line x1="${currentX.toFixed(1)}" y1="${currentY.toFixed(1)}" x2="${x1}" y2="${y1}" stroke="var(--blue)" stroke-width="2" stroke-dasharray="6 5"/><circle cx="${x0}" cy="${y0}" r="7" fill="var(--blue)" stroke="var(--surface)" stroke-width="2"/><text class="heading" x="${x0 + 22}" y="${y0 + 18}">${escapeXml(t.logicalBaseline)}</text><text class="caption" x="${x0 + 22}" y="${y0 + 37}">${total} ${escapeXml(t.remainingShort)}</text>${currentMarker}<text class="heading" x="${Math.max(x0 + 150, currentX - 22)}" y="${Math.max(y0 + 60, currentY - 49)}" text-anchor="end">${escapeXml(t.currentSnapshot)}</text><text class="caption" x="${Math.max(x0 + 150, currentX - 22)}" y="${Math.max(y0 + 79, currentY - 30)}" text-anchor="end">${escapeXml(`${remaining} ${t.remainingShort} · ${
|
|
534
|
+
<line x1="${x0}" y1="${y0}" x2="${currentX.toFixed(1)}" y2="${currentY.toFixed(1)}" stroke="var(--blue)" stroke-width="2"/><line x1="${currentX.toFixed(1)}" y1="${currentY.toFixed(1)}" x2="${x1}" y2="${y1}" stroke="var(--blue)" stroke-width="2" stroke-dasharray="6 5"/><circle cx="${x0}" cy="${y0}" r="7" fill="var(--blue)" stroke="var(--surface)" stroke-width="2"/><text class="heading" x="${x0 + 22}" y="${y0 + 18}">${escapeXml(t.logicalBaseline)}</text><text class="caption" x="${x0 + 22}" y="${y0 + 37}">${total} ${escapeXml(t.remainingShort)}</text>${currentMarker}<text class="heading" x="${Math.max(x0 + 150, currentX - 22)}" y="${Math.max(y0 + 60, currentY - 49)}" text-anchor="end">${escapeXml(t.currentSnapshot)}</text><text class="caption" x="${Math.max(x0 + 150, currentX - 22)}" y="${Math.max(y0 + 79, currentY - 30)}" text-anchor="end">${escapeXml(`${remaining} ${t.remainingShort} · ${t.completionCounts(verified, planningComplete)}`)}</text>
|
|
485
535
|
<rect x="1040" y="340" width="460" height="390" rx="9" fill="var(--blue-wash)"/><text class="heading" x="1070" y="378">${escapeXml(t.howRead)}</text>${explanation}
|
|
486
|
-
<rect class="card" x="72" y="758" width="1428" height="${phaseBottom + 24 - 758}" rx="9"/><text class="heading" x="96" y="786">${escapeXml(t.phaseScope)}</text><text class="small" x="96" y="800">${escapeXml(`${t.active}: ${total}; ${t.verified}: ${verified}; ${t.remaining}: ${remaining}`)}</text><text class="small" x="
|
|
536
|
+
<rect class="card" x="72" y="758" width="1428" height="${phaseBottom + 24 - 758}" rx="9"/><text class="heading" x="96" y="786">${escapeXml(t.phaseScope)}</text><text class="small" x="96" y="800">${escapeXml(`${t.active}: ${total}; ${t.verified}: ${verified}; ${t.planningComplete}: ${planningComplete}; ${t.remaining}: ${remaining}`)}</text><text class="small" x="650" y="786" text-anchor="end">${escapeXml(t.verified)}</text><text class="small" x="750" y="786" text-anchor="end">${escapeXml(t.planningComplete)}</text><text class="small" x="850" y="786" text-anchor="end">${escapeXml(t.remaining)}</text><text class="small" x="930" y="786">${escapeXml(t.completion)}</text>${phaseRows}
|
|
487
537
|
<line class="grid" x1="72" y1="${height - 74}" x2="1500" y2="${height - 74}"/><text class="small" x="72" y="${height - 48}">${escapeXml(`${t.source}: pdp-wbs.md · progress.md (${t.reviewed}: ${reviewed})`)}</text><text class="small" x="1500" y="${height - 48}" text-anchor="end">${escapeXml(t.caveat)}</text>`;
|
|
488
538
|
return svgDocument(
|
|
489
539
|
`${suiteName} ${t.burndownTitle}`,
|
|
490
|
-
`${t.burndownTitle}. ${t.burndownDescription(total, verified, remaining)} ${t.noHistory}`,
|
|
540
|
+
`${t.burndownTitle}. ${t.burndownDescription(total, verified, planningComplete, remaining)} ${t.noHistory}`,
|
|
491
541
|
body,
|
|
492
542
|
height,
|
|
493
543
|
t.metadata,
|
|
544
|
+
planningComplete > 0,
|
|
494
545
|
);
|
|
495
546
|
}
|
|
496
547
|
|