@dailephd/my-dev-kit-lab 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -0
- package/benchmarks/contracts/benchmark-project-profiles.json +1199 -0
- package/benchmarks/contracts/todo-behavior.md +70 -0
- package/benchmarks/contracts/todo-benchmark-case.json +227 -0
- package/benchmarks/projects/README.md +34 -0
- package/benchmarks/projects/task-analytics-large-mixed/README.md +1 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/__init__.py +3 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/fixtures.py +6 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/metrics.py +29 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/models.py +21 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/parser.py +16 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/pipeline.py +9 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/quality.py +8 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/task_analytics/reporting.py +11 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_metrics.py +19 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_parser.py +15 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_quality.py +19 -0
- package/benchmarks/projects/task-analytics-large-mixed/py/tests/test_reporting.py +15 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/package.json +12 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/index.ts +11 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/models/analyticsSnapshot.ts +20 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/models/project.ts +5 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/models/task.ts +10 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/reporting/buildProjectLeaderboard.ts +7 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/reporting/formatTaskHealthReport.ts +13 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/buildAnalyticsSnapshot.ts +39 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/completeTask.ts +10 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/createTask.ts +21 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/listTasksByProject.ts +6 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/store/projectStore.ts +20 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/store/taskStore.ts +44 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/validation/projectValidation.ts +12 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/src/validation/taskValidation.ts +18 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/buildAnalyticsSnapshot.test.ts +48 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/completeTask.test.ts +21 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/createTask.test.ts +31 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/listTasksByProject.test.ts +18 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tests/reporting.test.ts +19 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/tsconfig.json +12 -0
- package/benchmarks/projects/task-analytics-large-mixed/ts/vitest.config.ts +5 -0
- package/benchmarks/projects/task-workflow-medium-ts/README.md +1 -0
- package/benchmarks/projects/task-workflow-medium-ts/package.json +12 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/index.ts +9 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/models/project.ts +6 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/models/task.ts +39 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/completeTask.ts +15 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/createTask.ts +26 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/filterTasks.ts +17 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/importTasks.ts +33 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/services/summarizeTasks.ts +30 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/store/taskStore.ts +76 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/utils/deterministicId.ts +3 -0
- package/benchmarks/projects/task-workflow-medium-ts/src/validation/taskValidation.ts +45 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/completeTask.test.ts +16 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/createTask.test.ts +21 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/filterTasks.test.ts +18 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/importTasks.test.ts +22 -0
- package/benchmarks/projects/task-workflow-medium-ts/tests/summarizeTasks.test.ts +29 -0
- package/benchmarks/projects/task-workflow-medium-ts/tsconfig.json +12 -0
- package/benchmarks/projects/task-workflow-medium-ts/vitest.config.ts +5 -0
- package/benchmarks/projects/todo-js/README.md +3 -0
- package/benchmarks/projects/todo-js/package.json +11 -0
- package/benchmarks/projects/todo-js/src/index.js +2 -0
- package/benchmarks/projects/todo-js/src/taskService.js +37 -0
- package/benchmarks/projects/todo-js/src/taskStore.js +28 -0
- package/benchmarks/projects/todo-js/tests/taskService.test.js +45 -0
- package/benchmarks/projects/todo-js/vitest.config.js +5 -0
- package/benchmarks/projects/todo-mixed-ts-py/README.md +3 -0
- package/benchmarks/projects/todo-mixed-ts-py/package.json +13 -0
- package/benchmarks/projects/todo-mixed-ts-py/python/task_service.py +76 -0
- package/benchmarks/projects/todo-mixed-ts-py/src/taskCli.ts +38 -0
- package/benchmarks/projects/todo-mixed-ts-py/tests/mixedBoundary.test.ts +18 -0
- package/benchmarks/projects/todo-mixed-ts-py/tsconfig.json +12 -0
- package/benchmarks/projects/todo-mixed-ts-py/vitest.config.ts +5 -0
- package/benchmarks/projects/todo-python/README.md +3 -0
- package/benchmarks/projects/todo-python/src/__init__.py +4 -0
- package/benchmarks/projects/todo-python/src/task_service.py +32 -0
- package/benchmarks/projects/todo-python/src/task_store.py +28 -0
- package/benchmarks/projects/todo-python/tests/test_task_service.py +52 -0
- package/benchmarks/projects/todo-ts/README.md +3 -0
- package/benchmarks/projects/todo-ts/package.json +12 -0
- package/benchmarks/projects/todo-ts/src/index.ts +2 -0
- package/benchmarks/projects/todo-ts/src/taskService.ts +41 -0
- package/benchmarks/projects/todo-ts/src/taskStore.ts +34 -0
- package/benchmarks/projects/todo-ts/tests/taskService.test.ts +45 -0
- package/benchmarks/projects/todo-ts/tsconfig.json +12 -0
- package/benchmarks/projects/todo-ts/vitest.config.ts +5 -0
- package/dist/scripts/build-gallery.js +3 -0
- package/dist/scripts/capture-demo-report.js +3 -0
- package/dist/scripts/evaluate-token-savings.js +2 -0
- package/dist/scripts/experiments/describeExperiment.js +143 -0
- package/dist/scripts/experiments/listExperiments.js +44 -0
- package/dist/scripts/experiments/runExperiment.js +199 -0
- package/dist/scripts/generate-experiment-plots.js +3 -0
- package/dist/scripts/generate-prompt-variants.js +2 -0
- package/dist/scripts/render-experiment-report.js +2 -0
- package/dist/scripts/run-agent-prompt.js +2 -0
- package/dist/scripts/run-controlled-experiment.js +2 -0
- package/dist/scripts/run-final-demo.js +3 -0
- package/dist/scripts/run-lab-demo.js +5 -0
- package/dist/scripts/run-visualization-demos.js +3 -0
- package/dist/scripts/security/runCodeql.js +57 -0
- package/dist/scripts/security/runDependencyChecks.js +57 -0
- package/dist/scripts/security/runFuzzSmoke.js +29 -0
- package/dist/scripts/security/runPackageChecks.js +56 -0
- package/dist/scripts/security/runSemgrep.js +63 -0
- package/dist/scripts/security/validate.js +117 -0
- package/dist/scripts/verify-benchmarks.js +202 -0
- package/dist/src/agents/adapters/claudeAdapter.js +37 -0
- package/dist/src/agents/adapters/codexAdapter.js +110 -0
- package/dist/src/agents/adapters/fakeAgentAdapter.js +101 -0
- package/dist/src/agents/agentRegistry.js +21 -0
- package/dist/src/agents/index.js +7 -0
- package/dist/src/agents/parseAgentTokenUsage.js +137 -0
- package/dist/src/agents/runAgentPrompt.js +38 -0
- package/dist/src/agents/types.js +1 -0
- package/dist/src/commands/buildGalleryCommand.js +56 -0
- package/dist/src/commands/captureDemoReport.js +116 -0
- package/dist/src/commands/evaluateTokenSavings.js +175 -0
- package/dist/src/commands/generateExperimentPlotsCommand.js +38 -0
- package/dist/src/commands/generatePromptVariants.js +67 -0
- package/dist/src/commands/renderExperimentReportCommand.js +131 -0
- package/dist/src/commands/runAgentPromptCommand.js +132 -0
- package/dist/src/commands/runControlledExperimentCommand.js +174 -0
- package/dist/src/commands/runFinalDemoCommand.js +123 -0
- package/dist/src/commands/runLabDemo.js +62 -0
- package/dist/src/commands/runVisualizationDemosCommand.js +67 -0
- package/dist/src/core/commandLine.js +59 -0
- package/dist/src/core/countTokens.js +8 -0
- package/dist/src/core/fileGlobs.js +100 -0
- package/dist/src/core/localProjectTarget.js +75 -0
- package/dist/src/core/pathSafety.js +19 -0
- package/dist/src/core/pythonCommand.js +30 -0
- package/dist/src/core/resolveCommand.js +110 -0
- package/dist/src/core/runMeasuredCommand.js +143 -0
- package/dist/src/evaluation/benchmarkMetadata.js +207 -0
- package/dist/src/evaluation/buildExperimentMatrix.js +75 -0
- package/dist/src/evaluation/classifyAgentRunOutcome.js +40 -0
- package/dist/src/evaluation/compareExperimentRuns.js +79 -0
- package/dist/src/evaluation/compareTokenSavings.js +47 -0
- package/dist/src/evaluation/controlledExperimentTypes.js +1 -0
- package/dist/src/evaluation/index.js +18 -0
- package/dist/src/evaluation/parseAgentAnswer.js +230 -0
- package/dist/src/evaluation/projectComplexity.js +126 -0
- package/dist/src/evaluation/projectFileTree.js +83 -0
- package/dist/src/evaluation/readEvaluationCases.js +59 -0
- package/dist/src/evaluation/renderTokenSavingsReportInput.js +55 -0
- package/dist/src/evaluation/runControlledExperiment.js +158 -0
- package/dist/src/evaluation/runMyDevKitRetrieval.js +197 -0
- package/dist/src/evaluation/runRawFullFileBaseline.js +31 -0
- package/dist/src/evaluation/scoreCorrectness.js +127 -0
- package/dist/src/evaluation/types.js +1 -0
- package/dist/src/evaluation/writeExperimentArtifacts.js +104 -0
- package/dist/src/evaluation/writeTokenSavingsArtifacts.js +57 -0
- package/dist/src/experiments/config.js +24 -0
- package/dist/src/experiments/defaultRegistry.js +7 -0
- package/dist/src/experiments/errors.js +18 -0
- package/dist/src/experiments/index.js +9 -0
- package/dist/src/experiments/outputPaths.js +25 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/config.js +37 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/index.js +3 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/plugin.js +83 -0
- package/dist/src/experiments/plugins/contextStrategyComparison/resultMapping.js +260 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/registry.js +43 -0
- package/dist/src/experiments/results.js +48 -0
- package/dist/src/experiments/runner.js +181 -0
- package/dist/src/experiments/target.js +8 -0
- package/dist/src/experiments/types.js +1 -0
- package/dist/src/gallery/index.js +2 -0
- package/dist/src/gallery/types.js +1 -0
- package/dist/src/gallery/writeGalleryManifest.js +214 -0
- package/dist/src/index.js +12 -0
- package/dist/src/plots/buildExperimentPlotData.js +137 -0
- package/dist/src/plots/index.js +4 -0
- package/dist/src/plots/renderSvgChart.js +82 -0
- package/dist/src/plots/types.js +1 -0
- package/dist/src/plots/writePlotArtifacts.js +46 -0
- package/dist/src/prompts/buildPromptContext.js +68 -0
- package/dist/src/prompts/generateMyDevKitPrompt.js +106 -0
- package/dist/src/prompts/generatePromptVariants.js +36 -0
- package/dist/src/prompts/generateRawFullFilePrompt.js +97 -0
- package/dist/src/prompts/index.js +7 -0
- package/dist/src/prompts/measurePromptComplexity.js +41 -0
- package/dist/src/prompts/types.js +1 -0
- package/dist/src/prompts/writePromptArtifacts.js +43 -0
- package/dist/src/report/buildExperimentReportInput.js +339 -0
- package/dist/src/report/experimentReportTypes.js +1 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +153 -0
- package/dist/src/report/experiments/experimentReportModel.js +1 -0
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +133 -0
- package/dist/src/report/experiments/writePluginExperimentReports.js +30 -0
- package/dist/src/report/index.js +8 -0
- package/dist/src/report/renderExperimentHtmlReport.js +354 -0
- package/dist/src/report/renderHtmlReport.js +103 -0
- package/dist/src/report/types.js +10 -0
- package/dist/src/report/writeExperimentReportArtifacts.js +38 -0
- package/dist/src/report/writeReportArtifacts.js +39 -0
- package/dist/src/screenshot/captureReportScreenshot.js +75 -0
- package/dist/src/screenshot/index.js +2 -0
- package/dist/src/screenshot/types.js +1 -0
- package/dist/src/securityValidation/artifacts.js +15 -0
- package/dist/src/securityValidation/cliAdversarial/adversarialCliConfig.js +38 -0
- package/dist/src/securityValidation/cliAdversarial/dataVolumeChecks.js +194 -0
- package/dist/src/securityValidation/cliAdversarial/jsonStdoutChecks.js +359 -0
- package/dist/src/securityValidation/cliAdversarial/malformedArtifactChecks.js +284 -0
- package/dist/src/securityValidation/cliAdversarial/malformedArtifactFixtures.js +79 -0
- package/dist/src/securityValidation/cliAdversarial/pathBoundaryChecks.js +431 -0
- package/dist/src/securityValidation/cliAdversarial/pathCases.js +144 -0
- package/dist/src/securityValidation/cliAdversarial/readOnlyBoundaryChecks.js +294 -0
- package/dist/src/securityValidation/cliAdversarial/runAdversarialCheck.js +149 -0
- package/dist/src/securityValidation/cliAdversarial/subprocessSafetyChecks.js +214 -0
- package/dist/src/securityValidation/cliAdversarial/tempWorkspace.js +160 -0
- package/dist/src/securityValidation/commandRunner.js +136 -0
- package/dist/src/securityValidation/config.js +39 -0
- package/dist/src/securityValidation/dependencies/parseNpmAudit.js +115 -0
- package/dist/src/securityValidation/dependencies/parseNpmLs.js +71 -0
- package/dist/src/securityValidation/dependencies/parseNpmOutdated.js +41 -0
- package/dist/src/securityValidation/dependencies/runDependencyChecks.js +239 -0
- package/dist/src/securityValidation/dependencies/runOsvScanner.js +43 -0
- package/dist/src/securityValidation/fuzz/fuzzHarness.js +61 -0
- package/dist/src/securityValidation/fuzz/fuzzTargets.js +204 -0
- package/dist/src/securityValidation/fuzz/randomInput.js +0 -0
- package/dist/src/securityValidation/index.js +34 -0
- package/dist/src/securityValidation/packageChecks/forbiddenPackageContents.js +67 -0
- package/dist/src/securityValidation/packageChecks/parseNpmPackDryRun.js +56 -0
- package/dist/src/securityValidation/packageChecks/runPackageChecks.js +88 -0
- package/dist/src/securityValidation/report/renderSecurityReport.js +248 -0
- package/dist/src/securityValidation/report/securityReportTypes.js +1 -0
- package/dist/src/securityValidation/staticScans/codeql.js +66 -0
- package/dist/src/securityValidation/staticScans/semgrep.js +180 -0
- package/dist/src/securityValidation/testMatrix.js +535 -0
- package/dist/src/securityValidation/types.js +34 -0
- package/dist/src/securityValidation/validate/resolveTarget.js +32 -0
- package/dist/src/securityValidation/validate/runSecurityValidation.js +169 -0
- package/dist/src/securityValidation/validate/verdict.js +73 -0
- package/dist/src/visualizationDemos/buildMyDevKitVisualizationCommands.js +59 -0
- package/dist/src/visualizationDemos/index.js +4 -0
- package/dist/src/visualizationDemos/runVisualizationDemos.js +82 -0
- package/dist/src/visualizationDemos/types.js +1 -0
- package/dist/src/visualizationDemos/writeVisualizationDemoArtifacts.js +25 -0
- package/docs/METRICS.md +286 -0
- package/examples/demo-report-input.json +78 -0
- package/examples/lab-demo-cases.json +35 -0
- package/examples/real-agent-campaign-cases.json +118 -0
- package/examples/token-savings-cases.json +122 -0
- package/package.json +91 -0
- package/tests/fixtures/fake-adversarial-cli.js +152 -0
- package/tests/fixtures/fake-my-dev-kit-cli.js +83 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Todo Core Benchmark Behavior
|
|
2
|
+
|
|
3
|
+
Purpose:
|
|
4
|
+
|
|
5
|
+
This benchmark defines a small, deterministic Todo Core program used for retrieval, benchmark, and later evaluation workflows in my-dev-kit-lab.
|
|
6
|
+
|
|
7
|
+
These projects are small by design so they are cheap to index, test, compare, and use in future raw full-file versus retrieved-context experiments.
|
|
8
|
+
|
|
9
|
+
Shared task shape:
|
|
10
|
+
|
|
11
|
+
- `id: string`
|
|
12
|
+
- `title: string`
|
|
13
|
+
- `completed: boolean`
|
|
14
|
+
|
|
15
|
+
Required operations:
|
|
16
|
+
|
|
17
|
+
- `createTask(title)`
|
|
18
|
+
- `completeTask(id)`
|
|
19
|
+
- `listTasks()`
|
|
20
|
+
- `listOpenTasks()`
|
|
21
|
+
- `summarizeTasks()`
|
|
22
|
+
|
|
23
|
+
Expected validation behavior:
|
|
24
|
+
|
|
25
|
+
- `createTask` must reject empty or whitespace-only titles
|
|
26
|
+
- validation failure must raise a clear error
|
|
27
|
+
|
|
28
|
+
Expected deterministic ID behavior:
|
|
29
|
+
|
|
30
|
+
- IDs must be deterministic for a fresh in-memory store
|
|
31
|
+
- the first created task must use `task-1`
|
|
32
|
+
- the second created task must use `task-2`
|
|
33
|
+
|
|
34
|
+
Required test coverage:
|
|
35
|
+
|
|
36
|
+
- create task
|
|
37
|
+
- complete task
|
|
38
|
+
- list all tasks
|
|
39
|
+
- list open tasks
|
|
40
|
+
- summarize task counts
|
|
41
|
+
- validate empty task title
|
|
42
|
+
- deterministic IDs
|
|
43
|
+
|
|
44
|
+
Benchmark purpose:
|
|
45
|
+
|
|
46
|
+
The benchmark exists to keep multiple language layouts behaviorally aligned so later prompts can compare retrieval and evaluation approaches without changing the underlying task semantics.
|
|
47
|
+
|
|
48
|
+
Project profile metadata:
|
|
49
|
+
|
|
50
|
+
- project profiles live in `benchmarks/contracts/benchmark-project-profiles.json`
|
|
51
|
+
- every profile names the benchmark project, language mix, primary language, source roots, test roots, compact file tree, benchmark purpose, expected use cases, complexity metrics, complexity score, and scoring formula
|
|
52
|
+
- file-tree entries use relative paths and include source, test, README, package, and config files
|
|
53
|
+
- file-tree entries must exclude generated or external folders such as `node_modules`, `dist`, `build`, `coverage`, `lab-output`, `.git`, and interpreter caches
|
|
54
|
+
|
|
55
|
+
Project complexity formula:
|
|
56
|
+
|
|
57
|
+
- `projectComplexityScore` ranges from 0 to 100
|
|
58
|
+
- every value is normalized as `min(value / cap, 1)`
|
|
59
|
+
- formula: `100 * ((0.20 * normalizedSourceFileCount) + (0.20 * normalizedSourceLinesOfCode) + (0.15 * normalizedLanguageCount) + (0.15 * normalizedInternalImportCount) + (0.10 * normalizedMaxFileLines) + (0.10 * normalizedExpectedRelevantFilesAverage) + (0.10 * normalizedExpectedRelevantSymbolsAverage))`
|
|
60
|
+
- caps are `sourceFileCount: 20`, `sourceLinesOfCode: 2000`, `languageCount: 4`, `internalImportCount: 50`, `maxFileLines: 300`, `expectedRelevantFilesAverage: 10`, and `expectedRelevantSymbolsAverage: 20`
|
|
61
|
+
- the final score is rounded to the nearest integer
|
|
62
|
+
|
|
63
|
+
Answer key requirements:
|
|
64
|
+
|
|
65
|
+
- every benchmark case must include `answerKey`
|
|
66
|
+
- `answerKey.expectedFiles` lists files a correct answer may need to cite
|
|
67
|
+
- `answerKey.expectedSymbols` lists relevant symbols
|
|
68
|
+
- `answerKey.expectedFacts` lists weighted facts with unique `id`, `text`, `weight`, and `required`
|
|
69
|
+
- `answerKey.minimumCorrectFacts` defines the future scoring threshold, but scoring is not implemented in this prompt
|
|
70
|
+
- optional `forbiddenWrongClaims` records claims that should be penalized by a later correctness scorer
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "todo-create-task",
|
|
4
|
+
"title": "Create a task with deterministic IDs",
|
|
5
|
+
"task": "Find the code path that creates a task and assigns its deterministic id.",
|
|
6
|
+
"query": "create task deterministic id task-1",
|
|
7
|
+
"expectedOperation": "createTask",
|
|
8
|
+
"expectedSymbols": ["createTask", "create_task", "TaskService"],
|
|
9
|
+
"expectedFilesByProject": {
|
|
10
|
+
"todo-ts": ["src/taskService.ts", "src/taskStore.ts"],
|
|
11
|
+
"todo-python": ["src/task_service.py", "src/task_store.py"],
|
|
12
|
+
"todo-js": ["src/taskService.js", "src/taskStore.js"],
|
|
13
|
+
"todo-mixed-ts-py": ["src/taskCli.ts", "python/task_service.py"]
|
|
14
|
+
},
|
|
15
|
+
"answerKey": {
|
|
16
|
+
"expectedFiles": ["src/taskService.ts", "src/taskStore.ts", "src/task_service.py", "src/task_store.py", "src/taskService.js", "src/taskStore.js", "src/taskCli.ts", "python/task_service.py"],
|
|
17
|
+
"expectedSymbols": ["createTask", "create_task", "TaskService"],
|
|
18
|
+
"expectedFacts": [
|
|
19
|
+
{
|
|
20
|
+
"id": "create-validates-title",
|
|
21
|
+
"text": "Task creation validates that the title is not empty or whitespace-only.",
|
|
22
|
+
"weight": 1,
|
|
23
|
+
"required": true
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "create-deterministic-id",
|
|
27
|
+
"text": "Task creation assigns deterministic IDs such as task-1 and task-2.",
|
|
28
|
+
"weight": 1,
|
|
29
|
+
"required": true
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "create-stores-incomplete-task",
|
|
33
|
+
"text": "A newly created task is stored with completed set to false.",
|
|
34
|
+
"weight": 1,
|
|
35
|
+
"required": true
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"forbiddenWrongClaims": ["Task IDs are random UUIDs.", "New tasks are completed by default."],
|
|
39
|
+
"minimumCorrectFacts": 2,
|
|
40
|
+
"notes": "A correct answer must identify deterministic ID assignment and the creation path."
|
|
41
|
+
},
|
|
42
|
+
"promptComplexityHint": "single operation with storage side effect",
|
|
43
|
+
"projectComplexityRelevance": "uses service and store files in single-language projects and boundary files in the mixed project",
|
|
44
|
+
"rawIncludeGlobs": ["src/**/*", "python/**/*", "tests/**/*"],
|
|
45
|
+
"notes": "The creation path should be obvious and small across all projects."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "todo-complete-task",
|
|
49
|
+
"title": "Complete an existing task",
|
|
50
|
+
"task": "Locate the code path that completes a task by id.",
|
|
51
|
+
"query": "complete task by id",
|
|
52
|
+
"expectedOperation": "completeTask",
|
|
53
|
+
"expectedSymbols": ["completeTask", "complete_task"],
|
|
54
|
+
"expectedFilesByProject": {
|
|
55
|
+
"todo-ts": ["src/taskService.ts"],
|
|
56
|
+
"todo-python": ["src/task_service.py"],
|
|
57
|
+
"todo-js": ["src/taskService.js"],
|
|
58
|
+
"todo-mixed-ts-py": ["python/task_service.py", "src/taskCli.ts"]
|
|
59
|
+
},
|
|
60
|
+
"answerKey": {
|
|
61
|
+
"expectedFiles": ["src/taskService.ts", "src/task_service.py", "src/taskService.js", "python/task_service.py", "src/taskCli.ts"],
|
|
62
|
+
"expectedSymbols": ["completeTask", "complete_task"],
|
|
63
|
+
"expectedFacts": [
|
|
64
|
+
{
|
|
65
|
+
"id": "complete-finds-by-id",
|
|
66
|
+
"text": "Completion locates the matching task by id.",
|
|
67
|
+
"weight": 1,
|
|
68
|
+
"required": true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "complete-marks-completed",
|
|
72
|
+
"text": "Completion marks the matching task as completed.",
|
|
73
|
+
"weight": 1,
|
|
74
|
+
"required": true
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "complete-preserves-other-tasks",
|
|
78
|
+
"text": "Completion does not mark unrelated tasks as completed.",
|
|
79
|
+
"weight": 1,
|
|
80
|
+
"required": false
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"forbiddenWrongClaims": ["completeTask deletes the task.", "completeTask marks every task as completed."],
|
|
84
|
+
"minimumCorrectFacts": 2,
|
|
85
|
+
"notes": "A correct answer must distinguish targeted completion from list or delete behavior."
|
|
86
|
+
},
|
|
87
|
+
"promptComplexityHint": "single update operation by identifier",
|
|
88
|
+
"projectComplexityRelevance": "primarily service logic, with mixed project boundary coverage",
|
|
89
|
+
"rawIncludeGlobs": ["src/**/*", "python/**/*", "tests/**/*"],
|
|
90
|
+
"notes": "Completion must flip the completed flag for the targeted task."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "todo-list-open-tasks",
|
|
94
|
+
"title": "List open tasks only",
|
|
95
|
+
"task": "Identify the logic that returns only incomplete tasks.",
|
|
96
|
+
"query": "list open tasks incomplete tasks",
|
|
97
|
+
"expectedOperation": "listOpenTasks",
|
|
98
|
+
"expectedSymbols": ["listOpenTasks", "list_open_tasks"],
|
|
99
|
+
"expectedFilesByProject": {
|
|
100
|
+
"todo-ts": ["src/taskService.ts"],
|
|
101
|
+
"todo-python": ["src/task_service.py"],
|
|
102
|
+
"todo-js": ["src/taskService.js"],
|
|
103
|
+
"todo-mixed-ts-py": ["python/task_service.py", "src/taskCli.ts"]
|
|
104
|
+
},
|
|
105
|
+
"answerKey": {
|
|
106
|
+
"expectedFiles": ["src/taskService.ts", "src/task_service.py", "src/taskService.js", "python/task_service.py", "src/taskCli.ts"],
|
|
107
|
+
"expectedSymbols": ["listOpenTasks", "list_open_tasks"],
|
|
108
|
+
"expectedFacts": [
|
|
109
|
+
{
|
|
110
|
+
"id": "open-filters-incomplete",
|
|
111
|
+
"text": "Open-task listing returns tasks where completed is false.",
|
|
112
|
+
"weight": 1,
|
|
113
|
+
"required": true
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"id": "open-separate-from-all",
|
|
117
|
+
"text": "Open-task listing is separate from list-all task behavior.",
|
|
118
|
+
"weight": 1,
|
|
119
|
+
"required": true
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "open-preserves-task-shape",
|
|
123
|
+
"text": "Open-task listing returns task objects with id, title, and completed fields.",
|
|
124
|
+
"weight": 1,
|
|
125
|
+
"required": false
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"forbiddenWrongClaims": ["listOpenTasks returns completed tasks only.", "listOpenTasks mutates task completion state."],
|
|
129
|
+
"minimumCorrectFacts": 2,
|
|
130
|
+
"notes": "A correct answer must identify filtering on incomplete tasks."
|
|
131
|
+
},
|
|
132
|
+
"promptComplexityHint": "read-only filtered list operation",
|
|
133
|
+
"projectComplexityRelevance": "service-level filter with no storage mutation",
|
|
134
|
+
"rawIncludeGlobs": ["src/**/*", "python/**/*", "tests/**/*"],
|
|
135
|
+
"notes": "Open-task listing should remain separate from list-all behavior."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "todo-summarize-tasks",
|
|
139
|
+
"title": "Summarize task counts",
|
|
140
|
+
"task": "Find the summary logic that reports total, open, and completed counts.",
|
|
141
|
+
"query": "summarize tasks total open completed",
|
|
142
|
+
"expectedOperation": "summarizeTasks",
|
|
143
|
+
"expectedSymbols": ["summarizeTasks", "summarize_tasks"],
|
|
144
|
+
"expectedFilesByProject": {
|
|
145
|
+
"todo-ts": ["src/taskService.ts"],
|
|
146
|
+
"todo-python": ["src/task_service.py"],
|
|
147
|
+
"todo-js": ["src/taskService.js"],
|
|
148
|
+
"todo-mixed-ts-py": ["python/task_service.py", "src/taskCli.ts"]
|
|
149
|
+
},
|
|
150
|
+
"answerKey": {
|
|
151
|
+
"expectedFiles": ["src/taskService.ts", "src/task_service.py", "src/taskService.js", "python/task_service.py", "src/taskCli.ts"],
|
|
152
|
+
"expectedSymbols": ["summarizeTasks", "summarize_tasks"],
|
|
153
|
+
"expectedFacts": [
|
|
154
|
+
{
|
|
155
|
+
"id": "summary-total",
|
|
156
|
+
"text": "Task summary reports the total task count.",
|
|
157
|
+
"weight": 1,
|
|
158
|
+
"required": true
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "summary-open",
|
|
162
|
+
"text": "Task summary reports the open task count.",
|
|
163
|
+
"weight": 1,
|
|
164
|
+
"required": true
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "summary-completed",
|
|
168
|
+
"text": "Task summary reports the completed task count.",
|
|
169
|
+
"weight": 1,
|
|
170
|
+
"required": true
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
"forbiddenWrongClaims": ["Summary returns only a string.", "Summary omits completed tasks."],
|
|
174
|
+
"minimumCorrectFacts": 3,
|
|
175
|
+
"notes": "A correct answer must identify the total, open, and completed fields."
|
|
176
|
+
},
|
|
177
|
+
"promptComplexityHint": "aggregate read operation",
|
|
178
|
+
"projectComplexityRelevance": "tests aggregation logic across single-language and mixed-language projects",
|
|
179
|
+
"rawIncludeGlobs": ["src/**/*", "python/**/*", "tests/**/*"],
|
|
180
|
+
"notes": "Summary shape must stay aligned across every benchmark project."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"id": "todo-empty-title-validation",
|
|
184
|
+
"title": "Reject an empty task title",
|
|
185
|
+
"task": "Locate the validation that rejects blank task titles.",
|
|
186
|
+
"query": "empty title validation create task blank title",
|
|
187
|
+
"expectedOperation": "createTask",
|
|
188
|
+
"expectedSymbols": ["createTask", "create_task", "validate"],
|
|
189
|
+
"expectedFilesByProject": {
|
|
190
|
+
"todo-ts": ["src/taskService.ts"],
|
|
191
|
+
"todo-python": ["src/task_service.py"],
|
|
192
|
+
"todo-js": ["src/taskService.js"],
|
|
193
|
+
"todo-mixed-ts-py": ["python/task_service.py", "src/taskCli.ts"]
|
|
194
|
+
},
|
|
195
|
+
"answerKey": {
|
|
196
|
+
"expectedFiles": ["src/taskService.ts", "src/task_service.py", "src/taskService.js", "python/task_service.py", "src/taskCli.ts"],
|
|
197
|
+
"expectedSymbols": ["createTask", "create_task", "validate"],
|
|
198
|
+
"expectedFacts": [
|
|
199
|
+
{
|
|
200
|
+
"id": "empty-title-trim",
|
|
201
|
+
"text": "Title validation rejects empty or whitespace-only task titles.",
|
|
202
|
+
"weight": 1,
|
|
203
|
+
"required": true
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"id": "empty-title-create-path",
|
|
207
|
+
"text": "Title validation is part of the create task path.",
|
|
208
|
+
"weight": 1,
|
|
209
|
+
"required": true
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"id": "empty-title-error",
|
|
213
|
+
"text": "Invalid titles produce a clear deterministic error instead of creating a task.",
|
|
214
|
+
"weight": 1,
|
|
215
|
+
"required": true
|
|
216
|
+
}
|
|
217
|
+
],
|
|
218
|
+
"forbiddenWrongClaims": ["Blank titles are silently converted to Untitled.", "Validation happens only in tests."],
|
|
219
|
+
"minimumCorrectFacts": 2,
|
|
220
|
+
"notes": "A correct answer must locate validation and explain the rejected blank title behavior."
|
|
221
|
+
},
|
|
222
|
+
"promptComplexityHint": "validation behavior inside create operation",
|
|
223
|
+
"projectComplexityRelevance": "requires reading creation logic rather than only public exports",
|
|
224
|
+
"rawIncludeGlobs": ["src/**/*", "python/**/*", "tests/**/*"],
|
|
225
|
+
"notes": "Validation errors should be clear and deterministic."
|
|
226
|
+
}
|
|
227
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Benchmark Projects
|
|
2
|
+
|
|
3
|
+
This directory contains deterministic benchmark projects used by my-dev-kit-lab.
|
|
4
|
+
|
|
5
|
+
The current suite mixes baseline Todo fixtures with more complex workflow and analytics projects:
|
|
6
|
+
|
|
7
|
+
- `todo-ts`
|
|
8
|
+
- `todo-js`
|
|
9
|
+
- `todo-python`
|
|
10
|
+
- `todo-mixed-ts-py`
|
|
11
|
+
- `task-workflow-medium-ts`
|
|
12
|
+
- `task-analytics-large-mixed`
|
|
13
|
+
|
|
14
|
+
The Todo fixtures stay intentionally small so validation and smoke experiments remain cheap. The newer medium and large projects are intentionally more connected so raw full-file context and guided retrieval diverge more meaningfully.
|
|
15
|
+
|
|
16
|
+
Benchmark project profiles live in `benchmarks/contracts/benchmark-project-profiles.json`.
|
|
17
|
+
|
|
18
|
+
Each profile records:
|
|
19
|
+
|
|
20
|
+
- project identity and description
|
|
21
|
+
- language mix and primary language
|
|
22
|
+
- source roots and test roots
|
|
23
|
+
- deterministic file-tree metadata
|
|
24
|
+
- complexity metrics and complexity score
|
|
25
|
+
- benchmark purpose and expected use cases
|
|
26
|
+
|
|
27
|
+
Current complexity levels:
|
|
28
|
+
|
|
29
|
+
- `todo-ts`: small
|
|
30
|
+
- `todo-js`: small
|
|
31
|
+
- `todo-python`: small
|
|
32
|
+
- `todo-mixed-ts-py`: mixed-language
|
|
33
|
+
- `task-workflow-medium-ts`: medium
|
|
34
|
+
- `task-analytics-large-mixed`: large
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Mixed TypeScript and Python benchmark project for deterministic task analytics, quality labeling, and report generation.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
def sample_rows():
|
|
2
|
+
return [
|
|
3
|
+
{"task_id": "task-1", "project_id": "alpha", "completed": True, "story_points": 5, "updated_day": 8},
|
|
4
|
+
{"task_id": "task-2", "project_id": "alpha", "completed": False, "story_points": 3, "updated_day": 1},
|
|
5
|
+
{"task_id": "task-3", "project_id": "beta", "completed": False, "story_points": 8, "updated_day": 1},
|
|
6
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from .models import ProjectMetrics
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
STALE_DAY_THRESHOLD = 10
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def calculate_project_metrics(tasks, current_day):
|
|
8
|
+
grouped = {}
|
|
9
|
+
for task in tasks:
|
|
10
|
+
grouped.setdefault(task.project_id, []).append(task)
|
|
11
|
+
|
|
12
|
+
metrics = []
|
|
13
|
+
for project_id in sorted(grouped):
|
|
14
|
+
project_tasks = grouped[project_id]
|
|
15
|
+
completed = sum(1 for task in project_tasks if task.completed)
|
|
16
|
+
stale = sum(1 for task in project_tasks if not task.completed and current_day - task.updated_day >= STALE_DAY_THRESHOLD)
|
|
17
|
+
total = len(project_tasks)
|
|
18
|
+
metrics.append(
|
|
19
|
+
ProjectMetrics(
|
|
20
|
+
project_id=project_id,
|
|
21
|
+
total_tasks=total,
|
|
22
|
+
completed_tasks=completed,
|
|
23
|
+
open_tasks=total - completed,
|
|
24
|
+
stale_tasks=stale,
|
|
25
|
+
completion_rate=round((completed / total) * 100, 2) if total else 0.0,
|
|
26
|
+
average_story_points=round(sum(task.story_points for task in project_tasks) / total, 2) if total else 0.0,
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
return metrics
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass(frozen=True)
|
|
5
|
+
class TaskRecord:
|
|
6
|
+
task_id: str
|
|
7
|
+
project_id: str
|
|
8
|
+
completed: bool
|
|
9
|
+
story_points: int
|
|
10
|
+
updated_day: int
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class ProjectMetrics:
|
|
15
|
+
project_id: str
|
|
16
|
+
total_tasks: int
|
|
17
|
+
completed_tasks: int
|
|
18
|
+
open_tasks: int
|
|
19
|
+
stale_tasks: int
|
|
20
|
+
completion_rate: float
|
|
21
|
+
average_story_points: float
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .models import TaskRecord
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def parse_task_rows(rows):
|
|
5
|
+
records = []
|
|
6
|
+
for row in rows:
|
|
7
|
+
records.append(
|
|
8
|
+
TaskRecord(
|
|
9
|
+
task_id=str(row["task_id"]),
|
|
10
|
+
project_id=str(row["project_id"]),
|
|
11
|
+
completed=bool(row["completed"]),
|
|
12
|
+
story_points=int(row["story_points"]),
|
|
13
|
+
updated_day=int(row["updated_day"]),
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
return records
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from .metrics import calculate_project_metrics
|
|
2
|
+
from .parser import parse_task_rows
|
|
3
|
+
from .reporting import build_health_report
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def build_report_from_rows(rows, current_day):
|
|
7
|
+
tasks = parse_task_rows(rows)
|
|
8
|
+
metrics = calculate_project_metrics(tasks, current_day)
|
|
9
|
+
return build_health_report(metrics)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .quality import determine_quality_label
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def build_health_report(metrics):
|
|
5
|
+
lines = []
|
|
6
|
+
for metric in metrics:
|
|
7
|
+
label = determine_quality_label(metric)
|
|
8
|
+
lines.append(
|
|
9
|
+
f"{metric.project_id}: {label} | completion={metric.completion_rate}% | open={metric.open_tasks} | stale={metric.stale_tasks}"
|
|
10
|
+
)
|
|
11
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from task_analytics.fixtures import sample_rows
|
|
4
|
+
from task_analytics.metrics import calculate_project_metrics
|
|
5
|
+
from task_analytics.parser import parse_task_rows
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MetricsTests(unittest.TestCase):
|
|
9
|
+
def test_calculate_project_metrics(self):
|
|
10
|
+
metrics = calculate_project_metrics(parse_task_rows(sample_rows()), 15)
|
|
11
|
+
self.assertEqual(metrics[0].project_id, "alpha")
|
|
12
|
+
self.assertEqual(metrics[0].completion_rate, 50.0)
|
|
13
|
+
self.assertEqual(metrics[0].stale_tasks, 1)
|
|
14
|
+
self.assertEqual(metrics[1].project_id, "beta")
|
|
15
|
+
self.assertEqual(metrics[1].completion_rate, 0.0)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
unittest.main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from task_analytics.parser import parse_task_rows
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ParserTests(unittest.TestCase):
|
|
7
|
+
def test_parse_task_rows(self):
|
|
8
|
+
rows = [{"task_id": "task-1", "project_id": "alpha", "completed": False, "story_points": 3, "updated_day": 2}]
|
|
9
|
+
parsed = parse_task_rows(rows)
|
|
10
|
+
self.assertEqual(parsed[0].task_id, "task-1")
|
|
11
|
+
self.assertFalse(parsed[0].completed)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
unittest.main()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from task_analytics.metrics import calculate_project_metrics
|
|
4
|
+
from task_analytics.parser import parse_task_rows
|
|
5
|
+
from task_analytics.quality import determine_quality_label
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QualityTests(unittest.TestCase):
|
|
9
|
+
def test_determine_quality_label(self):
|
|
10
|
+
rows = [
|
|
11
|
+
{"task_id": "task-1", "project_id": "alpha", "completed": True, "story_points": 5, "updated_day": 8},
|
|
12
|
+
{"task_id": "task-2", "project_id": "alpha", "completed": True, "story_points": 3, "updated_day": 7},
|
|
13
|
+
]
|
|
14
|
+
metrics = calculate_project_metrics(parse_task_rows(rows), 15)
|
|
15
|
+
self.assertEqual(determine_quality_label(metrics[0]), "healthy")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
unittest.main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from task_analytics.fixtures import sample_rows
|
|
4
|
+
from task_analytics.pipeline import build_report_from_rows
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ReportingTests(unittest.TestCase):
|
|
8
|
+
def test_build_report_from_rows(self):
|
|
9
|
+
report = build_report_from_rows(sample_rows(), 15)
|
|
10
|
+
self.assertIn("alpha: watch", report)
|
|
11
|
+
self.assertIn("beta: risk", report)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
unittest.main()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./models/analyticsSnapshot.js";
|
|
2
|
+
export * from "./models/project.js";
|
|
3
|
+
export * from "./models/task.js";
|
|
4
|
+
export * from "./reporting/buildProjectLeaderboard.js";
|
|
5
|
+
export * from "./reporting/formatTaskHealthReport.js";
|
|
6
|
+
export * from "./services/buildAnalyticsSnapshot.js";
|
|
7
|
+
export * from "./services/completeTask.js";
|
|
8
|
+
export * from "./services/createTask.js";
|
|
9
|
+
export * from "./services/listTasksByProject.js";
|
|
10
|
+
export * from "./store/projectStore.js";
|
|
11
|
+
export * from "./store/taskStore.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type ProjectSnapshot = {
|
|
2
|
+
projectId: string;
|
|
3
|
+
totalTasks: number;
|
|
4
|
+
completedTasks: number;
|
|
5
|
+
openTasks: number;
|
|
6
|
+
staleTasks: number;
|
|
7
|
+
averageStoryPoints: number;
|
|
8
|
+
completionRate: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type AnalyticsSnapshot = {
|
|
12
|
+
generatedAt: string;
|
|
13
|
+
projects: ProjectSnapshot[];
|
|
14
|
+
totals: {
|
|
15
|
+
totalTasks: number;
|
|
16
|
+
completedTasks: number;
|
|
17
|
+
openTasks: number;
|
|
18
|
+
staleTasks: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
package/benchmarks/projects/task-analytics-large-mixed/ts/src/reporting/buildProjectLeaderboard.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AnalyticsSnapshot } from "../models/analyticsSnapshot.js";
|
|
2
|
+
|
|
3
|
+
export function buildProjectLeaderboard(snapshot: AnalyticsSnapshot): string[] {
|
|
4
|
+
return [...snapshot.projects]
|
|
5
|
+
.sort((left, right) => right.completionRate - left.completionRate || left.staleTasks - right.staleTasks || left.projectId.localeCompare(right.projectId))
|
|
6
|
+
.map((project, index) => `${index + 1}. ${project.projectId} (${project.completionRate}% complete, ${project.staleTasks} stale)`);
|
|
7
|
+
}
|
package/benchmarks/projects/task-analytics-large-mixed/ts/src/reporting/formatTaskHealthReport.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AnalyticsSnapshot } from "../models/analyticsSnapshot.js";
|
|
2
|
+
import { buildProjectLeaderboard } from "./buildProjectLeaderboard.js";
|
|
3
|
+
|
|
4
|
+
export function formatTaskHealthReport(snapshot: AnalyticsSnapshot): string {
|
|
5
|
+
const leaderBoard = buildProjectLeaderboard(snapshot).join(" | ");
|
|
6
|
+
return [
|
|
7
|
+
`Total tasks: ${snapshot.totals.totalTasks}`,
|
|
8
|
+
`Completed: ${snapshot.totals.completedTasks}`,
|
|
9
|
+
`Open: ${snapshot.totals.openTasks}`,
|
|
10
|
+
`Stale: ${snapshot.totals.staleTasks}`,
|
|
11
|
+
`Projects: ${leaderBoard}`
|
|
12
|
+
].join("\n");
|
|
13
|
+
}
|
package/benchmarks/projects/task-analytics-large-mixed/ts/src/services/buildAnalyticsSnapshot.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AnalyticsSnapshot, ProjectSnapshot } from "../models/analyticsSnapshot.js";
|
|
2
|
+
import { ProjectStore } from "../store/projectStore.js";
|
|
3
|
+
import { AnalyticsTaskStore } from "../store/taskStore.js";
|
|
4
|
+
import { listTasksByProject } from "./listTasksByProject.js";
|
|
5
|
+
|
|
6
|
+
const STALE_DAY_THRESHOLD = 10;
|
|
7
|
+
|
|
8
|
+
export function buildAnalyticsSnapshot(taskStore: AnalyticsTaskStore, projectStore: ProjectStore, currentDay: number): AnalyticsSnapshot {
|
|
9
|
+
const projects = projectStore.list();
|
|
10
|
+
const snapshots: ProjectSnapshot[] = projects.map((project) => {
|
|
11
|
+
const tasks = listTasksByProject(taskStore, project.id);
|
|
12
|
+
const completedTasks = tasks.filter((task) => task.completed).length;
|
|
13
|
+
const staleTasks = tasks.filter((task) => !task.completed && currentDay - task.updatedDay >= STALE_DAY_THRESHOLD).length;
|
|
14
|
+
return {
|
|
15
|
+
projectId: project.id,
|
|
16
|
+
totalTasks: tasks.length,
|
|
17
|
+
completedTasks,
|
|
18
|
+
openTasks: tasks.length - completedTasks,
|
|
19
|
+
staleTasks,
|
|
20
|
+
averageStoryPoints: tasks.length === 0 ? 0 : round(tasks.reduce((sum, task) => sum + task.storyPoints, 0) / tasks.length),
|
|
21
|
+
completionRate: tasks.length === 0 ? 0 : round((completedTasks / tasks.length) * 100)
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
generatedAt: `2026-03-${String(currentDay).padStart(2, "0")}T00:00:00.000Z`,
|
|
27
|
+
projects: snapshots,
|
|
28
|
+
totals: {
|
|
29
|
+
totalTasks: snapshots.reduce((sum, snapshot) => sum + snapshot.totalTasks, 0),
|
|
30
|
+
completedTasks: snapshots.reduce((sum, snapshot) => sum + snapshot.completedTasks, 0),
|
|
31
|
+
openTasks: snapshots.reduce((sum, snapshot) => sum + snapshot.openTasks, 0),
|
|
32
|
+
staleTasks: snapshots.reduce((sum, snapshot) => sum + snapshot.staleTasks, 0)
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function round(value: number): number {
|
|
38
|
+
return Math.round(value * 100) / 100;
|
|
39
|
+
}
|