mirror-mind 0.0.1 → 0.33.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/.claude/hooks/log-assistant-reply.sh +85 -0
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +646 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +100 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.33.0.md +164 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
- package/plugins/mirror-mind/hooks/hooks.json +62 -0
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/hookPayload.ts +51 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +851 -0
- package/ts/src/conversation/loggerCli.ts +321 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1872 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +1001 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/stdinSource.ts +37 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +298 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +214 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +73 -0
- package/ts/src/hooks/runtime.ts +179 -0
- package/ts/src/hooks/runtimeDetection.ts +34 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/mirrorForm.ts +43 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,964 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 4 — story closure: Validate, Debt Review, Coherence, Done.
|
|
2
|
+
//
|
|
3
|
+
// Port of the second half of `src/memory/builder/lifecycle.py`.
|
|
4
|
+
//
|
|
5
|
+
// The four verbs share one shape, and it is the shape a port gets wrong: each
|
|
6
|
+
// computes a MISSING-EVIDENCE tuple, and that tuple decides three cursor fields at
|
|
7
|
+
// once — `activeCheckpoint`, `pendingConfirmation`, and `lastDeliveryEvent`. So
|
|
8
|
+
// "incomplete" is not an error and not a different surface; it is the same surface
|
|
9
|
+
// over a different persisted state. A port can render the right card and write the
|
|
10
|
+
// wrong three fields, which is why the corpus grades every branch as a sequence.
|
|
11
|
+
//
|
|
12
|
+
// Two of the four allow EXACT-STATE re-entry to answer their own pending
|
|
13
|
+
// confirmation — Review for `navigator_debt_decision`, Coherence for
|
|
14
|
+
// `navigator_coherence` — because without it `--decision pending` deadlocks the
|
|
15
|
+
// lifecycle: every verb refuses while the confirmation it would resolve stays
|
|
16
|
+
// pending. That was found live on another project, not reasoned about. Done allows no
|
|
17
|
+
// re-entry at all and refuses any pending confirmation its predecessors left behind.
|
|
18
|
+
//
|
|
19
|
+
// Until CV22.DS10.TS1 all four ordered a Journey projection refresh after their
|
|
20
|
+
// artifact existed. The projection subsystem retired with the Python core, so the
|
|
21
|
+
// ordering dance is gone and the cursor write is the whole write.
|
|
22
|
+
//
|
|
23
|
+
// They live in one module rather than four because they are one state machine over
|
|
24
|
+
// one cursor, sharing the missing-evidence idiom and the guard vocabulary. Splitting
|
|
25
|
+
// them would duplicate the shared normalizers without separating any concern.
|
|
26
|
+
|
|
27
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
28
|
+
import { pyStrip, sortByCodePoint } from "#util/pythonText.ts";
|
|
29
|
+
import type { ArtifactOutcome } from "./artifacts/artifactWriter.ts";
|
|
30
|
+
import {
|
|
31
|
+
renderCoherenceArtifact,
|
|
32
|
+
renderDoneArtifact,
|
|
33
|
+
renderReviewArtifact,
|
|
34
|
+
renderValidationArtifact,
|
|
35
|
+
writeClosureArtifact,
|
|
36
|
+
} from "./artifacts/closureArtifacts.ts";
|
|
37
|
+
import { artifactState } from "./artifacts/scaffoldState.ts";
|
|
38
|
+
import { cardPrefixed, cardText, cardWrapped } from "./card.ts";
|
|
39
|
+
import { normalizeRequired } from "./cursorTransitions.ts";
|
|
40
|
+
import {
|
|
41
|
+
type BuilderDeliveryCursor,
|
|
42
|
+
type CursorWriteDeps,
|
|
43
|
+
getDeliveryCursor,
|
|
44
|
+
setDeliveryCursor,
|
|
45
|
+
} from "./deliveryCursor.ts";
|
|
46
|
+
import { LifecycleRefusal, refuseIfAlreadyComplete } from "./lifecycleRefusal.ts";
|
|
47
|
+
import { renderLifecycleRibbon } from "./lifecycleRibbon.ts";
|
|
48
|
+
import type { ContractDefinition, MethodDefinition } from "./methodDefinition.ts";
|
|
49
|
+
import { displayPath, targetLine } from "./projectPaths.ts";
|
|
50
|
+
import { wrapAriadSurface } from "./surfaceProtocol.ts";
|
|
51
|
+
|
|
52
|
+
/** Python `_contract_for`. */
|
|
53
|
+
function contractFor(method: MethodDefinition, contractId: string): ContractDefinition {
|
|
54
|
+
const contract = method.contracts.find((candidate) => candidate.id === contractId);
|
|
55
|
+
if (contract === undefined) {
|
|
56
|
+
throw new Error(`method ${method.id} is missing contract ${contractId}`);
|
|
57
|
+
}
|
|
58
|
+
return contract;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Python `_normalize_validation_choice`.
|
|
63
|
+
*
|
|
64
|
+
* Lowercases and maps `-` to `_` before the membership test, and the refusal lists
|
|
65
|
+
* the allowed values SORTED — so the message is stable regardless of the order the
|
|
66
|
+
* call site declared them in.
|
|
67
|
+
*/
|
|
68
|
+
function normalizeChoice(value: string, field: string, allowed: readonly string[]): string {
|
|
69
|
+
const normalized =
|
|
70
|
+
typeof value === "string" ? pyStrip(value).toLowerCase().replaceAll("-", "_") : "";
|
|
71
|
+
if (!allowed.includes(normalized)) {
|
|
72
|
+
throw new Error(`${field} must be one of ${sortByCodePoint([...allowed]).join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
return normalized;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Python's `tuple(x.strip() for x in items if x.strip())`. */
|
|
78
|
+
function normalizedList(items: readonly string[]): string[] {
|
|
79
|
+
return items.map((item) => pyStrip(item)).filter((item) => item !== "");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Python's `(value or default).strip()`. */
|
|
83
|
+
function textOr(value: string | null | undefined, fallback: string): string {
|
|
84
|
+
return pyStrip(value || fallback);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Python's `x.strip() if x and x.strip() else None`. */
|
|
88
|
+
function optionalText(value: string | null | undefined): string | null {
|
|
89
|
+
if (!value) return null;
|
|
90
|
+
const stripped = pyStrip(value);
|
|
91
|
+
return stripped || null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// --- Validation ------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
export interface BuilderValidationReport {
|
|
97
|
+
readonly journey: string;
|
|
98
|
+
readonly method: string;
|
|
99
|
+
readonly activeItem: string;
|
|
100
|
+
readonly activeItemTitle: string | null;
|
|
101
|
+
readonly automatedChecks: readonly string[];
|
|
102
|
+
readonly checksStatus: string;
|
|
103
|
+
readonly e2eDecision: string;
|
|
104
|
+
readonly e2eEvidence: string | null;
|
|
105
|
+
readonly navigatorValidationRoute: string;
|
|
106
|
+
readonly navigatorAccepted: boolean;
|
|
107
|
+
readonly expectedObservation: string;
|
|
108
|
+
readonly passCondition: string;
|
|
109
|
+
readonly failCondition: string;
|
|
110
|
+
readonly missingEvidence: readonly string[];
|
|
111
|
+
readonly validationContract: ContractDefinition;
|
|
112
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
113
|
+
readonly validationArtifactPath: string | null;
|
|
114
|
+
/** The project the record was written under: the card names it relative (CR082). */
|
|
115
|
+
readonly projectRoot: string | null;
|
|
116
|
+
/** What the writer did with the record (CR079); absent when there is no artifact path. */
|
|
117
|
+
readonly validationArtifactOutcome?: ArtifactOutcome | null;
|
|
118
|
+
readonly nextEvent: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Python `_validation_missing_evidence`.
|
|
123
|
+
*
|
|
124
|
+
* Order is behavior: the tuple is rendered as a list, so a port that reorders these
|
|
125
|
+
* produces a different surface. Note the `elif` on acceptance — a MISSING route is
|
|
126
|
+
* reported instead of an unaccepted one, never both.
|
|
127
|
+
*/
|
|
128
|
+
function validationMissingEvidence(input: {
|
|
129
|
+
checks: readonly string[];
|
|
130
|
+
checksStatus: string;
|
|
131
|
+
e2eDecision: string;
|
|
132
|
+
e2eEvidence: string | null | undefined;
|
|
133
|
+
navigatorValidationRoute: string;
|
|
134
|
+
navigatorAccepted: boolean;
|
|
135
|
+
}): string[] {
|
|
136
|
+
const missing: string[] = [];
|
|
137
|
+
if (input.checks.length === 0) missing.push("automated checks are not declared");
|
|
138
|
+
if (input.checksStatus !== "passed") {
|
|
139
|
+
missing.push(`automated checks status is ${input.checksStatus}`);
|
|
140
|
+
}
|
|
141
|
+
const evidence = input.e2eEvidence && pyStrip(input.e2eEvidence) ? input.e2eEvidence : null;
|
|
142
|
+
if (input.e2eDecision === "required" && evidence === null) {
|
|
143
|
+
missing.push("required E2E evidence is missing");
|
|
144
|
+
}
|
|
145
|
+
if (input.e2eDecision === "skipped" && evidence === null) {
|
|
146
|
+
missing.push("skipped E2E requires an explicit reason");
|
|
147
|
+
}
|
|
148
|
+
if (!pyStrip(input.navigatorValidationRoute)) {
|
|
149
|
+
missing.push("Navigator validation route is missing");
|
|
150
|
+
} else if (!input.navigatorAccepted) {
|
|
151
|
+
missing.push("Navigator validation has not been accepted");
|
|
152
|
+
}
|
|
153
|
+
return missing;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface ValidateOptions {
|
|
157
|
+
readonly journey: string;
|
|
158
|
+
readonly method: MethodDefinition;
|
|
159
|
+
readonly automatedChecks?: readonly string[];
|
|
160
|
+
readonly checksStatus?: string;
|
|
161
|
+
readonly e2eDecision?: string;
|
|
162
|
+
readonly e2eEvidence?: string | null;
|
|
163
|
+
readonly navigatorValidationRoute?: string | null;
|
|
164
|
+
readonly navigatorAccepted?: boolean;
|
|
165
|
+
readonly expectedObservation?: string | null;
|
|
166
|
+
readonly passCondition?: string | null;
|
|
167
|
+
readonly failCondition?: string | null;
|
|
168
|
+
readonly implementationComplete?: boolean;
|
|
169
|
+
readonly validationArtifactPath?: string | null;
|
|
170
|
+
/** The project the artifact must stay inside (CR079). */
|
|
171
|
+
readonly projectRoot?: string | null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const PLAN_EVENTS = new Set(["plan_approved", "delivery_story_plan_approved"]);
|
|
175
|
+
|
|
176
|
+
/** Python `validate_lifecycle_item`. */
|
|
177
|
+
export function validateLifecycleItem(
|
|
178
|
+
db: WritableDatabase,
|
|
179
|
+
options: ValidateOptions,
|
|
180
|
+
deps: CursorWriteDeps,
|
|
181
|
+
): BuilderValidationReport {
|
|
182
|
+
const journey = normalizeRequired(options.journey, "journey");
|
|
183
|
+
const existing = getDeliveryCursor(db, journey);
|
|
184
|
+
if (existing === null) throw new Error("delivery cursor is required before validation");
|
|
185
|
+
if (!existing.activeItem) throw new Error("active item is required before validation");
|
|
186
|
+
refuseIfAlreadyComplete("validate", existing);
|
|
187
|
+
if (existing.pendingConfirmation && existing.pendingConfirmation !== "navigator_validation") {
|
|
188
|
+
throw new LifecycleRefusal(
|
|
189
|
+
"validate",
|
|
190
|
+
"pending_confirmation",
|
|
191
|
+
`Validation is blocked: pending confirmation ${existing.pendingConfirmation}.`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const navigatorAccepted = options.navigatorAccepted ?? false;
|
|
195
|
+
if (existing.pendingConfirmation === "navigator_validation" && !navigatorAccepted) {
|
|
196
|
+
throw new LifecycleRefusal(
|
|
197
|
+
"validate",
|
|
198
|
+
"pending_confirmation",
|
|
199
|
+
"Validation is blocked: pending Navigator validation acceptance.",
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const lastEvent = existing.lastDeliveryEvent ?? "";
|
|
203
|
+
if (
|
|
204
|
+
!(
|
|
205
|
+
PLAN_EVENTS.has(lastEvent) ||
|
|
206
|
+
lastEvent === "implementation_complete" ||
|
|
207
|
+
lastEvent === "validate"
|
|
208
|
+
)
|
|
209
|
+
) {
|
|
210
|
+
throw new LifecycleRefusal(
|
|
211
|
+
"validate",
|
|
212
|
+
"not_reached",
|
|
213
|
+
"Validation requires an approved Plan and completed implementation",
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (PLAN_EVENTS.has(lastEvent) && !(options.implementationComplete ?? false)) {
|
|
217
|
+
throw new LifecycleRefusal(
|
|
218
|
+
"validate",
|
|
219
|
+
"missing_evidence",
|
|
220
|
+
"Validation requires implementation completion evidence",
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const checks = normalizedList(options.automatedChecks ?? []);
|
|
225
|
+
const checksStatus = normalizeChoice(options.checksStatus ?? "not_run", "checks_status", [
|
|
226
|
+
"passed",
|
|
227
|
+
"failed",
|
|
228
|
+
"not_run",
|
|
229
|
+
]);
|
|
230
|
+
const e2eDecision = normalizeChoice(options.e2eDecision ?? "not_required", "e2e_decision", [
|
|
231
|
+
"required",
|
|
232
|
+
"not_required",
|
|
233
|
+
"waived",
|
|
234
|
+
"skipped",
|
|
235
|
+
]);
|
|
236
|
+
const route = textOr(
|
|
237
|
+
options.navigatorValidationRoute,
|
|
238
|
+
"Navigator validates the behavior described by the Plan.",
|
|
239
|
+
);
|
|
240
|
+
const observation = textOr(
|
|
241
|
+
options.expectedObservation,
|
|
242
|
+
"Expected behavior from the Plan is observable.",
|
|
243
|
+
);
|
|
244
|
+
const passText = textOr(options.passCondition, "Navigator accepts the observed behavior.");
|
|
245
|
+
const failText = textOr(options.failCondition, "Navigator cannot observe the planned behavior.");
|
|
246
|
+
const missing = validationMissingEvidence({
|
|
247
|
+
checks,
|
|
248
|
+
checksStatus,
|
|
249
|
+
e2eDecision,
|
|
250
|
+
e2eEvidence: options.e2eEvidence,
|
|
251
|
+
navigatorValidationRoute: route,
|
|
252
|
+
navigatorAccepted,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const cursor = setDeliveryCursor(
|
|
256
|
+
db,
|
|
257
|
+
{
|
|
258
|
+
journey,
|
|
259
|
+
method: options.method.id,
|
|
260
|
+
activeItem: existing.activeItem,
|
|
261
|
+
activeItemTitle: existing.activeItemTitle,
|
|
262
|
+
activeItemLevel: existing.activeItemLevel,
|
|
263
|
+
activeCheckpoint: missing.length > 0 ? "after_validation" : null,
|
|
264
|
+
pendingConfirmation: missing.length > 0 ? "navigator_validation" : null,
|
|
265
|
+
lastDeliveryEvent: missing.length > 0 ? "validate" : "validation_passed",
|
|
266
|
+
cadenceProfile: existing.cadenceProfile,
|
|
267
|
+
cadenceLimits: existing.cadenceLimits,
|
|
268
|
+
granularityDecision: existing.granularityDecision,
|
|
269
|
+
navigatorFlowUnit: existing.navigatorFlowUnit,
|
|
270
|
+
childWorkItems: existing.childWorkItems,
|
|
271
|
+
aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
|
|
272
|
+
},
|
|
273
|
+
deps,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const report: BuilderValidationReport = {
|
|
277
|
+
journey,
|
|
278
|
+
method: options.method.id,
|
|
279
|
+
activeItem: existing.activeItem,
|
|
280
|
+
activeItemTitle: existing.activeItemTitle,
|
|
281
|
+
automatedChecks: checks,
|
|
282
|
+
checksStatus,
|
|
283
|
+
e2eDecision,
|
|
284
|
+
e2eEvidence: optionalText(options.e2eEvidence),
|
|
285
|
+
navigatorValidationRoute: route,
|
|
286
|
+
navigatorAccepted,
|
|
287
|
+
expectedObservation: observation,
|
|
288
|
+
passCondition: passText,
|
|
289
|
+
failCondition: failText,
|
|
290
|
+
missingEvidence: missing,
|
|
291
|
+
validationContract: contractFor(options.method, "validation_contract"),
|
|
292
|
+
cursor,
|
|
293
|
+
validationArtifactPath: options.validationArtifactPath ?? null,
|
|
294
|
+
projectRoot: options.projectRoot ?? null,
|
|
295
|
+
nextEvent: "debt_review",
|
|
296
|
+
};
|
|
297
|
+
if (report.validationArtifactPath === null) return report;
|
|
298
|
+
const validationArtifactOutcome = writeClosureArtifact(
|
|
299
|
+
report.validationArtifactPath,
|
|
300
|
+
renderValidationArtifact(report),
|
|
301
|
+
options.projectRoot,
|
|
302
|
+
);
|
|
303
|
+
return { ...report, validationArtifactOutcome };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Python `render_validation_checkpoint`. */
|
|
307
|
+
export function renderValidationCheckpoint(report: BuilderValidationReport): string {
|
|
308
|
+
const blocked = report.missingEvidence.length > 0;
|
|
309
|
+
const body = `${[
|
|
310
|
+
"Delivery",
|
|
311
|
+
renderLifecycleRibbon("validate", report.cursor.activeItemLevel),
|
|
312
|
+
"",
|
|
313
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
314
|
+
"│ 🧪■ VALIDATION CHECKPOINT │",
|
|
315
|
+
"│ │",
|
|
316
|
+
cardText("active item"),
|
|
317
|
+
cardText(report.activeItem),
|
|
318
|
+
"│ │",
|
|
319
|
+
cardText("status"),
|
|
320
|
+
cardText(blocked ? "pending_navigator_validation" : "passed"),
|
|
321
|
+
"│ │",
|
|
322
|
+
cardText("automated checks"),
|
|
323
|
+
...cardPrefixed(
|
|
324
|
+
report.automatedChecks.length > 0
|
|
325
|
+
? report.automatedChecks
|
|
326
|
+
: ["No automated checks declared."],
|
|
327
|
+
"✓",
|
|
328
|
+
),
|
|
329
|
+
"│ │",
|
|
330
|
+
cardText("checks status"),
|
|
331
|
+
cardText(report.checksStatus),
|
|
332
|
+
"│ │",
|
|
333
|
+
cardText("e2e decision"),
|
|
334
|
+
cardText(report.e2eDecision),
|
|
335
|
+
"│ │",
|
|
336
|
+
cardText("e2e evidence"),
|
|
337
|
+
...cardWrapped(report.e2eEvidence || "none"),
|
|
338
|
+
"│ │",
|
|
339
|
+
cardText("navigator validation route"),
|
|
340
|
+
...cardWrapped(report.navigatorValidationRoute),
|
|
341
|
+
"│ │",
|
|
342
|
+
cardText("navigator accepted"),
|
|
343
|
+
cardText(report.navigatorAccepted ? "yes" : "no"),
|
|
344
|
+
"│ │",
|
|
345
|
+
cardText("expected observation"),
|
|
346
|
+
...cardWrapped(report.expectedObservation),
|
|
347
|
+
"│ │",
|
|
348
|
+
cardText("pass condition"),
|
|
349
|
+
...cardWrapped(report.passCondition),
|
|
350
|
+
"│ │",
|
|
351
|
+
cardText("fail condition"),
|
|
352
|
+
...cardWrapped(report.failCondition),
|
|
353
|
+
"│ │",
|
|
354
|
+
cardText("missing evidence"),
|
|
355
|
+
...cardPrefixed(blocked ? report.missingEvidence : ["none"], blocked ? "✕" : "✓"),
|
|
356
|
+
"│ │",
|
|
357
|
+
cardText("validation contract"),
|
|
358
|
+
...cardPrefixed(report.validationContract.rules, "✓"),
|
|
359
|
+
"│ │",
|
|
360
|
+
cardText("validation artifact"),
|
|
361
|
+
...recordRows(report.validationArtifactPath, report.projectRoot, report.journey),
|
|
362
|
+
...preservedNote(report.validationArtifactOutcome),
|
|
363
|
+
"│ │",
|
|
364
|
+
cardText("boundary"),
|
|
365
|
+
...cardWrapped(
|
|
366
|
+
blocked
|
|
367
|
+
? "Do not move past Validation until missing evidence is resolved."
|
|
368
|
+
: "Validation is complete; Builder may proceed to Debt Review.",
|
|
369
|
+
),
|
|
370
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
371
|
+
].join("\n")}\n`;
|
|
372
|
+
return wrapAriadSurface("validation_checkpoint", body);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// --- Debt Review -----------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
export interface BuilderReviewReport {
|
|
378
|
+
readonly journey: string;
|
|
379
|
+
readonly method: string;
|
|
380
|
+
readonly activeItem: string;
|
|
381
|
+
readonly activeItemTitle: string | null;
|
|
382
|
+
readonly debtFindings: readonly string[];
|
|
383
|
+
readonly debtDecision: string;
|
|
384
|
+
readonly deferReason: string | null;
|
|
385
|
+
readonly revisitTrigger: string | null;
|
|
386
|
+
readonly missingDecision: readonly string[];
|
|
387
|
+
readonly debtReviewContract: ContractDefinition;
|
|
388
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
389
|
+
readonly reviewArtifactPath: string | null;
|
|
390
|
+
/** The project the record was written under: the card names it relative (CR082). */
|
|
391
|
+
readonly projectRoot: string | null;
|
|
392
|
+
/** What the writer did with the record (CR079); absent when there is no artifact path. */
|
|
393
|
+
readonly reviewArtifactOutcome?: ArtifactOutcome | null;
|
|
394
|
+
readonly nextEvent: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Python `_review_missing_decision`. */
|
|
398
|
+
function reviewMissingDecision(input: {
|
|
399
|
+
debtDecision: string;
|
|
400
|
+
deferReason: string | null | undefined;
|
|
401
|
+
revisitTrigger: string | null | undefined;
|
|
402
|
+
}): string[] {
|
|
403
|
+
const missing: string[] = [];
|
|
404
|
+
if (input.debtDecision === "pending") missing.push("Navigator debt decision is required");
|
|
405
|
+
if (input.debtDecision === "defer") {
|
|
406
|
+
if (!(input.deferReason && pyStrip(input.deferReason))) {
|
|
407
|
+
missing.push("deferred debt requires a reason");
|
|
408
|
+
}
|
|
409
|
+
if (!(input.revisitTrigger && pyStrip(input.revisitTrigger))) {
|
|
410
|
+
missing.push("deferred debt requires a revisit trigger");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// `pay_now` is never complete here: it must route through a Refactor loop first,
|
|
414
|
+
// so Debt Review cannot be the thing that closes it.
|
|
415
|
+
if (input.debtDecision === "pay_now") {
|
|
416
|
+
missing.push("pay-now debt must route through Refactor before Done");
|
|
417
|
+
}
|
|
418
|
+
return missing;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export interface ReviewOptions {
|
|
422
|
+
readonly journey: string;
|
|
423
|
+
readonly method: MethodDefinition;
|
|
424
|
+
readonly debtFindings?: readonly string[];
|
|
425
|
+
readonly debtDecision?: string;
|
|
426
|
+
readonly deferReason?: string | null;
|
|
427
|
+
readonly revisitTrigger?: string | null;
|
|
428
|
+
readonly reviewArtifactPath?: string | null;
|
|
429
|
+
/** The project the artifact must stay inside (CR079). */
|
|
430
|
+
readonly projectRoot?: string | null;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** Python `review_lifecycle_item`. */
|
|
434
|
+
export function reviewLifecycleItem(
|
|
435
|
+
db: WritableDatabase,
|
|
436
|
+
options: ReviewOptions,
|
|
437
|
+
deps: CursorWriteDeps,
|
|
438
|
+
): BuilderReviewReport {
|
|
439
|
+
const journey = normalizeRequired(options.journey, "journey");
|
|
440
|
+
const existing = getDeliveryCursor(db, journey);
|
|
441
|
+
if (existing === null) throw new Error("delivery cursor is required before review");
|
|
442
|
+
if (!existing.activeItem) throw new Error("active item is required before review");
|
|
443
|
+
refuseIfAlreadyComplete("debt_review", existing);
|
|
444
|
+
|
|
445
|
+
const reentering =
|
|
446
|
+
existing.pendingConfirmation === "navigator_debt_decision" &&
|
|
447
|
+
existing.lastDeliveryEvent === "review";
|
|
448
|
+
if (existing.pendingConfirmation && !reentering) {
|
|
449
|
+
throw new LifecycleRefusal(
|
|
450
|
+
"debt_review",
|
|
451
|
+
"pending_confirmation",
|
|
452
|
+
`Review is blocked: pending confirmation ${existing.pendingConfirmation}.`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
if (existing.lastDeliveryEvent !== "validation_passed" && !reentering) {
|
|
456
|
+
throw new LifecycleRefusal(
|
|
457
|
+
"debt_review",
|
|
458
|
+
"not_reached",
|
|
459
|
+
"Debt Review requires passed Validation",
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const decision = normalizeChoice(options.debtDecision ?? "pending", "debt_decision", [
|
|
464
|
+
"pending",
|
|
465
|
+
"no_action",
|
|
466
|
+
"defer",
|
|
467
|
+
"pay_now",
|
|
468
|
+
]);
|
|
469
|
+
const findings = normalizedList(options.debtFindings ?? []);
|
|
470
|
+
const missing = reviewMissingDecision({
|
|
471
|
+
debtDecision: decision,
|
|
472
|
+
deferReason: options.deferReason,
|
|
473
|
+
revisitTrigger: options.revisitTrigger,
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
const cursor = setDeliveryCursor(
|
|
477
|
+
db,
|
|
478
|
+
{
|
|
479
|
+
journey,
|
|
480
|
+
method: options.method.id,
|
|
481
|
+
activeItem: existing.activeItem,
|
|
482
|
+
activeItemTitle: existing.activeItemTitle,
|
|
483
|
+
activeItemLevel: existing.activeItemLevel,
|
|
484
|
+
activeCheckpoint: missing.length > 0 ? "review_decision" : null,
|
|
485
|
+
pendingConfirmation: missing.length > 0 ? "navigator_debt_decision" : null,
|
|
486
|
+
lastDeliveryEvent: missing.length > 0 ? "review" : "review_complete",
|
|
487
|
+
cadenceProfile: existing.cadenceProfile,
|
|
488
|
+
cadenceLimits: existing.cadenceLimits,
|
|
489
|
+
granularityDecision: existing.granularityDecision,
|
|
490
|
+
navigatorFlowUnit: existing.navigatorFlowUnit,
|
|
491
|
+
childWorkItems: existing.childWorkItems,
|
|
492
|
+
aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
|
|
493
|
+
},
|
|
494
|
+
deps,
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
const report: BuilderReviewReport = {
|
|
498
|
+
journey,
|
|
499
|
+
method: options.method.id,
|
|
500
|
+
activeItem: existing.activeItem,
|
|
501
|
+
activeItemTitle: existing.activeItemTitle,
|
|
502
|
+
debtFindings: findings,
|
|
503
|
+
debtDecision: decision,
|
|
504
|
+
deferReason: optionalText(options.deferReason),
|
|
505
|
+
revisitTrigger: optionalText(options.revisitTrigger),
|
|
506
|
+
missingDecision: missing,
|
|
507
|
+
debtReviewContract: contractFor(options.method, "debt_review_contract"),
|
|
508
|
+
cursor,
|
|
509
|
+
reviewArtifactPath: options.reviewArtifactPath ?? null,
|
|
510
|
+
projectRoot: options.projectRoot ?? null,
|
|
511
|
+
nextEvent: "coherence",
|
|
512
|
+
};
|
|
513
|
+
if (report.reviewArtifactPath === null) return report;
|
|
514
|
+
const reviewArtifactOutcome = writeClosureArtifact(
|
|
515
|
+
report.reviewArtifactPath,
|
|
516
|
+
renderReviewArtifact(report),
|
|
517
|
+
options.projectRoot,
|
|
518
|
+
);
|
|
519
|
+
return { ...report, reviewArtifactOutcome };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Python `render_review_checkpoint`. */
|
|
523
|
+
export function renderReviewCheckpoint(report: BuilderReviewReport): string {
|
|
524
|
+
const blocked = report.missingDecision.length > 0;
|
|
525
|
+
const body = `${[
|
|
526
|
+
"Delivery",
|
|
527
|
+
renderLifecycleRibbon("debt_review", report.cursor.activeItemLevel),
|
|
528
|
+
"",
|
|
529
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
530
|
+
"│ 🔎■ DEBT REVIEW CHECKPOINT │",
|
|
531
|
+
"│ │",
|
|
532
|
+
cardText("active item"),
|
|
533
|
+
cardText(report.activeItem),
|
|
534
|
+
"│ │",
|
|
535
|
+
cardText("status"),
|
|
536
|
+
cardText(blocked ? "pending_debt_decision" : "reviewed"),
|
|
537
|
+
"│ │",
|
|
538
|
+
cardText("debt findings"),
|
|
539
|
+
...cardPrefixed(
|
|
540
|
+
report.debtFindings.length > 0 ? report.debtFindings : ["No debt findings declared."],
|
|
541
|
+
"△",
|
|
542
|
+
),
|
|
543
|
+
"│ │",
|
|
544
|
+
cardText("debt decision"),
|
|
545
|
+
cardText(report.debtDecision),
|
|
546
|
+
"│ │",
|
|
547
|
+
cardText("defer reason"),
|
|
548
|
+
...cardWrapped(report.deferReason || "none"),
|
|
549
|
+
"│ │",
|
|
550
|
+
cardText("revisit trigger"),
|
|
551
|
+
...cardWrapped(report.revisitTrigger || "none"),
|
|
552
|
+
"│ │",
|
|
553
|
+
cardText("missing decision"),
|
|
554
|
+
...cardPrefixed(blocked ? report.missingDecision : ["none"], blocked ? "✕" : "✓"),
|
|
555
|
+
"│ │",
|
|
556
|
+
cardText("debt review contract"),
|
|
557
|
+
...cardPrefixed(report.debtReviewContract.rules, "✓"),
|
|
558
|
+
"│ │",
|
|
559
|
+
cardText("review artifact"),
|
|
560
|
+
...recordRows(report.reviewArtifactPath, report.projectRoot, report.journey),
|
|
561
|
+
...preservedNote(report.reviewArtifactOutcome),
|
|
562
|
+
"│ │",
|
|
563
|
+
cardText("boundary"),
|
|
564
|
+
...cardWrapped(
|
|
565
|
+
blocked
|
|
566
|
+
? "Do not move past Debt Review until the Navigator debt decision is resolved."
|
|
567
|
+
: "Debt Review is complete; Builder may proceed to Done.",
|
|
568
|
+
),
|
|
569
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
570
|
+
].join("\n")}\n`;
|
|
571
|
+
return wrapAriadSurface("debt_review_checkpoint", body);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// --- Coherence -------------------------------------------------------------
|
|
575
|
+
|
|
576
|
+
export interface BuilderCoherenceReport {
|
|
577
|
+
readonly journey: string;
|
|
578
|
+
readonly method: string;
|
|
579
|
+
readonly activeItem: string;
|
|
580
|
+
readonly activeItemTitle: string | null;
|
|
581
|
+
readonly processAlignment: string;
|
|
582
|
+
readonly projectAlignment: string;
|
|
583
|
+
readonly productAlignment: string;
|
|
584
|
+
readonly localDifferences: readonly string[];
|
|
585
|
+
readonly missingCoherence: readonly string[];
|
|
586
|
+
readonly coherenceContract: ContractDefinition;
|
|
587
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
588
|
+
readonly coherenceArtifactPath: string | null;
|
|
589
|
+
/** The project the record was written under: the card names it relative (CR082). */
|
|
590
|
+
readonly projectRoot: string | null;
|
|
591
|
+
/** What the writer did with the record (CR079); absent when there is no artifact path. */
|
|
592
|
+
readonly coherenceArtifactOutcome?: ArtifactOutcome | null;
|
|
593
|
+
readonly nextEvent: string;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
export interface CoherenceOptions {
|
|
597
|
+
readonly journey: string;
|
|
598
|
+
readonly method: MethodDefinition;
|
|
599
|
+
readonly processAlignment?: string | null;
|
|
600
|
+
readonly projectAlignment?: string | null;
|
|
601
|
+
readonly productAlignment?: string | null;
|
|
602
|
+
readonly localDifferences?: readonly string[];
|
|
603
|
+
readonly coherenceArtifactPath?: string | null;
|
|
604
|
+
/** The project the artifact must stay inside (CR079). */
|
|
605
|
+
readonly projectRoot?: string | null;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/** Python `coherence_lifecycle_item`. */
|
|
609
|
+
export function coherenceLifecycleItem(
|
|
610
|
+
db: WritableDatabase,
|
|
611
|
+
options: CoherenceOptions,
|
|
612
|
+
deps: CursorWriteDeps,
|
|
613
|
+
): BuilderCoherenceReport {
|
|
614
|
+
const journey = normalizeRequired(options.journey, "journey");
|
|
615
|
+
const existing = getDeliveryCursor(db, journey);
|
|
616
|
+
if (existing === null) throw new Error("delivery cursor is required before coherence");
|
|
617
|
+
if (!existing.activeItem) throw new Error("active item is required before coherence");
|
|
618
|
+
refuseIfAlreadyComplete("coherence", existing);
|
|
619
|
+
|
|
620
|
+
const reentering =
|
|
621
|
+
existing.pendingConfirmation === "navigator_coherence" &&
|
|
622
|
+
existing.lastDeliveryEvent === "coherence";
|
|
623
|
+
if (existing.pendingConfirmation && !reentering) {
|
|
624
|
+
throw new LifecycleRefusal(
|
|
625
|
+
"coherence",
|
|
626
|
+
"pending_confirmation",
|
|
627
|
+
`Coherence is blocked: pending confirmation ${existing.pendingConfirmation}.`,
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
if (existing.lastDeliveryEvent !== "review_complete" && !reentering) {
|
|
631
|
+
throw new LifecycleRefusal(
|
|
632
|
+
"coherence",
|
|
633
|
+
"not_reached",
|
|
634
|
+
"Coherence requires completed Debt Review",
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const process = textOr(
|
|
639
|
+
options.processAlignment,
|
|
640
|
+
"Process evidence is aligned with the Ariad lifecycle.",
|
|
641
|
+
);
|
|
642
|
+
const project = textOr(
|
|
643
|
+
options.projectAlignment,
|
|
644
|
+
"Project docs and artifacts reflect the validated change.",
|
|
645
|
+
);
|
|
646
|
+
const product = textOr(
|
|
647
|
+
options.productAlignment,
|
|
648
|
+
"Product behavior matches the accepted validation evidence.",
|
|
649
|
+
);
|
|
650
|
+
const differences = normalizedList(options.localDifferences ?? []);
|
|
651
|
+
// Python `_coherence_missing`: a blank string defaults ABOVE, so the only way to
|
|
652
|
+
// reach these is an argument that was whitespace before the default could apply.
|
|
653
|
+
const missing: string[] = [];
|
|
654
|
+
if (!pyStrip(process)) missing.push("process alignment is missing");
|
|
655
|
+
if (!pyStrip(project)) missing.push("project alignment is missing");
|
|
656
|
+
if (!pyStrip(product)) missing.push("product alignment is missing");
|
|
657
|
+
|
|
658
|
+
const cursor = setDeliveryCursor(
|
|
659
|
+
db,
|
|
660
|
+
{
|
|
661
|
+
journey,
|
|
662
|
+
method: options.method.id,
|
|
663
|
+
activeItem: existing.activeItem,
|
|
664
|
+
activeItemTitle: existing.activeItemTitle,
|
|
665
|
+
activeItemLevel: existing.activeItemLevel,
|
|
666
|
+
activeCheckpoint: missing.length > 0 ? "coherence" : null,
|
|
667
|
+
pendingConfirmation: missing.length > 0 ? "navigator_coherence" : null,
|
|
668
|
+
lastDeliveryEvent: missing.length > 0 ? "coherence" : "coherence_complete",
|
|
669
|
+
cadenceProfile: existing.cadenceProfile,
|
|
670
|
+
cadenceLimits: existing.cadenceLimits,
|
|
671
|
+
granularityDecision: existing.granularityDecision,
|
|
672
|
+
navigatorFlowUnit: existing.navigatorFlowUnit,
|
|
673
|
+
childWorkItems: existing.childWorkItems,
|
|
674
|
+
aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
|
|
675
|
+
},
|
|
676
|
+
deps,
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
const report: BuilderCoherenceReport = {
|
|
680
|
+
journey,
|
|
681
|
+
method: options.method.id,
|
|
682
|
+
activeItem: existing.activeItem,
|
|
683
|
+
activeItemTitle: existing.activeItemTitle,
|
|
684
|
+
processAlignment: process,
|
|
685
|
+
projectAlignment: project,
|
|
686
|
+
productAlignment: product,
|
|
687
|
+
localDifferences: differences,
|
|
688
|
+
missingCoherence: missing,
|
|
689
|
+
coherenceContract: contractFor(options.method, "coherence_contract"),
|
|
690
|
+
cursor,
|
|
691
|
+
coherenceArtifactPath: options.coherenceArtifactPath ?? null,
|
|
692
|
+
projectRoot: options.projectRoot ?? null,
|
|
693
|
+
nextEvent: "done",
|
|
694
|
+
};
|
|
695
|
+
if (report.coherenceArtifactPath === null) return report;
|
|
696
|
+
const coherenceArtifactOutcome = writeClosureArtifact(
|
|
697
|
+
report.coherenceArtifactPath,
|
|
698
|
+
renderCoherenceArtifact(report),
|
|
699
|
+
options.projectRoot,
|
|
700
|
+
);
|
|
701
|
+
return { ...report, coherenceArtifactOutcome };
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/** Python `render_coherence_checkpoint`: note the ribbon says `done`, not `coherence`. */
|
|
705
|
+
export function renderCoherenceCheckpoint(report: BuilderCoherenceReport): string {
|
|
706
|
+
const blocked = report.missingCoherence.length > 0;
|
|
707
|
+
const body = `${[
|
|
708
|
+
"Delivery",
|
|
709
|
+
renderLifecycleRibbon("done", report.cursor.activeItemLevel),
|
|
710
|
+
"",
|
|
711
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
712
|
+
"│ ◉■ COHERENCE CHECKPOINT │",
|
|
713
|
+
"│ │",
|
|
714
|
+
cardText("active item"),
|
|
715
|
+
cardText(report.activeItem),
|
|
716
|
+
"│ │",
|
|
717
|
+
cardText("status"),
|
|
718
|
+
cardText(blocked ? "pending_coherence" : "coherent"),
|
|
719
|
+
"│ │",
|
|
720
|
+
cardText("process alignment"),
|
|
721
|
+
...cardWrapped(report.processAlignment),
|
|
722
|
+
"│ │",
|
|
723
|
+
cardText("project alignment"),
|
|
724
|
+
...cardWrapped(report.projectAlignment),
|
|
725
|
+
"│ │",
|
|
726
|
+
cardText("product alignment"),
|
|
727
|
+
...cardWrapped(report.productAlignment),
|
|
728
|
+
"│ │",
|
|
729
|
+
cardText("local guide differences"),
|
|
730
|
+
...cardPrefixed(report.localDifferences.length > 0 ? report.localDifferences : ["none"], "△"),
|
|
731
|
+
"│ │",
|
|
732
|
+
cardText("missing coherence"),
|
|
733
|
+
...cardPrefixed(blocked ? report.missingCoherence : ["none"], blocked ? "✕" : "✓"),
|
|
734
|
+
"│ │",
|
|
735
|
+
cardText("coherence contract"),
|
|
736
|
+
...cardPrefixed(report.coherenceContract.rules, "✓"),
|
|
737
|
+
"│ │",
|
|
738
|
+
cardText("coherence artifact"),
|
|
739
|
+
...recordRows(report.coherenceArtifactPath, report.projectRoot, report.journey),
|
|
740
|
+
...preservedNote(report.coherenceArtifactOutcome),
|
|
741
|
+
"│ │",
|
|
742
|
+
cardText("boundary"),
|
|
743
|
+
...cardWrapped(
|
|
744
|
+
blocked
|
|
745
|
+
? "Do not move to Done until coherence gaps are resolved."
|
|
746
|
+
: "Coherence is complete; Builder may proceed to Done.",
|
|
747
|
+
),
|
|
748
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
749
|
+
].join("\n")}\n`;
|
|
750
|
+
return wrapAriadSurface("coherence_checkpoint", body);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// --- Done ------------------------------------------------------------------
|
|
754
|
+
|
|
755
|
+
export interface BuilderDoneReport {
|
|
756
|
+
readonly journey: string;
|
|
757
|
+
readonly method: string;
|
|
758
|
+
readonly activeItem: string;
|
|
759
|
+
readonly activeItemTitle: string | null;
|
|
760
|
+
readonly historyAction: string;
|
|
761
|
+
readonly roadmapUpdate: string;
|
|
762
|
+
readonly nextRecommendation: string;
|
|
763
|
+
readonly missingDone: readonly string[];
|
|
764
|
+
readonly doneContract: ContractDefinition;
|
|
765
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
766
|
+
readonly doneArtifactPath: string | null;
|
|
767
|
+
/** The project the record was written under: the card names it relative (CR082). */
|
|
768
|
+
readonly projectRoot: string | null;
|
|
769
|
+
/** What the writer did with the record (CR079); absent when there is no artifact path. */
|
|
770
|
+
readonly doneArtifactOutcome?: ArtifactOutcome | null;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export interface DoneOptions {
|
|
774
|
+
readonly journey: string;
|
|
775
|
+
readonly method: MethodDefinition;
|
|
776
|
+
readonly historyAction?: string | null;
|
|
777
|
+
readonly roadmapUpdate?: string | null;
|
|
778
|
+
readonly nextRecommendation?: string | null;
|
|
779
|
+
readonly doneArtifactPath?: string | null;
|
|
780
|
+
/** The project the artifact must stay inside (CR079). */
|
|
781
|
+
readonly projectRoot?: string | null;
|
|
782
|
+
/**
|
|
783
|
+
* The story's `index.md` (CR112). Done refuses while its placeholder sections are
|
|
784
|
+
* still the scaffold's; `null` when there is no project, and no file to read.
|
|
785
|
+
* Required, not optional: `continue-lifecycle` once reached Done without it, and a
|
|
786
|
+
* story closed with its record still the scaffold (CR112's handoff review).
|
|
787
|
+
*/
|
|
788
|
+
readonly indexArtifactPath: string | null;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* CR112, decision D4: a story does not close while its own record still says what
|
|
793
|
+
* Expand or Plan wrote -- "I want to <title>". Only the placeholder headings the file
|
|
794
|
+
* has are judged, so an index in the Driver's own structure is never refused.
|
|
795
|
+
*/
|
|
796
|
+
function refuseUnauthoredIndex(indexPath: string | null, projectRoot: string | null): void {
|
|
797
|
+
if (indexPath === null) return;
|
|
798
|
+
const verdict = artifactState("index.md", indexPath);
|
|
799
|
+
if (verdict.toAuthor.length === 0) return;
|
|
800
|
+
throw new LifecycleRefusal(
|
|
801
|
+
"done",
|
|
802
|
+
"missing_evidence",
|
|
803
|
+
`Done needs the story's own record. Still to author in ${displayPath(indexPath, projectRoot ?? null)}: ${verdict.toAuthor.join(", ")}.`,
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/** Python `done_lifecycle_item`. */
|
|
808
|
+
export function doneLifecycleItem(
|
|
809
|
+
db: WritableDatabase,
|
|
810
|
+
options: DoneOptions,
|
|
811
|
+
deps: CursorWriteDeps,
|
|
812
|
+
): BuilderDoneReport {
|
|
813
|
+
const journey = normalizeRequired(options.journey, "journey");
|
|
814
|
+
const existing = getDeliveryCursor(db, journey);
|
|
815
|
+
if (existing === null) throw new Error("delivery cursor is required before done");
|
|
816
|
+
if (!existing.activeItem) throw new Error("active item is required before done");
|
|
817
|
+
refuseIfAlreadyComplete("done", existing);
|
|
818
|
+
// No re-entry: Done refuses every pending confirmation, including the ones its own
|
|
819
|
+
// predecessors left behind.
|
|
820
|
+
if (existing.pendingConfirmation) {
|
|
821
|
+
throw new LifecycleRefusal(
|
|
822
|
+
"done",
|
|
823
|
+
"pending_confirmation",
|
|
824
|
+
`Done is blocked: pending confirmation ${existing.pendingConfirmation}.`,
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
// `review_complete` is accepted DIRECTLY: Coherence is an option, not a
|
|
828
|
+
// precondition. A port that requires `coherence_complete` blocks a legal closure.
|
|
829
|
+
if (
|
|
830
|
+
existing.lastDeliveryEvent !== "review_complete" &&
|
|
831
|
+
existing.lastDeliveryEvent !== "coherence_complete"
|
|
832
|
+
) {
|
|
833
|
+
throw new LifecycleRefusal("done", "not_reached", "Done requires completed Debt Review");
|
|
834
|
+
}
|
|
835
|
+
refuseUnauthoredIndex(options.indexArtifactPath, options.projectRoot ?? null);
|
|
836
|
+
|
|
837
|
+
const history = textOr(
|
|
838
|
+
options.historyAction,
|
|
839
|
+
"History action recorded according to project policy.",
|
|
840
|
+
);
|
|
841
|
+
const roadmap = textOr(options.roadmapUpdate, "Roadmap/story package reflects the closed story.");
|
|
842
|
+
const nextStep = textOr(
|
|
843
|
+
options.nextRecommendation,
|
|
844
|
+
"Inspect pull candidates for the next Ariad movement.",
|
|
845
|
+
);
|
|
846
|
+
const missing: string[] = [];
|
|
847
|
+
if (!pyStrip(history)) missing.push("history action is missing");
|
|
848
|
+
if (!pyStrip(roadmap)) missing.push("roadmap update is missing");
|
|
849
|
+
if (!pyStrip(nextStep)) missing.push("next recommendation is missing");
|
|
850
|
+
|
|
851
|
+
const cursor = setDeliveryCursor(
|
|
852
|
+
db,
|
|
853
|
+
{
|
|
854
|
+
journey,
|
|
855
|
+
method: options.method.id,
|
|
856
|
+
activeItem: existing.activeItem,
|
|
857
|
+
activeItemTitle: existing.activeItemTitle,
|
|
858
|
+
activeItemLevel: existing.activeItemLevel,
|
|
859
|
+
activeCheckpoint: missing.length > 0 ? "done" : null,
|
|
860
|
+
pendingConfirmation: missing.length > 0 ? "navigator_done" : null,
|
|
861
|
+
lastDeliveryEvent: missing.length > 0 ? "done" : "done_complete",
|
|
862
|
+
cadenceProfile: existing.cadenceProfile,
|
|
863
|
+
cadenceLimits: existing.cadenceLimits,
|
|
864
|
+
granularityDecision: existing.granularityDecision,
|
|
865
|
+
navigatorFlowUnit: existing.navigatorFlowUnit,
|
|
866
|
+
childWorkItems: existing.childWorkItems,
|
|
867
|
+
aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
|
|
868
|
+
},
|
|
869
|
+
deps,
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
const report: BuilderDoneReport = {
|
|
873
|
+
journey,
|
|
874
|
+
method: options.method.id,
|
|
875
|
+
activeItem: existing.activeItem,
|
|
876
|
+
activeItemTitle: existing.activeItemTitle,
|
|
877
|
+
historyAction: history,
|
|
878
|
+
roadmapUpdate: roadmap,
|
|
879
|
+
nextRecommendation: nextStep,
|
|
880
|
+
missingDone: missing,
|
|
881
|
+
doneContract: contractFor(options.method, "done_contract"),
|
|
882
|
+
cursor,
|
|
883
|
+
doneArtifactPath: options.doneArtifactPath ?? null,
|
|
884
|
+
projectRoot: options.projectRoot ?? null,
|
|
885
|
+
};
|
|
886
|
+
if (report.doneArtifactPath === null) return report;
|
|
887
|
+
const doneArtifactOutcome = writeClosureArtifact(
|
|
888
|
+
report.doneArtifactPath,
|
|
889
|
+
renderDoneArtifact(report),
|
|
890
|
+
options.projectRoot,
|
|
891
|
+
);
|
|
892
|
+
return { ...report, doneArtifactOutcome };
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/** Python `render_done_checkpoint`. */
|
|
896
|
+
export function renderDoneCheckpoint(report: BuilderDoneReport): string {
|
|
897
|
+
const blocked = report.missingDone.length > 0;
|
|
898
|
+
const body = `${[
|
|
899
|
+
"Delivery",
|
|
900
|
+
renderLifecycleRibbon("done", report.cursor.activeItemLevel),
|
|
901
|
+
"",
|
|
902
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
903
|
+
"│ 🟩■ DONE CHECKPOINT │",
|
|
904
|
+
"│ │",
|
|
905
|
+
cardText("active item"),
|
|
906
|
+
cardText(report.activeItem),
|
|
907
|
+
"│ │",
|
|
908
|
+
cardText("status"),
|
|
909
|
+
cardText(blocked ? "pending_done" : "done"),
|
|
910
|
+
"│ │",
|
|
911
|
+
cardText("history action"),
|
|
912
|
+
...cardWrapped(report.historyAction),
|
|
913
|
+
"│ │",
|
|
914
|
+
cardText("roadmap update"),
|
|
915
|
+
...cardWrapped(report.roadmapUpdate),
|
|
916
|
+
"│ │",
|
|
917
|
+
cardText("next recommendation"),
|
|
918
|
+
...cardWrapped(report.nextRecommendation),
|
|
919
|
+
"│ │",
|
|
920
|
+
cardText("missing done"),
|
|
921
|
+
...cardPrefixed(blocked ? report.missingDone : ["none"], blocked ? "✕" : "✓"),
|
|
922
|
+
"│ │",
|
|
923
|
+
cardText("done contract"),
|
|
924
|
+
...cardPrefixed(report.doneContract.rules, "✓"),
|
|
925
|
+
"│ │",
|
|
926
|
+
cardText("done artifact"),
|
|
927
|
+
...recordRows(report.doneArtifactPath, report.projectRoot, report.journey),
|
|
928
|
+
...preservedNote(report.doneArtifactOutcome),
|
|
929
|
+
"│ │",
|
|
930
|
+
cardText("boundary"),
|
|
931
|
+
...cardWrapped(
|
|
932
|
+
blocked
|
|
933
|
+
? "Do not close the story until Done gaps are resolved."
|
|
934
|
+
: "Story closure is complete; Builder may recommend the next Pull.",
|
|
935
|
+
),
|
|
936
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
937
|
+
].join("\n")}\n`;
|
|
938
|
+
return wrapAriadSurface("done_checkpoint", body);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* A closure record's rows: the project and journey it was written for (CR009), then its
|
|
943
|
+
* path relative to that project (CR082), or `not materialized` when the journey has no
|
|
944
|
+
* project, and nothing was written.
|
|
945
|
+
*/
|
|
946
|
+
function recordRows(path: string | null, projectRoot: string | null, journey: string): string[] {
|
|
947
|
+
if (path === null) return cardWrapped("not materialized");
|
|
948
|
+
const target = projectRoot === null ? [] : cardWrapped(targetLine(projectRoot, journey));
|
|
949
|
+
return [...target, ...cardWrapped(displayPath(path, projectRoot))];
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* CR079: said only when a closure record was preserved rather than written, the
|
|
954
|
+
* one case a reader cannot assume. Created and updated records hold this
|
|
955
|
+
* checkpoint's fields, as they always have.
|
|
956
|
+
*/
|
|
957
|
+
function preservedNote(outcome: ArtifactOutcome | null | undefined): string[] {
|
|
958
|
+
return outcome === "preserved"
|
|
959
|
+
? cardWrapped(
|
|
960
|
+
"preserved as it is: Ariad did not write this file, or it was edited since. " +
|
|
961
|
+
"This checkpoint's fields are shown here, not written to it.",
|
|
962
|
+
)
|
|
963
|
+
: [];
|
|
964
|
+
}
|