reflectt-node 0.1.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/LICENSE +178 -0
- package/README.md +188 -0
- package/dist/activationEvents.d.ts +110 -0
- package/dist/activationEvents.d.ts.map +1 -0
- package/dist/activationEvents.js +378 -0
- package/dist/activationEvents.js.map +1 -0
- package/dist/activity-signal.d.ts +30 -0
- package/dist/activity-signal.d.ts.map +1 -0
- package/dist/activity-signal.js +93 -0
- package/dist/activity-signal.js.map +1 -0
- package/dist/alert-integrity.d.ts +100 -0
- package/dist/alert-integrity.d.ts.map +1 -0
- package/dist/alert-integrity.js +333 -0
- package/dist/alert-integrity.js.map +1 -0
- package/dist/alert-preflight.d.ts +40 -0
- package/dist/alert-preflight.d.ts.map +1 -0
- package/dist/alert-preflight.js +235 -0
- package/dist/alert-preflight.js.map +1 -0
- package/dist/analytics.d.ts +131 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +371 -0
- package/dist/analytics.js.map +1 -0
- package/dist/artifact-mirror.d.ts +26 -0
- package/dist/artifact-mirror.d.ts.map +1 -0
- package/dist/artifact-mirror.js +170 -0
- package/dist/artifact-mirror.js.map +1 -0
- package/dist/artifact-resolver.d.ts +48 -0
- package/dist/artifact-resolver.d.ts.map +1 -0
- package/dist/artifact-resolver.js +164 -0
- package/dist/artifact-resolver.js.map +1 -0
- package/dist/assignment.d.ts +116 -0
- package/dist/assignment.d.ts.map +1 -0
- package/dist/assignment.js +475 -0
- package/dist/assignment.js.map +1 -0
- package/dist/auditLedger.d.ts +50 -0
- package/dist/auditLedger.d.ts.map +1 -0
- package/dist/auditLedger.js +136 -0
- package/dist/auditLedger.js.map +1 -0
- package/dist/boardHealthWorker.d.ts +134 -0
- package/dist/boardHealthWorker.d.ts.map +1 -0
- package/dist/boardHealthWorker.js +882 -0
- package/dist/boardHealthWorker.js.map +1 -0
- package/dist/bootstrap-team.d.ts +42 -0
- package/dist/bootstrap-team.d.ts.map +1 -0
- package/dist/bootstrap-team.js +111 -0
- package/dist/bootstrap-team.js.map +1 -0
- package/dist/buildInfo.d.ts +17 -0
- package/dist/buildInfo.d.ts.map +1 -0
- package/dist/buildInfo.js +56 -0
- package/dist/buildInfo.js.map +1 -0
- package/dist/calendar-events.d.ts +133 -0
- package/dist/calendar-events.d.ts.map +1 -0
- package/dist/calendar-events.js +615 -0
- package/dist/calendar-events.js.map +1 -0
- package/dist/calendar-ical.d.ts +41 -0
- package/dist/calendar-ical.d.ts.map +1 -0
- package/dist/calendar-ical.js +413 -0
- package/dist/calendar-ical.js.map +1 -0
- package/dist/calendar-reminder-engine.d.ts +10 -0
- package/dist/calendar-reminder-engine.d.ts.map +1 -0
- package/dist/calendar-reminder-engine.js +143 -0
- package/dist/calendar-reminder-engine.js.map +1 -0
- package/dist/calendar.d.ts +75 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +391 -0
- package/dist/calendar.js.map +1 -0
- package/dist/canvas-multiplexer.d.ts +44 -0
- package/dist/canvas-multiplexer.d.ts.map +1 -0
- package/dist/canvas-multiplexer.js +150 -0
- package/dist/canvas-multiplexer.js.map +1 -0
- package/dist/canvas-slots.d.ts +83 -0
- package/dist/canvas-slots.d.ts.map +1 -0
- package/dist/canvas-slots.js +144 -0
- package/dist/canvas-slots.js.map +1 -0
- package/dist/canvas-types.d.ts +56 -0
- package/dist/canvas-types.d.ts.map +1 -0
- package/dist/canvas-types.js +54 -0
- package/dist/canvas-types.js.map +1 -0
- package/dist/cf-keepalive.d.ts +40 -0
- package/dist/cf-keepalive.d.ts.map +1 -0
- package/dist/cf-keepalive.js +153 -0
- package/dist/cf-keepalive.js.map +1 -0
- package/dist/changeFeed.d.ts +38 -0
- package/dist/changeFeed.d.ts.map +1 -0
- package/dist/changeFeed.js +324 -0
- package/dist/changeFeed.js.map +1 -0
- package/dist/channels.d.ts +28 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +23 -0
- package/dist/channels.js.map +1 -0
- package/dist/chat-approval-detector.d.ts +47 -0
- package/dist/chat-approval-detector.d.ts.map +1 -0
- package/dist/chat-approval-detector.js +224 -0
- package/dist/chat-approval-detector.js.map +1 -0
- package/dist/chat.d.ts +119 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +666 -0
- package/dist/chat.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1142 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud.d.ts +45 -0
- package/dist/cloud.d.ts.map +1 -0
- package/dist/cloud.js +962 -0
- package/dist/cloud.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +33 -0
- package/dist/config.js.map +1 -0
- package/dist/connectivity.d.ts +59 -0
- package/dist/connectivity.d.ts.map +1 -0
- package/dist/connectivity.js +173 -0
- package/dist/connectivity.js.map +1 -0
- package/dist/contacts.d.ts +59 -0
- package/dist/contacts.d.ts.map +1 -0
- package/dist/contacts.js +183 -0
- package/dist/contacts.js.map +1 -0
- package/dist/content.d.ts +130 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +186 -0
- package/dist/content.js.map +1 -0
- package/dist/context-budget.d.ts +87 -0
- package/dist/context-budget.d.ts.map +1 -0
- package/dist/context-budget.js +459 -0
- package/dist/context-budget.js.map +1 -0
- package/dist/continuity-loop.d.ts +55 -0
- package/dist/continuity-loop.d.ts.map +1 -0
- package/dist/continuity-loop.js +267 -0
- package/dist/continuity-loop.js.map +1 -0
- package/dist/dashboard.d.ts +6 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +2348 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/db.d.ts +44 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +648 -0
- package/dist/db.js.map +1 -0
- package/dist/doctor.d.ts +30 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +159 -0
- package/dist/doctor.js.map +1 -0
- package/dist/duplicateClosureGuard.d.ts +31 -0
- package/dist/duplicateClosureGuard.d.ts.map +1 -0
- package/dist/duplicateClosureGuard.js +83 -0
- package/dist/duplicateClosureGuard.js.map +1 -0
- package/dist/embeddings.d.ts +13 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +78 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/escalation.d.ts +80 -0
- package/dist/escalation.d.ts.map +1 -0
- package/dist/escalation.js +213 -0
- package/dist/escalation.js.map +1 -0
- package/dist/events.d.ts +130 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +382 -0
- package/dist/events.js.map +1 -0
- package/dist/executionSweeper.d.ts +97 -0
- package/dist/executionSweeper.d.ts.map +1 -0
- package/dist/executionSweeper.js +875 -0
- package/dist/executionSweeper.js.map +1 -0
- package/dist/experiments.d.ts +47 -0
- package/dist/experiments.d.ts.map +1 -0
- package/dist/experiments.js +133 -0
- package/dist/experiments.js.map +1 -0
- package/dist/feedback.d.ts +179 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +397 -0
- package/dist/feedback.js.map +1 -0
- package/dist/files.d.ts +52 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +172 -0
- package/dist/files.js.map +1 -0
- package/dist/format-duration.d.ts +19 -0
- package/dist/format-duration.d.ts.map +1 -0
- package/dist/format-duration.js +33 -0
- package/dist/format-duration.js.map +1 -0
- package/dist/github-actor-auth.d.ts +20 -0
- package/dist/github-actor-auth.d.ts.map +1 -0
- package/dist/github-actor-auth.js +54 -0
- package/dist/github-actor-auth.js.map +1 -0
- package/dist/github-ci.d.ts +16 -0
- package/dist/github-ci.d.ts.map +1 -0
- package/dist/github-ci.js +37 -0
- package/dist/github-ci.js.map +1 -0
- package/dist/github-identity.d.ts +30 -0
- package/dist/github-identity.d.ts.map +1 -0
- package/dist/github-identity.js +96 -0
- package/dist/github-identity.js.map +1 -0
- package/dist/github-reviews.d.ts +24 -0
- package/dist/github-reviews.d.ts.map +1 -0
- package/dist/github-reviews.js +56 -0
- package/dist/github-reviews.js.map +1 -0
- package/dist/health.d.ts +391 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +1841 -0
- package/dist/health.js.map +1 -0
- package/dist/host-keepalive.d.ts +22 -0
- package/dist/host-keepalive.d.ts.map +1 -0
- package/dist/host-keepalive.js +126 -0
- package/dist/host-keepalive.js.map +1 -0
- package/dist/host-registry.d.ts +43 -0
- package/dist/host-registry.d.ts.map +1 -0
- package/dist/host-registry.js +93 -0
- package/dist/host-registry.js.map +1 -0
- package/dist/inbox.d.ts +87 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +410 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +306 -0
- package/dist/index.js.map +1 -0
- package/dist/insight-mutation.d.ts +32 -0
- package/dist/insight-mutation.d.ts.map +1 -0
- package/dist/insight-mutation.js +160 -0
- package/dist/insight-mutation.js.map +1 -0
- package/dist/insight-promotion.d.ts +89 -0
- package/dist/insight-promotion.d.ts.map +1 -0
- package/dist/insight-promotion.js +278 -0
- package/dist/insight-promotion.js.map +1 -0
- package/dist/insight-task-bridge.d.ts +77 -0
- package/dist/insight-task-bridge.d.ts.map +1 -0
- package/dist/insight-task-bridge.js +556 -0
- package/dist/insight-task-bridge.js.map +1 -0
- package/dist/insights.d.ts +222 -0
- package/dist/insights.d.ts.map +1 -0
- package/dist/insights.js +871 -0
- package/dist/insights.js.map +1 -0
- package/dist/intake-pipeline.d.ts +74 -0
- package/dist/intake-pipeline.d.ts.map +1 -0
- package/dist/intake-pipeline.js +199 -0
- package/dist/intake-pipeline.js.map +1 -0
- package/dist/intensity.d.ts +31 -0
- package/dist/intensity.d.ts.map +1 -0
- package/dist/intensity.js +94 -0
- package/dist/intensity.js.map +1 -0
- package/dist/knowledge-auto-index.d.ts +37 -0
- package/dist/knowledge-auto-index.d.ts.map +1 -0
- package/dist/knowledge-auto-index.js +149 -0
- package/dist/knowledge-auto-index.js.map +1 -0
- package/dist/knowledge-docs.d.ts +45 -0
- package/dist/knowledge-docs.d.ts.map +1 -0
- package/dist/knowledge-docs.js +188 -0
- package/dist/knowledge-docs.js.map +1 -0
- package/dist/lane-config.d.ts +25 -0
- package/dist/lane-config.d.ts.map +1 -0
- package/dist/lane-config.js +105 -0
- package/dist/lane-config.js.map +1 -0
- package/dist/lineage.d.ts +86 -0
- package/dist/lineage.d.ts.map +1 -0
- package/dist/lineage.js +303 -0
- package/dist/lineage.js.map +1 -0
- package/dist/logStore.d.ts +25 -0
- package/dist/logStore.d.ts.map +1 -0
- package/dist/logStore.js +83 -0
- package/dist/logStore.js.map +1 -0
- package/dist/manage.d.ts +12 -0
- package/dist/manage.d.ts.map +1 -0
- package/dist/manage.js +253 -0
- package/dist/manage.js.map +1 -0
- package/dist/mcp.d.ts +5 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +604 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory.d.ts +47 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +149 -0
- package/dist/memory.js.map +1 -0
- package/dist/mention-ack.d.ts +80 -0
- package/dist/mention-ack.d.ts.map +1 -0
- package/dist/mention-ack.js +175 -0
- package/dist/mention-ack.js.map +1 -0
- package/dist/messageRouter.d.ts +60 -0
- package/dist/messageRouter.d.ts.map +1 -0
- package/dist/messageRouter.js +309 -0
- package/dist/messageRouter.js.map +1 -0
- package/dist/mutationAlert.d.ts +44 -0
- package/dist/mutationAlert.d.ts.map +1 -0
- package/dist/mutationAlert.js +174 -0
- package/dist/mutationAlert.js.map +1 -0
- package/dist/noise-budget.d.ts +136 -0
- package/dist/noise-budget.d.ts.map +1 -0
- package/dist/noise-budget.js +340 -0
- package/dist/noise-budget.js.map +1 -0
- package/dist/notifications.d.ts +67 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +253 -0
- package/dist/notifications.js.map +1 -0
- package/dist/openclaw.d.ts +34 -0
- package/dist/openclaw.d.ts.map +1 -0
- package/dist/openclaw.js +208 -0
- package/dist/openclaw.js.map +1 -0
- package/dist/pause-controls.d.ts +31 -0
- package/dist/pause-controls.d.ts.map +1 -0
- package/dist/pause-controls.js +130 -0
- package/dist/pause-controls.js.map +1 -0
- package/dist/pidlock.d.ts +25 -0
- package/dist/pidlock.d.ts.map +1 -0
- package/dist/pidlock.js +179 -0
- package/dist/pidlock.js.map +1 -0
- package/dist/policy.d.ts +139 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +264 -0
- package/dist/policy.js.map +1 -0
- package/dist/polls.d.ts +47 -0
- package/dist/polls.d.ts.map +1 -0
- package/dist/polls.js +162 -0
- package/dist/polls.js.map +1 -0
- package/dist/portability.d.ts +55 -0
- package/dist/portability.d.ts.map +1 -0
- package/dist/portability.js +292 -0
- package/dist/portability.js.map +1 -0
- package/dist/pr-integrity.d.ts +45 -0
- package/dist/pr-integrity.d.ts.map +1 -0
- package/dist/pr-integrity.js +124 -0
- package/dist/pr-integrity.js.map +1 -0
- package/dist/prAutoMerge.d.ts +62 -0
- package/dist/prAutoMerge.d.ts.map +1 -0
- package/dist/prAutoMerge.js +493 -0
- package/dist/prAutoMerge.js.map +1 -0
- package/dist/preflight.d.ts +66 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +864 -0
- package/dist/preflight.js.map +1 -0
- package/dist/presence.d.ts +98 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +347 -0
- package/dist/presence.js.map +1 -0
- package/dist/provisioning.d.ts +101 -0
- package/dist/provisioning.d.ts.map +1 -0
- package/dist/provisioning.js +430 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/reflection-automation.d.ts +59 -0
- package/dist/reflection-automation.d.ts.map +1 -0
- package/dist/reflection-automation.js +350 -0
- package/dist/reflection-automation.js.map +1 -0
- package/dist/reflections.d.ts +65 -0
- package/dist/reflections.d.ts.map +1 -0
- package/dist/reflections.js +306 -0
- package/dist/reflections.js.map +1 -0
- package/dist/release.d.ts +67 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +275 -0
- package/dist/release.js.map +1 -0
- package/dist/request-tracker.d.ts +36 -0
- package/dist/request-tracker.d.ts.map +1 -0
- package/dist/request-tracker.js +109 -0
- package/dist/request-tracker.js.map +1 -0
- package/dist/research.d.ts +75 -0
- package/dist/research.d.ts.map +1 -0
- package/dist/research.js +171 -0
- package/dist/research.js.map +1 -0
- package/dist/routing-approvals.d.ts +73 -0
- package/dist/routing-approvals.d.ts.map +1 -0
- package/dist/routing-approvals.js +88 -0
- package/dist/routing-approvals.js.map +1 -0
- package/dist/routing-override.d.ts +94 -0
- package/dist/routing-override.d.ts.map +1 -0
- package/dist/routing-override.js +290 -0
- package/dist/routing-override.js.map +1 -0
- package/dist/scope-routing.d.ts +18 -0
- package/dist/scope-routing.d.ts.map +1 -0
- package/dist/scope-routing.js +29 -0
- package/dist/scope-routing.js.map +1 -0
- package/dist/secrets.d.ts +77 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +287 -0
- package/dist/secrets.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +10887 -0
- package/dist/server.js.map +1 -0
- package/dist/service-probe.d.ts +53 -0
- package/dist/service-probe.d.ts.map +1 -0
- package/dist/service-probe.js +225 -0
- package/dist/service-probe.js.map +1 -0
- package/dist/shared-workspace-api.d.ts +73 -0
- package/dist/shared-workspace-api.d.ts.map +1 -0
- package/dist/shared-workspace-api.js +281 -0
- package/dist/shared-workspace-api.js.map +1 -0
- package/dist/shipped-heartbeat.d.ts +91 -0
- package/dist/shipped-heartbeat.d.ts.map +1 -0
- package/dist/shipped-heartbeat.js +272 -0
- package/dist/shipped-heartbeat.js.map +1 -0
- package/dist/starter-team.d.ts +23 -0
- package/dist/starter-team.d.ts.map +1 -0
- package/dist/starter-team.js +88 -0
- package/dist/starter-team.js.map +1 -0
- package/dist/suppression-ledger.d.ts +73 -0
- package/dist/suppression-ledger.d.ts.map +1 -0
- package/dist/suppression-ledger.js +125 -0
- package/dist/suppression-ledger.js.map +1 -0
- package/dist/system-loop-state.d.ts +4 -0
- package/dist/system-loop-state.d.ts.map +1 -0
- package/dist/system-loop-state.js +40 -0
- package/dist/system-loop-state.js.map +1 -0
- package/dist/taskCommentIngest.d.ts +43 -0
- package/dist/taskCommentIngest.d.ts.map +1 -0
- package/dist/taskCommentIngest.js +59 -0
- package/dist/taskCommentIngest.js.map +1 -0
- package/dist/taskPrecheck.d.ts +20 -0
- package/dist/taskPrecheck.d.ts.map +1 -0
- package/dist/taskPrecheck.js +329 -0
- package/dist/taskPrecheck.js.map +1 -0
- package/dist/taskStateSync.d.ts +8 -0
- package/dist/taskStateSync.d.ts.map +1 -0
- package/dist/taskStateSync.js +79 -0
- package/dist/taskStateSync.js.map +1 -0
- package/dist/tasks.d.ts +140 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +1281 -0
- package/dist/tasks.js.map +1 -0
- package/dist/team-config.d.ts +24 -0
- package/dist/team-config.d.ts.map +1 -0
- package/dist/team-config.js +221 -0
- package/dist/team-config.js.map +1 -0
- package/dist/team-doctor.d.ts +22 -0
- package/dist/team-doctor.d.ts.map +1 -0
- package/dist/team-doctor.js +270 -0
- package/dist/team-doctor.js.map +1 -0
- package/dist/team-pulse.d.ts +52 -0
- package/dist/team-pulse.d.ts.map +1 -0
- package/dist/team-pulse.js +176 -0
- package/dist/team-pulse.js.map +1 -0
- package/dist/telemetry.d.ts +74 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +256 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/test-task-filter.d.ts +21 -0
- package/dist/test-task-filter.d.ts.map +1 -0
- package/dist/test-task-filter.js +48 -0
- package/dist/test-task-filter.js.map +1 -0
- package/dist/types.d.ts +126 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-tracking.d.ts +101 -0
- package/dist/usage-tracking.d.ts.map +1 -0
- package/dist/usage-tracking.js +325 -0
- package/dist/usage-tracking.js.map +1 -0
- package/dist/vector-store.d.ts +87 -0
- package/dist/vector-store.d.ts.map +1 -0
- package/dist/vector-store.js +247 -0
- package/dist/vector-store.js.map +1 -0
- package/dist/watchdog/idleNudgeLane.d.ts +22 -0
- package/dist/watchdog/idleNudgeLane.d.ts.map +1 -0
- package/dist/watchdog/idleNudgeLane.js +98 -0
- package/dist/watchdog/idleNudgeLane.js.map +1 -0
- package/dist/webhooks.d.ts +103 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +398 -0
- package/dist/webhooks.js.map +1 -0
- package/dist/working-contract.d.ts +42 -0
- package/dist/working-contract.d.ts.map +1 -0
- package/dist/working-contract.js +228 -0
- package/dist/working-contract.js.map +1 -0
- package/dist/ws-heartbeat.d.ts +66 -0
- package/dist/ws-heartbeat.d.ts.map +1 -0
- package/dist/ws-heartbeat.js +174 -0
- package/dist/ws-heartbeat.js.map +1 -0
- package/package.json +87 -0
- package/plugins/reflectt-channel/README.md +96 -0
- package/plugins/reflectt-channel/index.ts +789 -0
- package/plugins/reflectt-channel/openclaw.plugin.json +23 -0
- package/plugins/reflectt-channel/package.json +23 -0
- package/plugins/reflectt-channel/src/channel.ts +433 -0
- package/plugins/reflectt-channel/src/types.ts +29 -0
- package/public/avatars/echo.png +0 -0
- package/public/avatars/harmony.png +0 -0
- package/public/avatars/kai.png +0 -0
- package/public/avatars/link.png +0 -0
- package/public/avatars/pixel.png +0 -0
- package/public/avatars/rhythm.png +0 -0
- package/public/avatars/ryan.png +0 -0
- package/public/avatars/sage.png +0 -0
- package/public/avatars/scout.png +0 -0
- package/public/avatars/spark.png +0 -0
- package/public/dashboard-animations.css +381 -0
- package/public/dashboard.js +3479 -0
- package/public/docs.md +1062 -0
- package/public/file-upload-mock.html +1097 -0
- package/public/og-card.png +0 -0
- package/public/ui-kit.html +318 -0
- package/public/widget/feedback.js +194 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Persistent suppression ledger for system message deduplication
|
|
3
|
+
//
|
|
4
|
+
// Tracks dedup keys (category + channel + content hash) in SQLite.
|
|
5
|
+
// Prevents duplicate system messages within a configurable window (default 30m).
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
import { getDb } from './db.js';
|
|
8
|
+
const DEFAULT_WINDOW_MS = 30 * 60 * 1000; // 30 minutes
|
|
9
|
+
export class SuppressionLedger {
|
|
10
|
+
windowMs;
|
|
11
|
+
constructor(windowMs) {
|
|
12
|
+
this.windowMs = windowMs ?? DEFAULT_WINDOW_MS;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Compute a dedup key from category + channel + content.
|
|
16
|
+
* Content is normalized: timestamps, task IDs, and message IDs stripped.
|
|
17
|
+
*/
|
|
18
|
+
computeDedupKey(category, channel, content) {
|
|
19
|
+
const normalized = content
|
|
20
|
+
.trim()
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/\b(msg-|task-|tcomment-|ins-|ref-)\S+/g, '')
|
|
23
|
+
.replace(/\d{13,}/g, '')
|
|
24
|
+
.replace(/\s+/g, ' ')
|
|
25
|
+
.slice(0, 300);
|
|
26
|
+
const raw = `${category}:${channel}:${normalized}`;
|
|
27
|
+
return createHash('sha256').update(raw).digest('hex').substring(0, 20);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if a message is a duplicate within the suppression window.
|
|
31
|
+
* If not a duplicate, records it in the ledger.
|
|
32
|
+
* If duplicate, increments the hit count and marks as suppressed.
|
|
33
|
+
*/
|
|
34
|
+
check(opts) {
|
|
35
|
+
const dedup_key = this.computeDedupKey(opts.category, opts.channel, opts.content);
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const db = getDb();
|
|
38
|
+
const existing = db.prepare('SELECT * FROM suppression_ledger WHERE dedup_key = ?').get(dedup_key);
|
|
39
|
+
if (existing && (now - existing.last_seen_at) < this.windowMs) {
|
|
40
|
+
// Duplicate within window — update hit count
|
|
41
|
+
db.prepare('UPDATE suppression_ledger SET hit_count = hit_count + 1, last_seen_at = ?, suppressed = 1 WHERE dedup_key = ?').run(now, dedup_key);
|
|
42
|
+
return {
|
|
43
|
+
isDuplicate: true,
|
|
44
|
+
dedup_key,
|
|
45
|
+
existing: {
|
|
46
|
+
...existing,
|
|
47
|
+
suppressed: true,
|
|
48
|
+
hit_count: existing.hit_count + 1,
|
|
49
|
+
last_seen_at: now,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Not a duplicate (or outside window) — upsert
|
|
54
|
+
db.prepare(`
|
|
55
|
+
INSERT INTO suppression_ledger (dedup_key, category, channel, "from", content_preview, first_seen_at, last_seen_at, hit_count, suppressed, window_ms)
|
|
56
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 1, 0, ?)
|
|
57
|
+
ON CONFLICT(dedup_key) DO UPDATE SET
|
|
58
|
+
last_seen_at = excluded.last_seen_at,
|
|
59
|
+
hit_count = 1,
|
|
60
|
+
suppressed = 0,
|
|
61
|
+
"from" = excluded."from",
|
|
62
|
+
content_preview = excluded.content_preview,
|
|
63
|
+
window_ms = excluded.window_ms
|
|
64
|
+
`).run(dedup_key, opts.category, opts.channel, opts.from, opts.content.slice(0, 200), now, now, this.windowMs);
|
|
65
|
+
return { isDuplicate: false, dedup_key };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get suppression statistics.
|
|
69
|
+
*/
|
|
70
|
+
getStats() {
|
|
71
|
+
const db = getDb();
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
const activeCutoff = now - this.windowMs;
|
|
74
|
+
const total = db.prepare('SELECT COUNT(*) as c FROM suppression_ledger').get().c;
|
|
75
|
+
const totalSuppressed = db.prepare('SELECT COUNT(*) as c FROM suppression_ledger WHERE suppressed = 1').get().c;
|
|
76
|
+
const totalHitsRow = db.prepare('SELECT COALESCE(SUM(hit_count), 0) as s FROM suppression_ledger').get();
|
|
77
|
+
const activeEntries = db.prepare('SELECT COUNT(*) as c FROM suppression_ledger WHERE last_seen_at >= ?').get(activeCutoff).c;
|
|
78
|
+
const byCatRows = db.prepare(`
|
|
79
|
+
SELECT category,
|
|
80
|
+
COUNT(*) as entries,
|
|
81
|
+
SUM(CASE WHEN suppressed = 1 THEN 1 ELSE 0 END) as suppressed,
|
|
82
|
+
SUM(hit_count) as hits
|
|
83
|
+
FROM suppression_ledger GROUP BY category
|
|
84
|
+
`).all();
|
|
85
|
+
const byChannelRows = db.prepare(`
|
|
86
|
+
SELECT channel,
|
|
87
|
+
COUNT(*) as entries,
|
|
88
|
+
SUM(CASE WHEN suppressed = 1 THEN 1 ELSE 0 END) as suppressed,
|
|
89
|
+
SUM(hit_count) as hits
|
|
90
|
+
FROM suppression_ledger GROUP BY channel
|
|
91
|
+
`).all();
|
|
92
|
+
return {
|
|
93
|
+
total_entries: total,
|
|
94
|
+
total_suppressed: totalSuppressed,
|
|
95
|
+
total_hits: totalHitsRow.s,
|
|
96
|
+
by_category: Object.fromEntries(byCatRows.map(r => [r.category, { entries: r.entries, suppressed: r.suppressed, hits: r.hits }])),
|
|
97
|
+
by_channel: Object.fromEntries(byChannelRows.map(r => [r.channel, { entries: r.entries, suppressed: r.suppressed, hits: r.hits }])),
|
|
98
|
+
window_ms: this.windowMs,
|
|
99
|
+
active_entries: activeEntries,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Prune old entries outside the window.
|
|
104
|
+
*/
|
|
105
|
+
prune() {
|
|
106
|
+
const db = getDb();
|
|
107
|
+
const cutoff = Date.now() - this.windowMs * 10; // Keep 10x window for stats
|
|
108
|
+
const result = db.prepare('DELETE FROM suppression_ledger WHERE last_seen_at < ?').run(cutoff);
|
|
109
|
+
return result.changes;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get window in ms.
|
|
113
|
+
*/
|
|
114
|
+
getWindowMs() {
|
|
115
|
+
return this.windowMs;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Update window.
|
|
119
|
+
*/
|
|
120
|
+
setWindowMs(ms) {
|
|
121
|
+
this.windowMs = ms;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export const suppressionLedger = new SuppressionLedger();
|
|
125
|
+
//# sourceMappingURL=suppression-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppression-ledger.js","sourceRoot":"","sources":["../src/suppression-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type SystemLoopName = 'idle_nudge' | 'cadence_watchdog' | 'mention_rescue' | 'reflection_pipeline' | 'board_health';
|
|
2
|
+
export declare function recordSystemLoopTick(name: SystemLoopName, now?: number): void;
|
|
3
|
+
export declare function getSystemLoopTicks(): Record<SystemLoopName, number>;
|
|
4
|
+
//# sourceMappingURL=system-loop-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-loop-state.d.ts","sourceRoot":"","sources":["../src/system-loop-state.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,kBAAkB,GAClB,gBAAgB,GAChB,qBAAqB,GACrB,cAAc,CAAA;AAElB,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,SAAa,GAAG,IAAI,CAWjF;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAqBnE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Persisted loop tick timestamps (SQLite) so /health/system can prove that
|
|
4
|
+
* timers/watchdogs are actually firing (and not just configured).
|
|
5
|
+
*/
|
|
6
|
+
import { getDb } from './db.js';
|
|
7
|
+
export function recordSystemLoopTick(name, now = Date.now()) {
|
|
8
|
+
const db = getDb();
|
|
9
|
+
try {
|
|
10
|
+
db.prepare(`INSERT INTO system_loop_ticks (name, last_tick_at)
|
|
11
|
+
VALUES (?, ?)
|
|
12
|
+
ON CONFLICT(name) DO UPDATE SET last_tick_at = excluded.last_tick_at`).run(name, now);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Best-effort: DB may be unavailable in some unit test contexts.
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function getSystemLoopTicks() {
|
|
19
|
+
const db = getDb();
|
|
20
|
+
const out = {
|
|
21
|
+
idle_nudge: 0,
|
|
22
|
+
cadence_watchdog: 0,
|
|
23
|
+
mention_rescue: 0,
|
|
24
|
+
reflection_pipeline: 0,
|
|
25
|
+
board_health: 0,
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
const rows = db.prepare('SELECT name, last_tick_at FROM system_loop_ticks').all();
|
|
29
|
+
for (const r of rows) {
|
|
30
|
+
const name = String(r.name);
|
|
31
|
+
if (name in out)
|
|
32
|
+
out[name] = Number(r.last_tick_at || 0);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// ignore
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=system-loop-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system-loop-state.js","sourceRoot":"","sources":["../src/system-loop-state.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAS/B,MAAM,UAAU,oBAAoB,CAAC,IAAoB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IACzE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,IAAI,CAAC;QACH,EAAE,CAAC,OAAO,CACR;;4EAEsE,CACvE,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,MAAM,GAAG,GAAmC;QAC1C,UAAU,EAAE,CAAC;QACb,gBAAgB,EAAE,CAAC;QACnB,cAAc,EAAE,CAAC;QACjB,mBAAmB,EAAE,CAAC;QACtB,YAAY,EAAE,CAAC;KAChB,CAAA;IAED,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,kDAAkD,CAAC,CAAC,GAAG,EAAmD,CAAA;QAClI,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAmB,CAAA;YAC7C,IAAI,IAAI,IAAI,GAAG;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type TaskCommentProvenance = {
|
|
2
|
+
source_channel?: string;
|
|
3
|
+
sender_id?: string;
|
|
4
|
+
original_message_id?: string;
|
|
5
|
+
integration?: string;
|
|
6
|
+
[k: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
export type TaskCommentReject = {
|
|
9
|
+
attempted_task_param: string;
|
|
10
|
+
resolved_task_id?: string | null;
|
|
11
|
+
author?: string | null;
|
|
12
|
+
content?: string | null;
|
|
13
|
+
reason: 'task_not_found' | 'invalid_task_refs' | 'other';
|
|
14
|
+
details?: Record<string, unknown>;
|
|
15
|
+
provenance?: TaskCommentProvenance;
|
|
16
|
+
timestamp?: number;
|
|
17
|
+
};
|
|
18
|
+
export declare function ensureTaskCommentRejectTable(): void;
|
|
19
|
+
export declare function recordTaskCommentReject(input: TaskCommentReject): {
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
export interface TaskCommentRejectRow {
|
|
23
|
+
id: string;
|
|
24
|
+
attempted_task_param: string;
|
|
25
|
+
resolved_task_id: string | null;
|
|
26
|
+
author: string | null;
|
|
27
|
+
content: string | null;
|
|
28
|
+
reason: string;
|
|
29
|
+
details: string | null;
|
|
30
|
+
provenance: string | null;
|
|
31
|
+
timestamp: number;
|
|
32
|
+
}
|
|
33
|
+
/** Query the reject ledger with optional filters. */
|
|
34
|
+
export declare function listTaskCommentRejects(opts?: {
|
|
35
|
+
limit?: number;
|
|
36
|
+
reason?: string;
|
|
37
|
+
author?: string;
|
|
38
|
+
since?: number;
|
|
39
|
+
}): {
|
|
40
|
+
rejects: TaskCommentRejectRow[];
|
|
41
|
+
total: number;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=taskCommentIngest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskCommentIngest.d.ts","sourceRoot":"","sources":["../src/taskCommentIngest.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,qBAAqB,GAAG;IAClC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IAEpB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,gBAAgB,GAAG,mBAAmB,GAAG,OAAO,CAAA;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,UAAU,CAAC,EAAE,qBAAqB,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,4BAA4B,IAAI,IAAI,CAkBnD;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAuBhF;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,qDAAqD;AACrD,wBAAgB,sBAAsB,CAAC,IAAI,CAAC,EAAE;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG;IAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA6BrD"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Task-comment ingestion observability + reject ledger
|
|
3
|
+
import { getDb, safeJsonStringify } from './db.js';
|
|
4
|
+
export function ensureTaskCommentRejectTable() {
|
|
5
|
+
const db = getDb();
|
|
6
|
+
db.exec(`
|
|
7
|
+
CREATE TABLE IF NOT EXISTS task_comment_ingest_rejects (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
attempted_task_param TEXT NOT NULL,
|
|
10
|
+
resolved_task_id TEXT,
|
|
11
|
+
author TEXT,
|
|
12
|
+
content TEXT,
|
|
13
|
+
reason TEXT NOT NULL,
|
|
14
|
+
details TEXT,
|
|
15
|
+
provenance TEXT,
|
|
16
|
+
timestamp INTEGER NOT NULL
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_task_comment_rejects_ts ON task_comment_ingest_rejects(timestamp);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_task_comment_rejects_reason ON task_comment_ingest_rejects(reason);
|
|
20
|
+
CREATE INDEX IF NOT EXISTS idx_task_comment_rejects_author ON task_comment_ingest_rejects(author);
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
export function recordTaskCommentReject(input) {
|
|
24
|
+
ensureTaskCommentRejectTable();
|
|
25
|
+
const db = getDb();
|
|
26
|
+
const now = input.timestamp ?? Date.now();
|
|
27
|
+
const id = `tcrej-${now}-${Math.random().toString(36).slice(2, 10)}`;
|
|
28
|
+
db.prepare(`
|
|
29
|
+
INSERT INTO task_comment_ingest_rejects (
|
|
30
|
+
id, attempted_task_param, resolved_task_id, author, content, reason, details, provenance, timestamp
|
|
31
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
32
|
+
`).run(id, input.attempted_task_param, input.resolved_task_id ?? null, input.author ?? null, input.content ?? null, input.reason, safeJsonStringify(input.details ?? null), safeJsonStringify(input.provenance ?? null), now);
|
|
33
|
+
return { id };
|
|
34
|
+
}
|
|
35
|
+
/** Query the reject ledger with optional filters. */
|
|
36
|
+
export function listTaskCommentRejects(opts) {
|
|
37
|
+
ensureTaskCommentRejectTable();
|
|
38
|
+
const db = getDb();
|
|
39
|
+
const limit = Math.min(opts?.limit ?? 50, 200);
|
|
40
|
+
const conditions = [];
|
|
41
|
+
const params = [];
|
|
42
|
+
if (opts?.reason) {
|
|
43
|
+
conditions.push('reason = ?');
|
|
44
|
+
params.push(opts.reason);
|
|
45
|
+
}
|
|
46
|
+
if (opts?.author) {
|
|
47
|
+
conditions.push('author = ?');
|
|
48
|
+
params.push(opts.author);
|
|
49
|
+
}
|
|
50
|
+
if (opts?.since) {
|
|
51
|
+
conditions.push('timestamp >= ?');
|
|
52
|
+
params.push(opts.since);
|
|
53
|
+
}
|
|
54
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
55
|
+
const total = db.prepare(`SELECT COUNT(*) as cnt FROM task_comment_ingest_rejects ${where}`).get(...params).cnt;
|
|
56
|
+
const rows = db.prepare(`SELECT * FROM task_comment_ingest_rejects ${where} ORDER BY timestamp DESC LIMIT ?`).all(...params, limit);
|
|
57
|
+
return { rejects: rows, total };
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=taskCommentIngest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskCommentIngest.js","sourceRoot":"","sources":["../src/taskCommentIngest.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,uDAAuD;AAEvD,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAsBlD,MAAM,UAAU,4BAA4B;IAC1C,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;GAeP,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAwB;IAC9D,4BAA4B,EAAE,CAAA;IAC9B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAA;IACzC,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAA;IAEpE,EAAE,CAAC,OAAO,CAAC;;;;GAIV,CAAC,CAAC,GAAG,CACJ,EAAE,EACF,KAAK,CAAC,oBAAoB,EAC1B,KAAK,CAAC,gBAAgB,IAAI,IAAI,EAC9B,KAAK,CAAC,MAAM,IAAI,IAAI,EACpB,KAAK,CAAC,OAAO,IAAI,IAAI,EACrB,KAAK,CAAC,MAAM,EACZ,iBAAiB,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,EACxC,iBAAiB,CAAC,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,EAC3C,GAAG,CACJ,CAAA;IAED,OAAO,EAAE,EAAE,EAAE,CAAA;AACf,CAAC;AAcD,qDAAqD;AACrD,MAAM,UAAU,sBAAsB,CAAC,IAKtC;IACC,4BAA4B,EAAE,CAAA;IAC9B,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE,EAAE,GAAG,CAAC,CAAA;IAC9C,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,MAAM,MAAM,GAAc,EAAE,CAAA;IAE5B,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC1B,CAAC;IACD,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC1B,CAAC;IACD,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;QAChB,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;QACjC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAE9E,MAAM,KAAK,GAAI,EAAE,CAAC,OAAO,CAAC,2DAA2D,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAqB,CAAC,GAAG,CAAA;IAEpI,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CACrB,6CAA6C,KAAK,kCAAkC,CACrF,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,KAAK,CAA2B,CAAA;IAEjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;AACjC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type PrecheckSeverity = 'error' | 'warning' | 'info';
|
|
2
|
+
export interface PrecheckItem {
|
|
3
|
+
field: string;
|
|
4
|
+
severity: PrecheckSeverity;
|
|
5
|
+
message: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
autoDefault?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface PrecheckResult {
|
|
10
|
+
taskId: string;
|
|
11
|
+
currentStatus: string;
|
|
12
|
+
targetStatus: string;
|
|
13
|
+
ready: boolean;
|
|
14
|
+
items: PrecheckItem[];
|
|
15
|
+
autoDefaults: Record<string, unknown>;
|
|
16
|
+
template: Record<string, unknown> | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function runPrecheck(taskId: string, targetStatus: string): PrecheckResult;
|
|
19
|
+
export declare function applyAutoDefaults(taskId: string, targetStatus: string, metadata: Record<string, unknown>): Record<string, unknown>;
|
|
20
|
+
//# sourceMappingURL=taskPrecheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taskPrecheck.d.ts","sourceRoot":"","sources":["../src/taskPrecheck.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;AAE3D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,YAAY,EAAE,CAAA;IACrB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACzC;AAcD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,cAAc,CAiRhF;AAID,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) Reflectt AI
|
|
3
|
+
/**
|
|
4
|
+
* Task Transition Precheck
|
|
5
|
+
*
|
|
6
|
+
* Surfaces required fields BEFORE a status transition attempt,
|
|
7
|
+
* so agents don't get rejected at gate time.
|
|
8
|
+
*
|
|
9
|
+
* Also provides auto-defaults for common fields (ETA, artifact path).
|
|
10
|
+
*/
|
|
11
|
+
import { taskManager } from './tasks.js';
|
|
12
|
+
import { policyManager } from './policy.js';
|
|
13
|
+
// ── Default ETA by priority ────────────────────────────────────────────────
|
|
14
|
+
const DEFAULT_ETA_BY_PRIORITY = {
|
|
15
|
+
P0: '~30m',
|
|
16
|
+
P1: '~2h',
|
|
17
|
+
P2: '~4h',
|
|
18
|
+
P3: '~1d',
|
|
19
|
+
P4: '~3d',
|
|
20
|
+
};
|
|
21
|
+
// ── Precheck ───────────────────────────────────────────────────────────────
|
|
22
|
+
export function runPrecheck(taskId, targetStatus) {
|
|
23
|
+
const task = taskManager.getTask(taskId);
|
|
24
|
+
if (!task) {
|
|
25
|
+
return {
|
|
26
|
+
taskId,
|
|
27
|
+
currentStatus: 'unknown',
|
|
28
|
+
targetStatus,
|
|
29
|
+
ready: false,
|
|
30
|
+
items: [{ field: 'task', severity: 'error', message: `Task ${taskId} not found` }],
|
|
31
|
+
autoDefaults: {},
|
|
32
|
+
template: null,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const items = [];
|
|
36
|
+
const autoDefaults = {};
|
|
37
|
+
const meta = task.metadata || {};
|
|
38
|
+
// ── Common gates (all non-todo statuses) ────────────────────────────
|
|
39
|
+
if (targetStatus !== 'todo') {
|
|
40
|
+
if (!task.done_criteria || task.done_criteria.length === 0) {
|
|
41
|
+
items.push({
|
|
42
|
+
field: 'done_criteria',
|
|
43
|
+
severity: 'error',
|
|
44
|
+
message: 'done_criteria required (at least 1 item)',
|
|
45
|
+
hint: 'Add specific, verifiable completion criteria.',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (!task.reviewer?.trim()) {
|
|
49
|
+
items.push({
|
|
50
|
+
field: 'reviewer',
|
|
51
|
+
severity: 'error',
|
|
52
|
+
message: 'reviewer required before starting work',
|
|
53
|
+
hint: 'Assign a reviewer who can validate the work.',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Warn-only: lane + surface metadata (routing + ownership discipline)
|
|
57
|
+
// Do not hard-block yet — just surface what to add.
|
|
58
|
+
if (!String(meta.lane || '').trim()) {
|
|
59
|
+
items.push({
|
|
60
|
+
field: 'metadata.lane',
|
|
61
|
+
severity: 'warning',
|
|
62
|
+
message: 'metadata.lane missing (recommended for routing + lane ownership)',
|
|
63
|
+
hint: 'Set metadata.lane to one of: design | product | infra | ops | growth',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (!String(meta.surface || '').trim()) {
|
|
67
|
+
items.push({
|
|
68
|
+
field: 'metadata.surface',
|
|
69
|
+
severity: 'warning',
|
|
70
|
+
message: 'metadata.surface missing (recommended for routing + priority triage)',
|
|
71
|
+
hint: 'Set metadata.surface to one of: reflectt-node | reflectt-cloud-app | reflectt.ai | infra',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// ── doing ───────────────────────────────────────────────────────────
|
|
76
|
+
if (targetStatus === 'doing') {
|
|
77
|
+
const eta = meta.eta;
|
|
78
|
+
if (!eta?.trim()) {
|
|
79
|
+
const defaultEta = DEFAULT_ETA_BY_PRIORITY[task.priority || 'P2'] || '~4h';
|
|
80
|
+
items.push({
|
|
81
|
+
field: 'metadata.eta',
|
|
82
|
+
severity: 'warning',
|
|
83
|
+
message: `ETA required. Auto-default available: "${defaultEta}" (based on ${task.priority || 'P2'} priority)`,
|
|
84
|
+
hint: 'Provide explicit ETA or accept the auto-default.',
|
|
85
|
+
autoDefault: defaultEta,
|
|
86
|
+
});
|
|
87
|
+
autoDefaults['metadata.eta'] = defaultEta;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ── validating ──────────────────────────────────────────────────────
|
|
91
|
+
if (targetStatus === 'validating') {
|
|
92
|
+
// Detect non-code task (explicit flag or lane-based)
|
|
93
|
+
const handoff = meta.review_handoff;
|
|
94
|
+
const isNonCode = handoff?.non_code === true || handoff?.doc_only === true || handoff?.config_only === true;
|
|
95
|
+
// Artifact path
|
|
96
|
+
const artifactPath = meta.artifact_path;
|
|
97
|
+
if (!artifactPath?.trim()) {
|
|
98
|
+
const suggestedPath = `process/TASK-${taskId.split('-').pop()}.md`;
|
|
99
|
+
items.push({
|
|
100
|
+
field: 'metadata.artifact_path',
|
|
101
|
+
severity: isNonCode ? 'warning' : 'error',
|
|
102
|
+
message: isNonCode ? 'artifact_path recommended' : 'artifact_path required under process/',
|
|
103
|
+
hint: `Suggested: "${suggestedPath}"`,
|
|
104
|
+
autoDefault: suggestedPath,
|
|
105
|
+
});
|
|
106
|
+
autoDefaults['metadata.artifact_path'] = suggestedPath;
|
|
107
|
+
}
|
|
108
|
+
else if (!isNonCode && !artifactPath.startsWith('process/')) {
|
|
109
|
+
items.push({
|
|
110
|
+
field: 'metadata.artifact_path',
|
|
111
|
+
severity: 'error',
|
|
112
|
+
message: 'artifact_path must be under process/ (repo-relative)',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Review handoff (reuse handoff from above)
|
|
116
|
+
if (!handoff) {
|
|
117
|
+
items.push({
|
|
118
|
+
field: 'metadata.review_handoff',
|
|
119
|
+
severity: 'error',
|
|
120
|
+
message: 'review_handoff object required for validating transition',
|
|
121
|
+
hint: 'Must include: task_id, known_caveats. For code tasks: also artifact_path, test_proof, pr_url, commit_sha. For non-code tasks: set non_code=true.',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
if (!handoff.task_id) {
|
|
126
|
+
items.push({ field: 'metadata.review_handoff.task_id', severity: 'error', message: 'task_id required in review_handoff' });
|
|
127
|
+
}
|
|
128
|
+
else if (handoff.task_id !== taskId) {
|
|
129
|
+
items.push({ field: 'metadata.review_handoff.task_id', severity: 'error', message: `task_id must match: expected "${taskId}"` });
|
|
130
|
+
}
|
|
131
|
+
if (!isNonCode && !handoff.artifact_path) {
|
|
132
|
+
items.push({ field: 'metadata.review_handoff.artifact_path', severity: 'error', message: 'artifact_path required in review_handoff (or set non_code=true)' });
|
|
133
|
+
}
|
|
134
|
+
if (!isNonCode && !handoff.test_proof) {
|
|
135
|
+
items.push({ field: 'metadata.review_handoff.test_proof', severity: 'error', message: 'test_proof required (e.g. "vitest run: 206 pass") — or set non_code=true' });
|
|
136
|
+
}
|
|
137
|
+
if (handoff.known_caveats === undefined) {
|
|
138
|
+
items.push({ field: 'metadata.review_handoff.known_caveats', severity: 'error', message: 'known_caveats required (use "none" if none)' });
|
|
139
|
+
}
|
|
140
|
+
const isDocOnly = handoff.doc_only === true;
|
|
141
|
+
const isConfigOnly = handoff.config_only === true;
|
|
142
|
+
if (!isNonCode && !isDocOnly && !isConfigOnly) {
|
|
143
|
+
if (!handoff.pr_url) {
|
|
144
|
+
items.push({ field: 'metadata.review_handoff.pr_url', severity: 'error', message: 'PR URL required (or set doc_only/config_only/non_code=true)' });
|
|
145
|
+
}
|
|
146
|
+
if (!handoff.commit_sha) {
|
|
147
|
+
items.push({ field: 'metadata.review_handoff.commit_sha', severity: 'error', message: 'commit SHA required (or set doc_only/config_only/non_code=true)' });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// QA bundle
|
|
152
|
+
const qaBundle = meta.qa_bundle;
|
|
153
|
+
if (!qaBundle) {
|
|
154
|
+
items.push({
|
|
155
|
+
field: 'metadata.qa_bundle',
|
|
156
|
+
severity: 'warning',
|
|
157
|
+
message: 'qa_bundle recommended for validating (summary, artifact_links, checks, lane, changed_files, screenshot_proof)',
|
|
158
|
+
hint: 'Include: { summary, artifact_links: [], checks: [], lane, pr_link, commit_shas: [], changed_files: [], screenshot_proof: [] }',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
if (!qaBundle.summary)
|
|
163
|
+
items.push({ field: 'metadata.qa_bundle.summary', severity: 'warning', message: 'qa_bundle.summary recommended' });
|
|
164
|
+
if (!Array.isArray(qaBundle.checks) || qaBundle.checks.length === 0) {
|
|
165
|
+
items.push({ field: 'metadata.qa_bundle.checks', severity: 'warning', message: 'qa_bundle.checks recommended (e.g. ["tsc clean", "vitest 206 pass"])' });
|
|
166
|
+
}
|
|
167
|
+
if (!Array.isArray(qaBundle.changed_files) || qaBundle.changed_files.length === 0) {
|
|
168
|
+
items.push({ field: 'metadata.qa_bundle.changed_files', severity: 'warning', message: 'qa_bundle.changed_files recommended' });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Duplicate-closure contract
|
|
172
|
+
// If a task is being closed/validated as a "duplicate", we must include a canonical reference
|
|
173
|
+
// (PR/commit/task id) so reviewers aren't asked to approve an N/A proof packet.
|
|
174
|
+
const isDuplicateClosure = (typeof meta.auto_close_reason === 'string' && meta.auto_close_reason.toLowerCase().includes('duplicate')) ||
|
|
175
|
+
Boolean(meta.duplicate_of) ||
|
|
176
|
+
(meta.qa_bundle && meta.qa_bundle.lane === 'duplicate-closure');
|
|
177
|
+
if (isDuplicateClosure) {
|
|
178
|
+
const dupeOf = meta.duplicate_of;
|
|
179
|
+
const canonicalPr = (meta.canonical_pr || meta.canonicalPr);
|
|
180
|
+
const canonicalCommit = (meta.canonical_commit || meta.canonicalCommit);
|
|
181
|
+
if (!dupeOf?.trim()) {
|
|
182
|
+
items.push({
|
|
183
|
+
field: 'metadata.duplicate_of',
|
|
184
|
+
severity: 'error',
|
|
185
|
+
message: 'duplicate closure requires duplicate_of (canonical task id)',
|
|
186
|
+
hint: "Set metadata.duplicate_of to the canonical task id you're duplicating (e.g. task-...).",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const hasCanonicalRef = (typeof canonicalPr === 'string' && canonicalPr.startsWith('http')) ||
|
|
190
|
+
(typeof handoff?.pr_url === 'string' && handoff.pr_url.startsWith('http'));
|
|
191
|
+
if (!hasCanonicalRef) {
|
|
192
|
+
items.push({
|
|
193
|
+
field: 'metadata.canonical_pr',
|
|
194
|
+
severity: 'error',
|
|
195
|
+
message: 'duplicate closure requires a canonical PR link (canonical_pr or review_handoff.pr_url)',
|
|
196
|
+
hint: 'Provide the shipped PR URL that satisfies the done criteria.',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
const hasCommitRef = (typeof canonicalCommit === 'string' && canonicalCommit.length >= 7) ||
|
|
200
|
+
(typeof handoff?.commit_sha === 'string' && (handoff.commit_sha.length >= 7));
|
|
201
|
+
if (!hasCommitRef) {
|
|
202
|
+
items.push({
|
|
203
|
+
field: 'metadata.canonical_commit',
|
|
204
|
+
severity: 'warning',
|
|
205
|
+
message: 'duplicate closure should include a canonical commit SHA (canonical_commit or review_handoff.commit_sha)',
|
|
206
|
+
hint: 'Include a commit SHA so reviewers can trace the exact shipped state.',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// ── done ────────────────────────────────────────────────────────────
|
|
212
|
+
if (targetStatus === 'done') {
|
|
213
|
+
if (!meta.reviewer_approved) {
|
|
214
|
+
items.push({
|
|
215
|
+
field: 'metadata.reviewer_approved',
|
|
216
|
+
severity: 'error',
|
|
217
|
+
message: 'reviewer_approved must be true before marking done',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (!meta.artifact_path && !meta.artifacts) {
|
|
221
|
+
items.push({
|
|
222
|
+
field: 'metadata.artifact_path',
|
|
223
|
+
severity: 'error',
|
|
224
|
+
message: 'artifact_path or artifacts required for done status',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// ── Ready-queue floor warning ────────────────────────────────────────
|
|
229
|
+
if ((targetStatus === 'validating' || targetStatus === 'done') && task.assignee) {
|
|
230
|
+
const policy = policyManager.get();
|
|
231
|
+
const rqf = policy.readyQueueFloor;
|
|
232
|
+
if (rqf?.enabled && rqf.agents.includes(task.assignee)) {
|
|
233
|
+
const todoTasks = taskManager.listTasks({ status: 'todo', assignee: task.assignee });
|
|
234
|
+
const unblockedTodo = todoTasks.filter(t => {
|
|
235
|
+
const blocked = t.metadata?.blocked_by;
|
|
236
|
+
if (!blocked)
|
|
237
|
+
return true;
|
|
238
|
+
const blocker = taskManager.getTask(blocked);
|
|
239
|
+
return !blocker || blocker.status === 'done';
|
|
240
|
+
});
|
|
241
|
+
// This task is leaving the queue, so effective count will drop
|
|
242
|
+
const effectiveReady = unblockedTodo.length;
|
|
243
|
+
const hasOverride = task.metadata?.readyQueueOverride === true;
|
|
244
|
+
if (effectiveReady < rqf.minReady && !hasOverride) {
|
|
245
|
+
const enforce = rqf.enforceBlock !== false; // default to enforced
|
|
246
|
+
items.push({
|
|
247
|
+
field: 'readyQueueFloor',
|
|
248
|
+
severity: enforce ? 'error' : 'warning',
|
|
249
|
+
message: `Ready queue will be ${effectiveReady}/${rqf.minReady} after this transition. ${enforce ? 'Blocked: queue more tasks for @' + task.assignee + ' before closing.' : 'Ensure next tasks are queued for @' + task.assignee + '.'}`,
|
|
250
|
+
hint: enforce
|
|
251
|
+
? 'Next-task-before-close rule: assign at least ' + rqf.minReady + ' todo tasks to @' + task.assignee + ' before this task can move to ' + targetStatus + '. Override with metadata.readyQueueOverride=true.'
|
|
252
|
+
: 'Create/assign more todo tasks to maintain engineering lane throughput.',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ── Build template ──────────────────────────────────────────────────
|
|
258
|
+
const template = buildTemplate(task, targetStatus, autoDefaults);
|
|
259
|
+
const hasErrors = items.some(i => i.severity === 'error');
|
|
260
|
+
return {
|
|
261
|
+
taskId,
|
|
262
|
+
currentStatus: task.status,
|
|
263
|
+
targetStatus,
|
|
264
|
+
ready: !hasErrors,
|
|
265
|
+
items,
|
|
266
|
+
autoDefaults,
|
|
267
|
+
template,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
// ── Auto-defaults application ──────────────────────────────────────────────
|
|
271
|
+
export function applyAutoDefaults(taskId, targetStatus, metadata) {
|
|
272
|
+
const task = taskManager.getTask(taskId);
|
|
273
|
+
if (!task)
|
|
274
|
+
return metadata;
|
|
275
|
+
const result = { ...metadata };
|
|
276
|
+
// Auto-fill ETA for doing
|
|
277
|
+
if (targetStatus === 'doing' && !result.eta) {
|
|
278
|
+
const priority = task.priority || 'P2';
|
|
279
|
+
result.eta = DEFAULT_ETA_BY_PRIORITY[priority] || '~4h';
|
|
280
|
+
}
|
|
281
|
+
// Auto-fill artifact_path for validating
|
|
282
|
+
if (targetStatus === 'validating' && !result.artifact_path) {
|
|
283
|
+
const shortId = taskId.split('-').pop();
|
|
284
|
+
result.artifact_path = `process/TASK-${shortId}.md`;
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
// ── Template builder ───────────────────────────────────────────────────────
|
|
289
|
+
function buildTemplate(task, targetStatus, autoDefaults) {
|
|
290
|
+
const shortId = task.id.split('-').pop();
|
|
291
|
+
if (targetStatus === 'doing') {
|
|
292
|
+
return {
|
|
293
|
+
status: 'doing',
|
|
294
|
+
metadata: {
|
|
295
|
+
eta: autoDefaults['metadata.eta'] || '<required>',
|
|
296
|
+
branch: `link/task-${shortId}`,
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
if (targetStatus === 'validating') {
|
|
301
|
+
return {
|
|
302
|
+
status: 'validating',
|
|
303
|
+
metadata: {
|
|
304
|
+
artifact_path: autoDefaults['metadata.artifact_path'] || `process/TASK-${shortId}.md`,
|
|
305
|
+
review_handoff: {
|
|
306
|
+
task_id: task.id,
|
|
307
|
+
repo: 'reflectt/reflectt-node',
|
|
308
|
+
pr_url: '<required: https://github.com/reflectt/reflectt-node/pull/NNN>',
|
|
309
|
+
commit_sha: '<required: 7+ hex chars>',
|
|
310
|
+
artifact_path: `process/TASK-${shortId}.md`,
|
|
311
|
+
test_proof: '<required: e.g. "vitest run: 206 pass, tsc clean">',
|
|
312
|
+
known_caveats: '<required: "none" or description>',
|
|
313
|
+
},
|
|
314
|
+
qa_bundle: {
|
|
315
|
+
summary: '<recommended>',
|
|
316
|
+
artifact_links: ['<PR URL>'],
|
|
317
|
+
checks: ['<e.g. tsc clean>', '<vitest NNN pass>'],
|
|
318
|
+
lane: '<feature name>',
|
|
319
|
+
pr_link: '<PR URL>',
|
|
320
|
+
commit_shas: ['<sha>'],
|
|
321
|
+
changed_files: ['<file1>', '<file2>'],
|
|
322
|
+
screenshot_proof: ['<test output summary>'],
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=taskPrecheck.js.map
|