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,408 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 3 — Plan: the checkpoint that blocks implementation.
|
|
2
|
+
//
|
|
3
|
+
// Port of `plan_lifecycle_item` and `render_plan_checkpoint` from
|
|
4
|
+
// `src/memory/builder/lifecycle.py`.
|
|
5
|
+
//
|
|
6
|
+
// Three behaviors carry the meaning, and two of them are invisible in the surface:
|
|
7
|
+
//
|
|
8
|
+
// 1. **The receipt argument is always EXPLICIT.** Plan passes the receipt it
|
|
9
|
+
// resolved — new, or the existing one — which bypasses the cursor's
|
|
10
|
+
// coordinate-change invalidation. That is what lets a freshly recorded receipt
|
|
11
|
+
// survive the very write that records it; passing the KEEP sentinel here would
|
|
12
|
+
// invalidate it on arrival.
|
|
13
|
+
// 2. **The generation does NOT advance.** Plan is a checkpoint on the item Pull
|
|
14
|
+
// committed to, and a bump would invalidate the receipt it just wrote.
|
|
15
|
+
// 3. **The scaffold is composed here, from one vocabulary (CR111 D3).** Plan fills
|
|
16
|
+
// `PRODUCT_PLAN` with the caller's title, else the title Pull recorded, else the
|
|
17
|
+
// item's code (D5), and names the siblings the caller read from the roadmap as
|
|
18
|
+
// Non-Goals. A caller may set only the objective (`--objective`). Python took each
|
|
19
|
+
// section as an argument and fell back, per field, to a second set of sentences
|
|
20
|
+
// no product path wrote; the recorded corpus graded that set.
|
|
21
|
+
//
|
|
22
|
+
// CR111: the card prints what the runtime knows, and no section of the plan. It used
|
|
23
|
+
// to print the scaffold's sentences as the plan -- scope, acceptance, validation,
|
|
24
|
+
// checked off -- even over a plan.md a person had written. It now names the story
|
|
25
|
+
// files, each with its state and the sections it still needs, in the rows `build show`
|
|
26
|
+
// prints, judged once here, right after the write, so the command and the recorded
|
|
27
|
+
// corpus print the same card.
|
|
28
|
+
//
|
|
29
|
+
// CR082: the card names the package relative to the project, and the four `*_path=`
|
|
30
|
+
// lines, the agent's, stay absolute and print below the surface's end marker (D1).
|
|
31
|
+
|
|
32
|
+
import { dirname } from "node:path";
|
|
33
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
34
|
+
import { STORY_SCOPED_COMMIT_RULE, writeStoryPackage } from "./artifacts/planArtifacts.ts";
|
|
35
|
+
import {
|
|
36
|
+
fill,
|
|
37
|
+
fillPlanVocabulary,
|
|
38
|
+
type PlanVocabulary,
|
|
39
|
+
PRODUCT_PLAN,
|
|
40
|
+
SIBLING_NON_GOAL,
|
|
41
|
+
} from "./artifacts/scaffoldSections.ts";
|
|
42
|
+
import type { ArtifactVerdict } from "./artifacts/scaffoldState.ts";
|
|
43
|
+
import { judgeStoryFiles, storyFileLines } from "./artifacts/storyFiles.ts";
|
|
44
|
+
import { cardPrefixed, cardText, cardWrapped } from "./card.ts";
|
|
45
|
+
import { isImplementableByDefault, normalizeRequired } from "./cursorTransitions.ts";
|
|
46
|
+
import {
|
|
47
|
+
type BuilderDeliveryCursor,
|
|
48
|
+
type CursorWriteDeps,
|
|
49
|
+
getDeliveryCursor,
|
|
50
|
+
type PlanPreauthorizationReceipt,
|
|
51
|
+
setDeliveryCursor,
|
|
52
|
+
setTo,
|
|
53
|
+
} from "./deliveryCursor.ts";
|
|
54
|
+
import { effectiveNavigatorFlowUnit, FLOW_UNIT_STORY_BY_STORY } from "./flowUnit.ts";
|
|
55
|
+
import { LifecycleRefusal, refuseIfAlreadyComplete } from "./lifecycleRefusal.ts";
|
|
56
|
+
import { renderLifecycleRibbon } from "./lifecycleRibbon.ts";
|
|
57
|
+
import type { ContractDefinition, MethodDefinition } from "./methodDefinition.ts";
|
|
58
|
+
import {
|
|
59
|
+
createPlanPreauthorizationReceipt,
|
|
60
|
+
PREAUTHORIZATION_STOP,
|
|
61
|
+
STORY_PLAN_CONTRACT,
|
|
62
|
+
} from "./planPreauthorization.ts";
|
|
63
|
+
import { displayPath } from "./projectPaths.ts";
|
|
64
|
+
import { wrapAriadSurface } from "./surfaceProtocol.ts";
|
|
65
|
+
|
|
66
|
+
/** Python `BuilderPlanReport`. */
|
|
67
|
+
export interface BuilderPlanReport {
|
|
68
|
+
readonly journey: string;
|
|
69
|
+
readonly method: string;
|
|
70
|
+
readonly activeItem: string;
|
|
71
|
+
readonly activeItemTitle: string | null;
|
|
72
|
+
readonly activeItemLevel: string | null;
|
|
73
|
+
readonly implementableByDefault: boolean;
|
|
74
|
+
readonly planContract: ContractDefinition;
|
|
75
|
+
readonly implementContract: ContractDefinition;
|
|
76
|
+
readonly validationContract: ContractDefinition;
|
|
77
|
+
readonly localRules: readonly string[];
|
|
78
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
79
|
+
readonly planArtifactPath: string | null;
|
|
80
|
+
/** The project Plan wrote under: the card names the package relative to it (CR082). */
|
|
81
|
+
readonly projectRoot: string | null;
|
|
82
|
+
/**
|
|
83
|
+
* The package's `index.md`, `plan.md`, and `test-guide.md`, judged right after Plan
|
|
84
|
+
* wrote what was missing (CR111); `null` when the journey has no project, so nothing
|
|
85
|
+
* was written.
|
|
86
|
+
*/
|
|
87
|
+
readonly storyFiles: readonly ArtifactVerdict[] | null;
|
|
88
|
+
/**
|
|
89
|
+
* An `--objective` given where nothing is written: with no project there is no
|
|
90
|
+
* plan.md to hold it, and the card says so rather than drop it silently (CR111's
|
|
91
|
+
* handoff review, finding 2).
|
|
92
|
+
*/
|
|
93
|
+
readonly unrecordedObjective: boolean;
|
|
94
|
+
readonly preauthorizationRecorded: boolean;
|
|
95
|
+
readonly nextEvent: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Python `_contract_for`: a missing contract is a method-definition defect, not a default. */
|
|
99
|
+
function contractFor(method: MethodDefinition, contractId: string): ContractDefinition {
|
|
100
|
+
const contract = method.contracts.find((candidate) => candidate.id === contractId);
|
|
101
|
+
if (contract === undefined) {
|
|
102
|
+
throw new Error(`method ${method.id} is missing contract ${contractId}`);
|
|
103
|
+
}
|
|
104
|
+
return contract;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Python `_cadence_preauthorizes_story_plan`.
|
|
109
|
+
*
|
|
110
|
+
* Keyed on the profile's POLICY, not on its name. `accelerated` is the profile that
|
|
111
|
+
* carries `bounded_story_authority` today, but a method that renames it or adds
|
|
112
|
+
* another profile with the same policy gets the same behavior — which is why the
|
|
113
|
+
* Python test asserts the policy rather than the profile id.
|
|
114
|
+
*/
|
|
115
|
+
function cadencePreauthorizesStoryPlan(
|
|
116
|
+
method: MethodDefinition,
|
|
117
|
+
cadenceProfile: string | null,
|
|
118
|
+
): boolean {
|
|
119
|
+
if (cadenceProfile === null) return false;
|
|
120
|
+
const profile = method.cadenceProfiles.find((candidate) => candidate.id === cadenceProfile);
|
|
121
|
+
return profile !== undefined && profile.planApprovalPolicy === "bounded_story_authority";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface PlanOptions {
|
|
125
|
+
readonly journey: string;
|
|
126
|
+
readonly method: MethodDefinition;
|
|
127
|
+
/** `--objective`: the one section a caller may write. Authored by construction. */
|
|
128
|
+
readonly objective?: string | null;
|
|
129
|
+
/**
|
|
130
|
+
* The item's title as the roadmap names it. Without one, the scaffold takes the
|
|
131
|
+
* title Pull recorded on the cursor, and only then the item's code (CR111 D5).
|
|
132
|
+
*/
|
|
133
|
+
readonly title?: string | null;
|
|
134
|
+
/** The titles of the item's siblings in the roadmap, named as Non-Goals. */
|
|
135
|
+
readonly siblings?: readonly string[];
|
|
136
|
+
readonly localRules?: readonly string[];
|
|
137
|
+
readonly planArtifactPath?: string | null;
|
|
138
|
+
/** The project the package must stay inside (CR079). */
|
|
139
|
+
readonly projectRoot?: string | null;
|
|
140
|
+
readonly preauthorize?: boolean;
|
|
141
|
+
readonly stopBoundary?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The sentences Plan writes where no file exists: `PRODUCT_PLAN`, filled with the
|
|
146
|
+
* caller's title, else the title Pull recorded, else the item's code (CR111 D5), with
|
|
147
|
+
* the roadmap's siblings as Non-Goals and the caller's objective when it gave one.
|
|
148
|
+
*/
|
|
149
|
+
function composeScaffold(
|
|
150
|
+
options: PlanOptions,
|
|
151
|
+
recordedTitle: string | null,
|
|
152
|
+
code: string,
|
|
153
|
+
): PlanVocabulary {
|
|
154
|
+
const vocabulary = fillPlanVocabulary(PRODUCT_PLAN, {
|
|
155
|
+
title: options.title || recordedTitle || code,
|
|
156
|
+
});
|
|
157
|
+
const siblings = (options.siblings ?? []).map((sibling) =>
|
|
158
|
+
fill(SIBLING_NON_GOAL, { title: sibling }),
|
|
159
|
+
);
|
|
160
|
+
return {
|
|
161
|
+
...vocabulary,
|
|
162
|
+
objective: options.objective || vocabulary.objective,
|
|
163
|
+
nonGoals: siblings.length > 0 ? siblings : vocabulary.nonGoals,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Python `plan_lifecycle_item`. */
|
|
168
|
+
export function planLifecycleItem(
|
|
169
|
+
db: WritableDatabase,
|
|
170
|
+
options: PlanOptions,
|
|
171
|
+
deps: CursorWriteDeps,
|
|
172
|
+
): BuilderPlanReport {
|
|
173
|
+
const journey = normalizeRequired(options.journey, "journey");
|
|
174
|
+
const method = options.method;
|
|
175
|
+
|
|
176
|
+
const existing = getDeliveryCursor(db, journey);
|
|
177
|
+
if (existing === null) throw new Error("delivery cursor is required before plan");
|
|
178
|
+
if (!existing.activeItem) throw new Error("active item is required before plan");
|
|
179
|
+
refuseIfAlreadyComplete("plan", existing);
|
|
180
|
+
if (existing.lastDeliveryEvent !== "prepare") {
|
|
181
|
+
throw new LifecycleRefusal("plan", "not_reached", "Prepare must be completed before Plan");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const implementable = isImplementableByDefault(existing.activeItemLevel);
|
|
185
|
+
if (!implementable) {
|
|
186
|
+
throw new LifecycleRefusal(
|
|
187
|
+
"plan",
|
|
188
|
+
"not_reached",
|
|
189
|
+
"Plan requires a User Story or Technical Story; expand the Delivery Story first",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const stopBoundary = options.stopBoundary ?? PREAUTHORIZATION_STOP;
|
|
194
|
+
const artifactPath = options.planArtifactPath ?? null;
|
|
195
|
+
let flowUnit = existing.navigatorFlowUnit;
|
|
196
|
+
let receipt: PlanPreauthorizationReceipt | null = existing.planPreauthorization;
|
|
197
|
+
const preauthorizationRecorded =
|
|
198
|
+
(options.preauthorize ?? false) ||
|
|
199
|
+
cadencePreauthorizesStoryPlan(method, existing.cadenceProfile);
|
|
200
|
+
|
|
201
|
+
if (preauthorizationRecorded) {
|
|
202
|
+
flowUnit = effectiveNavigatorFlowUnit(existing).flowUnit;
|
|
203
|
+
if (flowUnit !== FLOW_UNIT_STORY_BY_STORY) {
|
|
204
|
+
// Reachable only through a cursor written before CR117, when Pull carried a Delivery
|
|
205
|
+
// Story's unit to the story after it: the remedy is the Pull that no longer carries.
|
|
206
|
+
throw new Error(
|
|
207
|
+
"story Plan preauthorization requires story_by_story flow; the cursor holds " +
|
|
208
|
+
"delivery_story from a Delivery Story pulled before this story. Pull the story " +
|
|
209
|
+
"again to start it in story_by_story flow.",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
receipt = createPlanPreauthorizationReceipt(
|
|
213
|
+
{ ...existing, navigatorFlowUnit: flowUnit },
|
|
214
|
+
{
|
|
215
|
+
method: method.id,
|
|
216
|
+
planContractVersion: STORY_PLAN_CONTRACT,
|
|
217
|
+
stopBoundary,
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const cursor = setDeliveryCursor(
|
|
223
|
+
db,
|
|
224
|
+
{
|
|
225
|
+
journey,
|
|
226
|
+
method: method.id,
|
|
227
|
+
activeItem: existing.activeItem,
|
|
228
|
+
activeItemTitle: existing.activeItemTitle,
|
|
229
|
+
activeItemLevel: existing.activeItemLevel,
|
|
230
|
+
activeCheckpoint: "after_plan",
|
|
231
|
+
pendingConfirmation: "navigator_approval",
|
|
232
|
+
lastDeliveryEvent: "plan",
|
|
233
|
+
cadenceProfile: existing.cadenceProfile,
|
|
234
|
+
cadenceLimits: existing.cadenceLimits,
|
|
235
|
+
granularityDecision: null,
|
|
236
|
+
navigatorFlowUnit: flowUnit,
|
|
237
|
+
childWorkItems: existing.childWorkItems,
|
|
238
|
+
aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
|
|
239
|
+
cursorGeneration: existing.cursorGeneration,
|
|
240
|
+
planPreauthorization: setTo(receipt),
|
|
241
|
+
},
|
|
242
|
+
deps,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const report: Omit<BuilderPlanReport, "storyFiles" | "unrecordedObjective"> = {
|
|
246
|
+
journey,
|
|
247
|
+
method: method.id,
|
|
248
|
+
activeItem: existing.activeItem,
|
|
249
|
+
activeItemTitle: existing.activeItemTitle,
|
|
250
|
+
activeItemLevel: existing.activeItemLevel,
|
|
251
|
+
implementableByDefault: implementable,
|
|
252
|
+
planContract: contractFor(method, "plan_contract"),
|
|
253
|
+
implementContract: contractFor(method, "implement_contract"),
|
|
254
|
+
validationContract: contractFor(method, "validation_contract"),
|
|
255
|
+
localRules: options.localRules ?? [],
|
|
256
|
+
cursor,
|
|
257
|
+
planArtifactPath: artifactPath,
|
|
258
|
+
projectRoot: options.projectRoot ?? null,
|
|
259
|
+
preauthorizationRecorded,
|
|
260
|
+
nextEvent: "implement",
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
if (artifactPath !== null) {
|
|
264
|
+
const scaffold = composeScaffold(options, existing.activeItemTitle, existing.activeItem);
|
|
265
|
+
writeStoryPackage(dirname(artifactPath), options.projectRoot, {
|
|
266
|
+
activeItem: report.activeItem,
|
|
267
|
+
activeItemTitle: report.activeItemTitle,
|
|
268
|
+
activeItemLevel: report.activeItemLevel,
|
|
269
|
+
...scaffold,
|
|
270
|
+
localRules: report.localRules,
|
|
271
|
+
implementContract: report.implementContract,
|
|
272
|
+
activeCheckpoint: cursor.activeCheckpoint,
|
|
273
|
+
pendingConfirmation: cursor.pendingConfirmation,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// Judged after the write, so the verdict describes the files as they now stand:
|
|
277
|
+
// the scaffold Plan just wrote, or the file a person wrote before it (CR111).
|
|
278
|
+
const storyFiles = artifactPath === null ? null : judgeStoryFiles(dirname(artifactPath));
|
|
279
|
+
return {
|
|
280
|
+
...report,
|
|
281
|
+
storyFiles,
|
|
282
|
+
unrecordedObjective: artifactPath === null && Boolean(options.objective),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Python `_granularity_message`. */
|
|
287
|
+
function granularityMessage(report: BuilderPlanReport): string {
|
|
288
|
+
return report.implementableByDefault
|
|
289
|
+
? `${report.activeItemLevel ?? "item"} is implementable by default.`
|
|
290
|
+
: "Delivery Story must expand into User Stories and/or Technical Stories before Plan.";
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Python `_plan_next_action`, told by the file (CR111). The words are the skill's:
|
|
295
|
+
* the Driver authors and presents, the Navigator approves. None of them names passing
|
|
296
|
+
* the structural check as the goal. With no project nothing was written, so there is
|
|
297
|
+
* no file to name and the line stays what it was.
|
|
298
|
+
*/
|
|
299
|
+
function planNextAction(report: BuilderPlanReport): string {
|
|
300
|
+
if (report.storyFiles === null) {
|
|
301
|
+
return report.preauthorizationRecorded
|
|
302
|
+
? "Driver completes Plan and consumes bounded authority."
|
|
303
|
+
: "Navigator approves the Plan or requests changes.";
|
|
304
|
+
}
|
|
305
|
+
const authored = report.storyFiles.find((file) => file.name === "plan.md")?.state === "authored";
|
|
306
|
+
if (report.preauthorizationRecorded) {
|
|
307
|
+
return authored
|
|
308
|
+
? "Driver consumes bounded authority."
|
|
309
|
+
: "Driver authors plan.md, then consumes bounded authority.";
|
|
310
|
+
}
|
|
311
|
+
return authored
|
|
312
|
+
? "Navigator reads plan.md and approves it or requests changes."
|
|
313
|
+
: "Driver authors plan.md and presents it; the Navigator approves it or requests changes.";
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The story files block: `build show`'s rows for the same files. With no project
|
|
318
|
+
* nothing was written; the package row says why, and this one says what that cost.
|
|
319
|
+
*/
|
|
320
|
+
function storyFilesBlock(report: BuilderPlanReport): string[] {
|
|
321
|
+
if (report.storyFiles !== null) return report.storyFiles.flatMap(storyFileLines);
|
|
322
|
+
return cardWrapped(
|
|
323
|
+
report.unrecordedObjective
|
|
324
|
+
? "none written, so the --objective given was recorded nowhere"
|
|
325
|
+
: "none written",
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Python `_plan_boundary`. */
|
|
330
|
+
function planBoundary(report: BuilderPlanReport): string {
|
|
331
|
+
return report.preauthorizationRecorded
|
|
332
|
+
? "No additional Navigator approval turn is expected."
|
|
333
|
+
: "Implementation remains blocked until approval.";
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Python `render_plan_checkpoint`, without the plan (CR111).
|
|
338
|
+
*
|
|
339
|
+
* The card prints no section of the plan: not the objective, scope, non-goals,
|
|
340
|
+
* acceptance, or validation, which it used to fill from the scaffold's sentences.
|
|
341
|
+
* In their place it names the story files, each with its state and the sections it
|
|
342
|
+
* still needs, in the rows `build show` prints. The implementation contract stays:
|
|
343
|
+
* its lines are Ariad's rules and the project's own, true whatever the plan says.
|
|
344
|
+
*
|
|
345
|
+
* The card names the package relative to the project, as every card names a path
|
|
346
|
+
* (CR082). The four `*_path=` lines are the agent's: absolute, because an agent often
|
|
347
|
+
* works from a directory that is not the project, and printed only when an artifact
|
|
348
|
+
* path exists. They follow the surface's end marker, where the transport rule that
|
|
349
|
+
* copies a surface into the reply does not carry the owner's home directory (D1), the
|
|
350
|
+
* way `build load` prints `project_path=` outside its card.
|
|
351
|
+
*/
|
|
352
|
+
export function renderPlanCheckpoint(report: BuilderPlanReport): string {
|
|
353
|
+
const written = report.planArtifactPath !== null;
|
|
354
|
+
const packagePath = written ? dirname(report.planArtifactPath as string) : "not written";
|
|
355
|
+
|
|
356
|
+
const card = [
|
|
357
|
+
"Delivery",
|
|
358
|
+
renderLifecycleRibbon("plan", report.activeItemLevel),
|
|
359
|
+
"",
|
|
360
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
361
|
+
"│ 🧭■ PLAN CHECKPOINT │",
|
|
362
|
+
"│ │",
|
|
363
|
+
cardText("item"),
|
|
364
|
+
cardText(`🟦[${report.activeItem}]`),
|
|
365
|
+
cardText(`level: ${report.activeItemLevel ?? "unknown"}`),
|
|
366
|
+
"│ │",
|
|
367
|
+
cardText("story package"),
|
|
368
|
+
...cardWrapped(
|
|
369
|
+
written
|
|
370
|
+
? displayPath(packagePath, report.projectRoot)
|
|
371
|
+
: "none: the journey has no project path",
|
|
372
|
+
),
|
|
373
|
+
"│ │",
|
|
374
|
+
cardText("story files"),
|
|
375
|
+
...storyFilesBlock(report),
|
|
376
|
+
"│ │",
|
|
377
|
+
cardText("granularity"),
|
|
378
|
+
...cardWrapped(granularityMessage(report)),
|
|
379
|
+
"│ │",
|
|
380
|
+
cardText("implementation contract"),
|
|
381
|
+
...cardWrapped("TDD/characterization tests when behavior is testable."),
|
|
382
|
+
...cardWrapped("Keep changes scoped to the active story."),
|
|
383
|
+
...cardWrapped(STORY_SCOPED_COMMIT_RULE),
|
|
384
|
+
// A project without a guide adds nothing, not the card's usual `none` (CR019).
|
|
385
|
+
...(report.localRules.length > 0 ? cardPrefixed(report.localRules, "✓") : []),
|
|
386
|
+
"│ │",
|
|
387
|
+
cardText("approval gate"),
|
|
388
|
+
cardText(`checkpoint: ${report.cursor.activeCheckpoint}`),
|
|
389
|
+
cardText(`pending: ${report.cursor.pendingConfirmation}`),
|
|
390
|
+
"│ │",
|
|
391
|
+
cardText("next action"),
|
|
392
|
+
...cardWrapped(planNextAction(report)),
|
|
393
|
+
"│ │",
|
|
394
|
+
cardText("boundary"),
|
|
395
|
+
...cardWrapped(planBoundary(report)),
|
|
396
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
397
|
+
].join("\n");
|
|
398
|
+
|
|
399
|
+
const trailer = written
|
|
400
|
+
? [
|
|
401
|
+
`story_package_path=${packagePath}\n`,
|
|
402
|
+
`index_artifact_path=${packagePath}/index.md\n`,
|
|
403
|
+
`plan_artifact_path=${report.planArtifactPath as string}\n`,
|
|
404
|
+
`test_guide_artifact_path=${packagePath}/test-guide.md\n`,
|
|
405
|
+
].join("")
|
|
406
|
+
: "";
|
|
407
|
+
return wrapAriadSurface("plan_checkpoint", card) + trailer;
|
|
408
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 3 — bounded authority for conditional Plan approval.
|
|
2
|
+
//
|
|
3
|
+
// Port of `src/memory/builder/plan_preauthorization.py`.
|
|
4
|
+
//
|
|
5
|
+
// This is the security-sensitive module of the plateau. A receipt lets the Driver
|
|
6
|
+
// approve its own Plan once, without another Navigator turn, so every property
|
|
7
|
+
// that bounds it is load-bearing:
|
|
8
|
+
//
|
|
9
|
+
// * the receipt is **payload-free** — it carries coordinates and a hash, never
|
|
10
|
+
// Plan prose, so it cannot smuggle scope past the Navigator;
|
|
11
|
+
// * it is **generation-bound**, so any cursor movement invalidates it;
|
|
12
|
+
// * it is **single-use**, consumed atomically by a compare-and-swap;
|
|
13
|
+
// * a mismatch **falls back to ordinary approval** rather than failing open.
|
|
14
|
+
//
|
|
15
|
+
// The fingerprint is an equality test on authority. If the two engines
|
|
16
|
+
// canonicalize its payload differently, a receipt Python recorded cannot be
|
|
17
|
+
// consumed by TypeScript — the Navigator is asked to approve a Plan they already
|
|
18
|
+
// authorized, which is the safe direction but still a defect. It never fails the
|
|
19
|
+
// other way, because a hash mismatch can only refuse.
|
|
20
|
+
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
|
|
23
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
24
|
+
import { pythonJsonDumpsCanonical } from "#util/pyGenerators.ts";
|
|
25
|
+
import { pyStrip, sortByCodePoint } from "#util/pythonText.ts";
|
|
26
|
+
import {
|
|
27
|
+
type BuilderDeliveryCursor,
|
|
28
|
+
type CursorWriteDeps,
|
|
29
|
+
DeliveryCursorConflict,
|
|
30
|
+
type PlanPreauthorizationReceipt,
|
|
31
|
+
setDeliveryCursor,
|
|
32
|
+
setTo,
|
|
33
|
+
} from "./deliveryCursor.ts";
|
|
34
|
+
|
|
35
|
+
/** Python module constants. */
|
|
36
|
+
export const PREAUTHORIZATION_POLICY = "exact_scope";
|
|
37
|
+
export const PREAUTHORIZATION_STOP = "navigator_validation";
|
|
38
|
+
export const DELIVERY_STORY_PLAN_CONTRACT = "delivery_story_plan@1";
|
|
39
|
+
export const STORY_PLAN_CONTRACT = "story_plan@1";
|
|
40
|
+
|
|
41
|
+
/** Python `PlanPreauthorizationMismatch`. */
|
|
42
|
+
export class PlanPreauthorizationMismatch extends Error {
|
|
43
|
+
readonly reason: string;
|
|
44
|
+
/**
|
|
45
|
+
* For `plan_incomplete`, the sections still to author (CR112). The surface names
|
|
46
|
+
* them; the receipt keeps only the reason token, so a persisted cursor reads back as
|
|
47
|
+
* it always did.
|
|
48
|
+
*/
|
|
49
|
+
readonly sections: readonly string[];
|
|
50
|
+
|
|
51
|
+
constructor(reason: string, sections: readonly string[] = []) {
|
|
52
|
+
super(reason);
|
|
53
|
+
this.name = "PlanPreauthorizationMismatch";
|
|
54
|
+
this.reason = reason;
|
|
55
|
+
this.sections = sections;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ScopeFingerprintInput {
|
|
60
|
+
readonly journey: string;
|
|
61
|
+
readonly method: string;
|
|
62
|
+
readonly cursorGeneration: number;
|
|
63
|
+
readonly activeItem: string;
|
|
64
|
+
readonly activeItemLevel: string;
|
|
65
|
+
readonly flowUnit: string;
|
|
66
|
+
readonly childWorkItems: readonly string[];
|
|
67
|
+
readonly planContractVersion: string;
|
|
68
|
+
readonly stopBoundary: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Python `scope_fingerprint`: sha256 over canonical sorted-compact ASCII JSON. */
|
|
72
|
+
export function scopeFingerprint(input: ScopeFingerprintInput): string {
|
|
73
|
+
const payload = {
|
|
74
|
+
journey: input.journey,
|
|
75
|
+
method: input.method,
|
|
76
|
+
cursor_generation: input.cursorGeneration,
|
|
77
|
+
active_item: input.activeItem,
|
|
78
|
+
active_item_level: input.activeItemLevel,
|
|
79
|
+
flow_unit: input.flowUnit,
|
|
80
|
+
child_work_items: [...input.childWorkItems],
|
|
81
|
+
plan_contract_version: input.planContractVersion,
|
|
82
|
+
policy: PREAUTHORIZATION_POLICY,
|
|
83
|
+
stop_boundary: input.stopBoundary,
|
|
84
|
+
};
|
|
85
|
+
return createHash("sha256").update(pythonJsonDumpsCanonical(payload), "utf8").digest("hex");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Python `canonical_child_scope`: compare as a SET, store deterministically.
|
|
90
|
+
*
|
|
91
|
+
* Sorted and de-duplicated, so child ORDER is presentational while addition or
|
|
92
|
+
* removal is a mismatch — the distinction the Delivery Story flow depends on.
|
|
93
|
+
*/
|
|
94
|
+
export function canonicalChildScope(items: readonly string[]): string[] {
|
|
95
|
+
const trimmed = new Set<string>();
|
|
96
|
+
for (const item of items) {
|
|
97
|
+
const normalized = pyStrip(item);
|
|
98
|
+
if (normalized) trimmed.add(normalized);
|
|
99
|
+
}
|
|
100
|
+
return sortByCodePoint([...trimmed]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface CreateReceiptOptions {
|
|
104
|
+
readonly method: string;
|
|
105
|
+
readonly planContractVersion: string;
|
|
106
|
+
readonly childWorkItems?: readonly string[];
|
|
107
|
+
readonly stopBoundary?: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Python `create_plan_preauthorization_receipt`. */
|
|
111
|
+
export function createPlanPreauthorizationReceipt(
|
|
112
|
+
cursor: BuilderDeliveryCursor,
|
|
113
|
+
options: CreateReceiptOptions,
|
|
114
|
+
): PlanPreauthorizationReceipt {
|
|
115
|
+
if (!cursor.activeItem || !cursor.activeItemLevel || !cursor.navigatorFlowUnit) {
|
|
116
|
+
throw new Error("complete active Plan coordinates are required");
|
|
117
|
+
}
|
|
118
|
+
const stopBoundary = options.stopBoundary ?? PREAUTHORIZATION_STOP;
|
|
119
|
+
if (stopBoundary !== PREAUTHORIZATION_STOP) {
|
|
120
|
+
throw new Error("unsupported Plan preauthorization stop boundary");
|
|
121
|
+
}
|
|
122
|
+
const children = canonicalChildScope(options.childWorkItems ?? []);
|
|
123
|
+
return {
|
|
124
|
+
journey: cursor.journey,
|
|
125
|
+
method: options.method,
|
|
126
|
+
cursorGeneration: cursor.cursorGeneration,
|
|
127
|
+
activeItem: cursor.activeItem,
|
|
128
|
+
activeItemLevel: cursor.activeItemLevel,
|
|
129
|
+
flowUnit: cursor.navigatorFlowUnit,
|
|
130
|
+
childWorkItems: children,
|
|
131
|
+
planContractVersion: options.planContractVersion,
|
|
132
|
+
policy: PREAUTHORIZATION_POLICY,
|
|
133
|
+
stopBoundary,
|
|
134
|
+
scopeFingerprint: scopeFingerprint({
|
|
135
|
+
journey: cursor.journey,
|
|
136
|
+
method: options.method,
|
|
137
|
+
cursorGeneration: cursor.cursorGeneration,
|
|
138
|
+
activeItem: cursor.activeItem,
|
|
139
|
+
activeItemLevel: cursor.activeItemLevel,
|
|
140
|
+
flowUnit: cursor.navigatorFlowUnit,
|
|
141
|
+
childWorkItems: children,
|
|
142
|
+
planContractVersion: options.planContractVersion,
|
|
143
|
+
stopBoundary,
|
|
144
|
+
}),
|
|
145
|
+
status: "pending",
|
|
146
|
+
reason: null,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface MismatchInput {
|
|
151
|
+
readonly journey: string;
|
|
152
|
+
readonly method: string;
|
|
153
|
+
readonly flowUnit: string;
|
|
154
|
+
readonly childWorkItems: readonly string[];
|
|
155
|
+
readonly planContractVersion: string;
|
|
156
|
+
readonly unfilledSections?: readonly string[];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Python `plan_preauthorization_mismatch_reason`: ONE bounded reason, or null.
|
|
161
|
+
*
|
|
162
|
+
* The checks run in Python's order and the FIRST failure wins, because the reason
|
|
163
|
+
* is Navigator-visible: a receipt that is both stale and cross-level reports
|
|
164
|
+
* `cursor_generation_changed`, not `active_item_changed`. The fingerprint is
|
|
165
|
+
* verified last, after the fields it hashes, so a tampered receipt whose fields
|
|
166
|
+
* still agree is caught by the hash rather than by a field comparison.
|
|
167
|
+
*/
|
|
168
|
+
export function planPreauthorizationMismatchReason(
|
|
169
|
+
cursor: BuilderDeliveryCursor,
|
|
170
|
+
input: MismatchInput,
|
|
171
|
+
): string | null {
|
|
172
|
+
const receipt = cursor.planPreauthorization;
|
|
173
|
+
if (receipt === null) return "authorization_missing";
|
|
174
|
+
if (receipt.status !== "pending") return receipt.reason || "authorization_not_pending";
|
|
175
|
+
|
|
176
|
+
const expectedChildren = canonicalChildScope(input.childWorkItems);
|
|
177
|
+
const checks: [boolean, string][] = [
|
|
178
|
+
[receipt.journey === input.journey, "journey_changed"],
|
|
179
|
+
[receipt.method === input.method, "method_changed"],
|
|
180
|
+
[receipt.cursorGeneration === cursor.cursorGeneration, "cursor_generation_changed"],
|
|
181
|
+
[receipt.activeItem === cursor.activeItem, "active_item_changed"],
|
|
182
|
+
[receipt.activeItemLevel === cursor.activeItemLevel, "active_item_level_changed"],
|
|
183
|
+
// Python's chained comparison: the receipt, the cursor, and the caller's flow
|
|
184
|
+
// unit must ALL agree, not just the receipt and the caller.
|
|
185
|
+
[
|
|
186
|
+
receipt.flowUnit === cursor.navigatorFlowUnit && cursor.navigatorFlowUnit === input.flowUnit,
|
|
187
|
+
"flow_unit_changed",
|
|
188
|
+
],
|
|
189
|
+
[sameScope(receipt.childWorkItems, expectedChildren), "child_scope_changed"],
|
|
190
|
+
[receipt.planContractVersion === input.planContractVersion, "plan_contract_changed"],
|
|
191
|
+
[receipt.policy === PREAUTHORIZATION_POLICY, "policy_changed"],
|
|
192
|
+
[receipt.stopBoundary === PREAUTHORIZATION_STOP, "stop_boundary_changed"],
|
|
193
|
+
];
|
|
194
|
+
for (const [matches, reason] of checks) {
|
|
195
|
+
if (!matches) return reason;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const expectedFingerprint = scopeFingerprint({
|
|
199
|
+
journey: receipt.journey,
|
|
200
|
+
method: receipt.method,
|
|
201
|
+
cursorGeneration: receipt.cursorGeneration,
|
|
202
|
+
activeItem: receipt.activeItem,
|
|
203
|
+
activeItemLevel: receipt.activeItemLevel,
|
|
204
|
+
flowUnit: receipt.flowUnit,
|
|
205
|
+
childWorkItems: receipt.childWorkItems,
|
|
206
|
+
planContractVersion: receipt.planContractVersion,
|
|
207
|
+
stopBoundary: receipt.stopBoundary,
|
|
208
|
+
});
|
|
209
|
+
if (receipt.scopeFingerprint !== expectedFingerprint) return "scope_fingerprint_changed";
|
|
210
|
+
if ((input.unfilledSections ?? []).length > 0) return "plan_incomplete";
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function sameScope(left: readonly string[], right: readonly string[]): boolean {
|
|
215
|
+
return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Python `invalidate_plan_preauthorization`.
|
|
220
|
+
*
|
|
221
|
+
* Swallows a `DeliveryCursorConflict` and returns the cursor unchanged: this runs
|
|
222
|
+
* on a refusal path, and failing to record WHY authority was refused must not turn
|
|
223
|
+
* a clean refusal into an error.
|
|
224
|
+
*/
|
|
225
|
+
export function invalidatePlanPreauthorization(
|
|
226
|
+
db: WritableDatabase,
|
|
227
|
+
cursor: BuilderDeliveryCursor,
|
|
228
|
+
reason: string,
|
|
229
|
+
deps: CursorWriteDeps,
|
|
230
|
+
): BuilderDeliveryCursor {
|
|
231
|
+
const receipt = cursor.planPreauthorization;
|
|
232
|
+
if (receipt === null || receipt.status !== "pending") return cursor;
|
|
233
|
+
try {
|
|
234
|
+
return setDeliveryCursor(
|
|
235
|
+
db,
|
|
236
|
+
{
|
|
237
|
+
journey: cursor.journey,
|
|
238
|
+
method: cursor.method,
|
|
239
|
+
activeItem: cursor.activeItem,
|
|
240
|
+
activeItemTitle: cursor.activeItemTitle,
|
|
241
|
+
activeItemLevel: cursor.activeItemLevel,
|
|
242
|
+
activeCheckpoint: cursor.activeCheckpoint,
|
|
243
|
+
pendingConfirmation: cursor.pendingConfirmation,
|
|
244
|
+
lastDeliveryEvent: cursor.lastDeliveryEvent,
|
|
245
|
+
cadenceProfile: cursor.cadenceProfile,
|
|
246
|
+
cadenceLimits: cursor.cadenceLimits,
|
|
247
|
+
granularityDecision: cursor.granularityDecision,
|
|
248
|
+
navigatorFlowUnit: cursor.navigatorFlowUnit,
|
|
249
|
+
childWorkItems: cursor.childWorkItems,
|
|
250
|
+
aggregateCheckpointStatus: cursor.aggregateCheckpointStatus,
|
|
251
|
+
cursorGeneration: cursor.cursorGeneration,
|
|
252
|
+
planPreauthorization: setTo({ ...receipt, status: "invalidated", reason }),
|
|
253
|
+
expectedCursor: cursor,
|
|
254
|
+
},
|
|
255
|
+
deps,
|
|
256
|
+
);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (error instanceof DeliveryCursorConflict) return cursor;
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// The sections a Plan approval requires, `PLAN_APPROVAL_SECTIONS`, and the reader that
|
|
264
|
+
// judges them live in `artifacts/` since CR111: one answer for every surface that says
|
|
265
|
+
// what a `plan.md` still needs, approval included.
|