@stravica-ai/digital-operator-pa 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/bin/desk +9 -0
- package/bin/distill +2 -0
- package/bin/estate-sync +2 -0
- package/bin/launch +2 -0
- package/bin/memory-sync +2 -0
- package/bin/migrate-inspect +2 -0
- package/bin/pa +2 -0
- package/bin/pa-coordinator-hook +2 -0
- package/bin/pa-init +2 -0
- package/bin/research +2 -0
- package/bin/scheduler +2 -0
- package/bin/secrets +2 -0
- package/bin/slice +2 -0
- package/bin/smoke +9 -0
- package/bin/sync +2 -0
- package/dist/engineering-pa-cli/README.md +205 -0
- package/dist/engineering-pa-cli/bin/estate-sync +2 -0
- package/dist/engineering-pa-cli/bin/launch +2 -0
- package/dist/engineering-pa-cli/bin/pa +2 -0
- package/dist/engineering-pa-cli/bin/pa-coordinator-hook +12 -0
- package/dist/engineering-pa-cli/bin/research +2 -0
- package/dist/engineering-pa-cli/bin/scheduler +2 -0
- package/dist/engineering-pa-cli/bin/secrets +2 -0
- package/dist/engineering-pa-cli/bin/smoke +9 -0
- package/dist/engineering-pa-cli/bin/sync +2 -0
- package/dist/engineering-pa-cli/config/coordinator-allowlist.json +118 -0
- package/dist/engineering-pa-cli/docs/coordinator-hook.md +211 -0
- package/dist/engineering-pa-cli/docs/estate-sync.md +412 -0
- package/dist/engineering-pa-cli/docs/gsc.md +175 -0
- package/dist/engineering-pa-cli/docs/lint-linkedin.md +139 -0
- package/dist/engineering-pa-cli/docs/secrets.md +221 -0
- package/dist/engineering-pa-cli/docs/smoke.md +113 -0
- package/dist/engineering-pa-cli/docs/staging-alias.md +114 -0
- package/dist/engineering-pa-cli/index.mjs +17 -0
- package/dist/engineering-pa-cli/package.json +37 -0
- package/dist/engineering-pa-cli/src/autonomy/cli.mjs +329 -0
- package/dist/engineering-pa-cli/src/autonomy/envelope.mjs +301 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/cli.mjs +140 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/config.mjs +134 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/message.mjs +47 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/policy.mjs +211 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/repo-root.mjs +44 -0
- package/dist/engineering-pa-cli/src/coordinator-hook/self.mjs +30 -0
- package/dist/engineering-pa-cli/src/environment/cli.mjs +287 -0
- package/dist/engineering-pa-cli/src/environment/probes.mjs +302 -0
- package/dist/engineering-pa-cli/src/gsc/auth.mjs +148 -0
- package/dist/engineering-pa-cli/src/gsc/cli.mjs +93 -0
- package/dist/engineering-pa-cli/src/gsc/cloudflare.mjs +318 -0
- package/dist/engineering-pa-cli/src/gsc/commands/add-property.mjs +70 -0
- package/dist/engineering-pa-cli/src/gsc/commands/list-properties.mjs +33 -0
- package/dist/engineering-pa-cli/src/gsc/commands/pull-analytics.mjs +130 -0
- package/dist/engineering-pa-cli/src/gsc/commands/submit-sitemap.mjs +41 -0
- package/dist/engineering-pa-cli/src/gsc/commands/verify-via-cf-dns.mjs +189 -0
- package/dist/engineering-pa-cli/src/knowledge/call-log.mjs +59 -0
- package/dist/engineering-pa-cli/src/knowledge/cli.mjs +242 -0
- package/dist/engineering-pa-cli/src/knowledge/eval.mjs +446 -0
- package/dist/engineering-pa-cli/src/knowledge/formatters.mjs +331 -0
- package/dist/engineering-pa-cli/src/knowledge/graph.mjs +710 -0
- package/dist/engineering-pa-cli/src/launch/README.md +214 -0
- package/dist/engineering-pa-cli/src/launch/cli.mjs +51 -0
- package/dist/engineering-pa-cli/src/launch/commands/launch.mjs +106 -0
- package/dist/engineering-pa-cli/src/launch/identity.mjs +43 -0
- package/dist/engineering-pa-cli/src/launch/planner.mjs +44 -0
- package/dist/engineering-pa-cli/src/launch/repo-root.mjs +53 -0
- package/dist/engineering-pa-cli/src/launch/runner.mjs +147 -0
- package/dist/engineering-pa-cli/src/launch/task-loader.mjs +85 -0
- package/dist/engineering-pa-cli/src/lint/cli.mjs +61 -0
- package/dist/engineering-pa-cli/src/lint/commands/linkedin.mjs +133 -0
- package/dist/engineering-pa-cli/src/lint/detectors.mjs +90 -0
- package/dist/engineering-pa-cli/src/lint/linter.mjs +69 -0
- package/dist/engineering-pa-cli/src/lint/reporter.mjs +32 -0
- package/dist/engineering-pa-cli/src/lint/rules.mjs +132 -0
- package/dist/engineering-pa-cli/src/pa/cli.mjs +369 -0
- package/dist/engineering-pa-cli/src/pa/doc-groups.mjs +164 -0
- package/dist/engineering-pa-cli/src/pa/io.mjs +80 -0
- package/dist/engineering-pa-cli/src/pa/project-cli.mjs +385 -0
- package/dist/engineering-pa-cli/src/pa/projects.mjs +352 -0
- package/dist/engineering-pa-cli/src/pa/repo-root.mjs +64 -0
- package/dist/engineering-pa-cli/src/pa/session.mjs +254 -0
- package/dist/engineering-pa-cli/src/pa/work-cli.mjs +164 -0
- package/dist/engineering-pa-cli/src/pa/work.mjs +196 -0
- package/dist/engineering-pa-cli/src/pa/workstreams.mjs +201 -0
- package/dist/engineering-pa-cli/src/research/README.md +360 -0
- package/dist/engineering-pa-cli/src/research/briefs/extractor.md +103 -0
- package/dist/engineering-pa-cli/src/research/briefs/searcher.md +113 -0
- package/dist/engineering-pa-cli/src/research/briefs/synthesizer.md +124 -0
- package/dist/engineering-pa-cli/src/research/briefs/verifier.md +86 -0
- package/dist/engineering-pa-cli/src/research/cli.mjs +141 -0
- package/dist/engineering-pa-cli/src/research/commands/gates.mjs +176 -0
- package/dist/engineering-pa-cli/src/research/commands/list.mjs +56 -0
- package/dist/engineering-pa-cli/src/research/commands/mark.mjs +97 -0
- package/dist/engineering-pa-cli/src/research/commands/paths.mjs +69 -0
- package/dist/engineering-pa-cli/src/research/commands/prune.mjs +86 -0
- package/dist/engineering-pa-cli/src/research/commands/run.mjs +94 -0
- package/dist/engineering-pa-cli/src/research/commands/show.mjs +43 -0
- package/dist/engineering-pa-cli/src/research/docs/operator-present-flow.md +120 -0
- package/dist/engineering-pa-cli/src/research/format.mjs +59 -0
- package/dist/engineering-pa-cli/src/research/promote.mjs +260 -0
- package/dist/engineering-pa-cli/src/research/schema/finding.schema.json +55 -0
- package/dist/engineering-pa-cli/src/research/schema/gates.schema.json +34 -0
- package/dist/engineering-pa-cli/src/research/schema/manifest.schema.json +59 -0
- package/dist/engineering-pa-cli/src/research/schema/plan.schema.json +41 -0
- package/dist/engineering-pa-cli/src/research/schema/sources.schema.json +31 -0
- package/dist/engineering-pa-cli/src/research/store/findings.mjs +61 -0
- package/dist/engineering-pa-cli/src/research/store/gates.mjs +73 -0
- package/dist/engineering-pa-cli/src/research/store/index.mjs +312 -0
- package/dist/engineering-pa-cli/src/research/store/manifest.mjs +56 -0
- package/dist/engineering-pa-cli/src/research/store/plan.mjs +59 -0
- package/dist/engineering-pa-cli/src/research/store/sources.mjs +44 -0
- package/dist/engineering-pa-cli/src/research/store/validate.mjs +92 -0
- package/dist/engineering-pa-cli/src/research/workflows/research-normal.js +573 -0
- package/dist/engineering-pa-cli/src/scheduler/cli.mjs +197 -0
- package/dist/engineering-pa-cli/src/secrets/cli.mjs +254 -0
- package/dist/engineering-pa-cli/src/secrets/commands/backup-template.mjs +67 -0
- package/dist/engineering-pa-cli/src/secrets/commands/backup.mjs +85 -0
- package/dist/engineering-pa-cli/src/secrets/commands/delete.mjs +58 -0
- package/dist/engineering-pa-cli/src/secrets/commands/find.mjs +36 -0
- package/dist/engineering-pa-cli/src/secrets/commands/get.mjs +49 -0
- package/dist/engineering-pa-cli/src/secrets/commands/init.mjs +69 -0
- package/dist/engineering-pa-cli/src/secrets/commands/list.mjs +37 -0
- package/dist/engineering-pa-cli/src/secrets/commands/rotate.mjs +46 -0
- package/dist/engineering-pa-cli/src/secrets/commands/set.mjs +123 -0
- package/dist/engineering-pa-cli/src/secrets/commands/status.mjs +146 -0
- package/dist/engineering-pa-cli/src/secrets/commands/vaults.mjs +57 -0
- package/dist/engineering-pa-cli/src/secrets/commands/verify.mjs +143 -0
- package/dist/engineering-pa-cli/src/secrets/vault.mjs +766 -0
- package/dist/engineering-pa-cli/src/smoke/checks.mjs +199 -0
- package/dist/engineering-pa-cli/src/smoke/cli.mjs +108 -0
- package/dist/engineering-pa-cli/src/smoke/commands/run.mjs +237 -0
- package/dist/engineering-pa-cli/src/smoke/runner.mjs +191 -0
- package/dist/engineering-pa-cli/src/staging-alias/cli.mjs +91 -0
- package/dist/engineering-pa-cli/src/staging-alias/cloudflare.mjs +380 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/list.mjs +36 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/set.mjs +188 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/status.mjs +113 -0
- package/dist/engineering-pa-cli/src/staging-alias/commands/unset.mjs +95 -0
- package/dist/engineering-pa-cli/src/staging-alias/sites.mjs +105 -0
- package/dist/engineering-pa-cli/src/sync/cli.mjs +151 -0
- package/dist/engineering-pa-cli/src/sync/git.mjs +149 -0
- package/dist/engineering-pa-cli/src/sync/lock.mjs +30 -0
- package/dist/engineering-pa-cli/src/sync/regenerated-artifacts.mjs +114 -0
- package/dist/engineering-pa-cli/src/sync/registry-scope.mjs +260 -0
- package/dist/engineering-pa-cli/src/sync/run.mjs +531 -0
- package/dist/engineering-pa-cli/src/sync/runner.mjs +62 -0
- package/dist/engineering-pa-cli/src/sync/runs-digest.mjs +138 -0
- package/dist/engineering-pa-core/README.md +60 -0
- package/dist/engineering-pa-core/bin/desk +9 -0
- package/dist/engineering-pa-core/bin/distill +2 -0
- package/dist/engineering-pa-core/bin/memory-sync +2 -0
- package/dist/engineering-pa-core/bin/slice +2 -0
- package/dist/engineering-pa-core/docs/autonomy-envelope-tracker.md +111 -0
- package/dist/engineering-pa-core/docs/blueprints/README.md +45 -0
- package/dist/engineering-pa-core/docs/blueprints/docker-services/blueprint.md +168 -0
- package/dist/engineering-pa-core/docs/blueprints/git-identity/blueprint.md +116 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/custom-workspace-views.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/knowledge-domain-patterns.md +15 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/office-integration.md +11 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/team-scaling.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/ideas/work-personal-separation.md +9 -0
- package/dist/engineering-pa-core/docs/blueprints/scheduled-distillation/blueprint.md +129 -0
- package/dist/engineering-pa-core/docs/blueprints/tool-integration/blueprint.md +110 -0
- package/dist/engineering-pa-core/docs/commands/init.md +159 -0
- package/dist/engineering-pa-core/docs/commands/schedule.md +98 -0
- package/dist/engineering-pa-core/docs/commands/upgrade.md +67 -0
- package/dist/engineering-pa-core/docs/entity-location-map.md +93 -0
- package/dist/engineering-pa-core/docs/knowledge-scaffold.md +121 -0
- package/dist/engineering-pa-core/docs/lock.md +155 -0
- package/dist/engineering-pa-core/docs/memory-sync.md +28 -0
- package/dist/engineering-pa-core/docs/pa-operating-model.md +457 -0
- package/dist/engineering-pa-core/docs/scheduling.md +35 -0
- package/dist/engineering-pa-core/docs/standards/README.md +24 -0
- package/dist/engineering-pa-core/docs/standards/briefing-contract.md +161 -0
- package/dist/engineering-pa-core/docs/standards/tooling.md +240 -0
- package/dist/engineering-pa-core/docs/tools/distill.md +230 -0
- package/dist/engineering-pa-core/docs/tools/memory-sync.md +98 -0
- package/dist/engineering-pa-core/docs/tools/scheduler.md +243 -0
- package/dist/engineering-pa-core/docs/tools/slice.md +150 -0
- package/dist/engineering-pa-core/docs/upstream-issue-filing.md +88 -0
- package/dist/engineering-pa-core/docs/working-directory.md +29 -0
- package/dist/engineering-pa-core/docs/workspace-operations.md +46 -0
- package/dist/engineering-pa-core/index.mjs +21 -0
- package/dist/engineering-pa-core/knowledge/README.md +53 -0
- package/dist/engineering-pa-core/knowledge/canonical/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/content-voice/README.md +64 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-baseline.md +133 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-shape.md +58 -0
- package/dist/engineering-pa-core/knowledge/content-voice/banned-ai-tells-taxonomy.md +33 -0
- package/dist/engineering-pa-core/knowledge/content-voice/content-pipeline-routing.md +58 -0
- package/dist/engineering-pa-core/knowledge/content-voice/new-tell-capture-procedure.md +40 -0
- package/dist/engineering-pa-core/knowledge/content-voice/voice-guide-shape.md +43 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/README.md +34 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/briefing-contract.md +77 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/delegation-modes.md +84 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/dispatch-envelope.md +238 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/task-groups-and-orchestrators.md +62 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/team-improvement-loop.md +48 -0
- package/dist/engineering-pa-core/knowledge/delegation-dispatch/tool-development-policy.md +48 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/blast-radius-and-safety.md +50 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/investigate-root-causes.md +48 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/role-ladder.md +57 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/verify-runtime-behaviour.md +53 -0
- package/dist/engineering-pa-core/knowledge/engineering-discipline/worktree-isolation.md +50 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/claude-md-hygiene.md +47 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/journal-and-self-improvement.md +53 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/knowledge-retrieval-discipline.md +62 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/librarian-synthesis-mandate.md +56 -0
- package/dist/engineering-pa-core/knowledge/knowledge-memory/staging-and-observations.md +58 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/README.md +32 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/communication-style.md +33 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/escalation-and-correction.md +45 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/push-back-discipline.md +42 -0
- package/dist/engineering-pa-core/knowledge/operator-interface/session-boot-and-end.md +56 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/README.md +37 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/desk-cli-reference.md +52 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/environment-awareness.md +91 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/mcp-servers-catalogue.md +52 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/pa-cli-reference.md +55 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/scheduling-and-launchd.md +56 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/tool-discovery-discipline.md +49 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/working-directory-vs-project-root.md +43 -0
- package/dist/engineering-pa-core/knowledge/platform-tooling/workspace-server.md +51 -0
- package/dist/engineering-pa-core/knowledge/raw/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/scaffold/domains.json +5 -0
- package/dist/engineering-pa-core/knowledge/schema/.gitkeep +0 -0
- package/dist/engineering-pa-core/knowledge/work-management/README.md +33 -0
- package/dist/engineering-pa-core/knowledge/work-management/desk-cli.md +84 -0
- package/dist/engineering-pa-core/knowledge/work-management/progress-md.md +48 -0
- package/dist/engineering-pa-core/knowledge/work-management/projects.md +64 -0
- package/dist/engineering-pa-core/knowledge/work-management/spawn-log.md +48 -0
- package/dist/engineering-pa-core/knowledge/work-management/work-items.md +67 -0
- package/dist/engineering-pa-core/package.json +26 -0
- package/dist/engineering-pa-core/roles/agent-ops/definition.md +79 -0
- package/dist/engineering-pa-core/roles/dispatch-briefer/definition.md +195 -0
- package/dist/engineering-pa-core/roles/librarian/definition.md +176 -0
- package/dist/engineering-pa-core/roles/pa/definition.md +93 -0
- package/dist/engineering-pa-core/roles/system-admin/definition.md +106 -0
- package/dist/engineering-pa-core/roles/tool-developer/definition.md +116 -0
- package/dist/engineering-pa-core/roles/writer/definition.md +130 -0
- package/dist/engineering-pa-core/schema/autonomy-envelope.schema.json +61 -0
- package/dist/engineering-pa-core/schema/project.json +114 -0
- package/dist/engineering-pa-core/schema/schedule.json +63 -0
- package/dist/engineering-pa-core/schema/work-item.json +92 -0
- package/dist/engineering-pa-core/src/config/pa-package-name.mjs +30 -0
- package/dist/engineering-pa-core/src/desk/cli.mjs +95 -0
- package/dist/engineering-pa-core/src/desk/commands.mjs +398 -0
- package/dist/engineering-pa-core/src/desk/format.mjs +84 -0
- package/dist/engineering-pa-core/src/desk/repo.mjs +172 -0
- package/dist/engineering-pa-core/src/desk/team-scope.mjs +142 -0
- package/dist/engineering-pa-core/src/distill/cli.mjs +112 -0
- package/dist/engineering-pa-core/src/distill/prereqs.mjs +171 -0
- package/dist/engineering-pa-core/src/distill/prompt.mjs +74 -0
- package/dist/engineering-pa-core/src/distill/run.mjs +230 -0
- package/dist/engineering-pa-core/src/distill/runner.mjs +86 -0
- package/dist/engineering-pa-core/src/distill/staging.mjs +59 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/backlink-indexer.mjs +154 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/graph-builder.mjs +186 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/index-builder.mjs +225 -0
- package/dist/engineering-pa-core/src/knowledge/retrieval/query.mjs +263 -0
- package/dist/engineering-pa-core/src/lock/lock.mjs +202 -0
- package/dist/engineering-pa-core/src/memory-sync/cli-support.mjs +12 -0
- package/dist/engineering-pa-core/src/memory-sync/cli.mjs +56 -0
- package/dist/engineering-pa-core/src/memory-sync/commands/pull.mjs +44 -0
- package/dist/engineering-pa-core/src/memory-sync/commands/push.mjs +46 -0
- package/dist/engineering-pa-core/src/memory-sync/mirror.mjs +114 -0
- package/dist/engineering-pa-core/src/memory-sync/repo.mjs +65 -0
- package/dist/engineering-pa-core/src/scheduler/detect.mjs +111 -0
- package/dist/engineering-pa-core/src/scheduler/host.mjs +38 -0
- package/dist/engineering-pa-core/src/scheduler/index.mjs +13 -0
- package/dist/engineering-pa-core/src/scheduler/install.mjs +854 -0
- package/dist/engineering-pa-core/src/scheduler/list.mjs +168 -0
- package/dist/engineering-pa-core/src/scheduler/remove.mjs +176 -0
- package/dist/engineering-pa-core/src/scheduler/verify.mjs +435 -0
- package/dist/engineering-pa-core/src/session-boot/autonomy-injection.mjs +84 -0
- package/dist/engineering-pa-core/src/slice/cli.mjs +139 -0
- package/dist/engineering-pa-core/src/slice/inputs.mjs +174 -0
- package/dist/engineering-pa-core/src/slice/prompt.mjs +94 -0
- package/dist/engineering-pa-core/src/slice/prompts/dave-boot.md +91 -0
- package/dist/engineering-pa-core/src/slice/run.mjs +334 -0
- package/dist/engineering-pa-core-extensions-atlassian/README.md +31 -0
- package/dist/engineering-pa-core-extensions-atlassian/index.mjs +9 -0
- package/dist/engineering-pa-core-extensions-atlassian/package.json +19 -0
- package/dist/engineering-pa-core-extensions-n8n/README.md +29 -0
- package/dist/engineering-pa-core-extensions-n8n/index.mjs +8 -0
- package/dist/engineering-pa-core-extensions-n8n/package.json +19 -0
- package/dist/engineering-pa-librarian/README.md +38 -0
- package/dist/engineering-pa-librarian/index.mjs +12 -0
- package/dist/engineering-pa-librarian/package.json +19 -0
- package/dist/engineering-pa-migrate-inspect/README.md +131 -0
- package/dist/engineering-pa-migrate-inspect/bin/migrate-inspect +2 -0
- package/dist/engineering-pa-migrate-inspect/package.json +21 -0
- package/dist/engineering-pa-migrate-inspect/src/age-key-migrate.mjs +83 -0
- package/dist/engineering-pa-migrate-inspect/src/cli.mjs +97 -0
- package/dist/engineering-pa-migrate-inspect/src/inspector.mjs +237 -0
- package/dist/engineering-pa-migrate-inspect/src/platforms/linux.mjs +87 -0
- package/dist/engineering-pa-migrate-inspect/src/platforms/macos.mjs +105 -0
- package/dist/engineering-pa-migrate-inspect/src/runner.mjs +87 -0
- package/dist/engineering-pa-runbook/README.md +103 -0
- package/dist/engineering-pa-runbook/package.json +18 -0
- package/dist/engineering-pa-runbook/reference/dex-template.runbook.json +66 -0
- package/dist/engineering-pa-runbook/reference/hq-dave.runbook.json +69 -0
- package/dist/engineering-pa-runbook/reference/smarthome01.runbook.json +92 -0
- package/dist/engineering-pa-runbook/schema/inspection.schema.json +113 -0
- package/dist/engineering-pa-runbook/schema/runbook.schema.json +67 -0
- package/dist/engineering-pa-runbook/src/validator.mjs +167 -0
- package/dist/engineering-pa-scaffold/README.md +164 -0
- package/dist/engineering-pa-scaffold/bin/pa-init +2 -0
- package/dist/engineering-pa-scaffold/index.mjs +42 -0
- package/dist/engineering-pa-scaffold/package.json +22 -0
- package/dist/engineering-pa-scaffold/src/cli.mjs +115 -0
- package/dist/engineering-pa-scaffold/src/commands/init.mjs +169 -0
- package/dist/engineering-pa-scaffold/src/generator.mjs +354 -0
- package/dist/engineering-pa-scaffold/src/prompts.mjs +125 -0
- package/dist/engineering-pa-scaffold/src/runner.mjs +142 -0
- package/dist/engineering-pa-scaffold/templates/CLAUDE.md.template +196 -0
- package/dist/engineering-pa-scaffold/templates/operator-README.md.template +44 -0
- package/dist/engineering-pa-types-engineering/README.md +35 -0
- package/dist/engineering-pa-types-engineering/docs/commands/product-build.md +120 -0
- package/dist/engineering-pa-types-engineering/index.mjs +9 -0
- package/dist/engineering-pa-types-engineering/package.json +19 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/definition.md +650 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/build-spec.md +82 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/preflight-checklist.md +158 -0
- package/dist/engineering-pa-types-engineering/roles/product-engineer/templates/worker-brief.md +130 -0
- package/dist/engineering-pa-workspace/README.md +197 -0
- package/dist/engineering-pa-workspace/app/assets/md.js +187 -0
- package/dist/engineering-pa-workspace/app/assets/modal.js +120 -0
- package/dist/engineering-pa-workspace/app/assets/theme.css +132 -0
- package/dist/engineering-pa-workspace/app/assets/theme.js +159 -0
- package/dist/engineering-pa-workspace/app/banner.js +320 -0
- package/dist/engineering-pa-workspace/app/delegations.html +345 -0
- package/dist/engineering-pa-workspace/app/home.html +1060 -0
- package/dist/engineering-pa-workspace/app/index.html +361 -0
- package/dist/engineering-pa-workspace/app/knowledge-map.html +163 -0
- package/dist/engineering-pa-workspace/app/knowledge.html +304 -0
- package/dist/engineering-pa-workspace/app/nav.json +75 -0
- package/dist/engineering-pa-workspace/app/notes.html +199 -0
- package/dist/engineering-pa-workspace/app/onboarding.html +592 -0
- package/dist/engineering-pa-workspace/app/profile.html +243 -0
- package/dist/engineering-pa-workspace/app/project-detail.html +432 -0
- package/dist/engineering-pa-workspace/app/projects.html +297 -0
- package/dist/engineering-pa-workspace/app/repos.html +598 -0
- package/dist/engineering-pa-workspace/app/schedules.html +486 -0
- package/dist/engineering-pa-workspace/app/secrets.html +164 -0
- package/dist/engineering-pa-workspace/app/staging.html +319 -0
- package/dist/engineering-pa-workspace/app/tasks.html +465 -0
- package/dist/engineering-pa-workspace/app/vendor/chart.umd.min.js +21 -0
- package/dist/engineering-pa-workspace/app/viewer.html +116 -0
- package/dist/engineering-pa-workspace/docs/DAEMON.md +104 -0
- package/dist/engineering-pa-workspace/docs/page-blueprint.md +286 -0
- package/dist/engineering-pa-workspace/docs/status-mon.md +168 -0
- package/dist/engineering-pa-workspace/index.mjs +12 -0
- package/dist/engineering-pa-workspace/nav.json.example +8 -0
- package/dist/engineering-pa-workspace/package.json +27 -0
- package/dist/engineering-pa-workspace/src/api.mjs +2086 -0
- package/dist/engineering-pa-workspace/src/install-daemon.mjs +573 -0
- package/dist/engineering-pa-workspace/src/nav.mjs +190 -0
- package/dist/engineering-pa-workspace/src/secrets-api.mjs +175 -0
- package/dist/engineering-pa-workspace/src/server.mjs +516 -0
- package/dist/engineering-pa-workspace/src/status-mon/poll.mjs +220 -0
- package/dist/engineering-pa-workspace/src/status-mon/poller.mjs +113 -0
- package/dist/engineering-pa-workspace/src/status-mon/run.mjs +133 -0
- package/dist/engineering-pa-workspace/src/supervisor.mjs +264 -0
- package/dist/engineering-pa-workspace/src/ws.mjs +633 -0
- package/index.mjs +60 -0
- package/package.json +58 -0
- package/templates/banned-ai-tells.md +102 -0
- package/templates/purpose.md +105 -0
- package/templates/voice-guide.md +98 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
// estate-sync — orchestrator.
|
|
2
|
+
//
|
|
3
|
+
// The single git writer per host for ESTATE-NATIVE content only. Designed
|
|
4
|
+
// for a 30-minute cron: quiet on success, loud on failure, exit-code
|
|
5
|
+
// accurate for monitoring.
|
|
6
|
+
//
|
|
7
|
+
// Flow (commit-first; see note below):
|
|
8
|
+
// 1. acquire the process lock (PID file; stale after 10m)
|
|
9
|
+
// 2. git fetch origin — refresh remote refs FIRST so ahead/behind is
|
|
10
|
+
// accurate (without this, a clean tree short-circuits to noop using
|
|
11
|
+
// a stale origin/main pointer and never discovers remote commits)
|
|
12
|
+
// 3. discover working-tree state — clean? ahead/behind upstream?
|
|
13
|
+
// 4. if nothing to do (clean tree + ahead == 0 + behind == 0) -> noop
|
|
14
|
+
// audit + exit 0
|
|
15
|
+
// 5. compose a commit body from the cron tools' runs.jsonl files, plus
|
|
16
|
+
// the registry-scoped commit-scope decision (see registry-scope.mjs)
|
|
17
|
+
// 6. if dirty: compute the commit scope — when
|
|
18
|
+
// <repoRoot>/operator/registry/{devices.json,repos.json} exist,
|
|
19
|
+
// resolve this device and exclude every entitled mount's `to`
|
|
20
|
+
// subtree (+ .vault/** unconditionally), then stage ONLY the
|
|
21
|
+
// in-scope dirty paths via explicit pathspecs (`git add -- <paths>`)
|
|
22
|
+
// — NEVER a bare `git add -A`. No registry present -> previous
|
|
23
|
+
// (pre-rewrite) behaviour: unscoped `git add -A`, but loud about it
|
|
24
|
+
// (stderr + commit body). Then
|
|
25
|
+
// `git commit -m "estate-sync <ISO ts>" -m "<body>"`.
|
|
26
|
+
// (this happens BEFORE the pull — git pull --rebase refuses to run
|
|
27
|
+
// with uncommitted changes in the index or working tree)
|
|
28
|
+
// 7. git pull --rebase origin <branch> (fetch already done in step 2)
|
|
29
|
+
// on rebase conflict: git rebase --abort + fail-rebase audit + exit 2
|
|
30
|
+
// 8. git push. on rejection: fetch + pull --rebase + push retry. one
|
|
31
|
+
// retry only.
|
|
32
|
+
// 9. append success audit to logs/runs.jsonl
|
|
33
|
+
//
|
|
34
|
+
// Why commit-first (Option A): `git pull --rebase` refuses to run when
|
|
35
|
+
// the index or working tree contains uncommitted changes. The original
|
|
36
|
+
// "add -> pull --rebase -> commit -> push" ordering is therefore
|
|
37
|
+
// impossible — sync would abort with "cannot pull with rebase: Your
|
|
38
|
+
// index contains uncommitted changes". Committing first means the
|
|
39
|
+
// rebase replays one clean local commit on top of origin's history;
|
|
40
|
+
// this is the same shape a human follows when integrating with origin.
|
|
41
|
+
// Failure modes are also cleaner: a conflicting rebase can be safely
|
|
42
|
+
// aborted (the commit stays local, next tick reconciles), whereas a
|
|
43
|
+
// stash-pop-conflict would leave the tree in a half-merged state with
|
|
44
|
+
// no good automated recovery.
|
|
45
|
+
//
|
|
46
|
+
// Audit-record contract: every non-dry-run exit path writes exactly one
|
|
47
|
+
// record. Status vocabulary (§15 of the tooling standard):
|
|
48
|
+
// ok commit + push (or pull-only) succeeded
|
|
49
|
+
// noop-nothing-to-do working tree clean and no commits ahead of origin
|
|
50
|
+
// noop-locked another sync process is running — try again
|
|
51
|
+
// fail-rebase pull --rebase conflicted; rebase aborted
|
|
52
|
+
// fail-push push rejected even after rebase retry
|
|
53
|
+
// fail-git any other git command failed
|
|
54
|
+
//
|
|
55
|
+
// Exit codes (§6 of the tooling standard):
|
|
56
|
+
// 0 ok (including noop)
|
|
57
|
+
// 1 usage (handled in cli.mjs)
|
|
58
|
+
// 2 logical / operational failure (rebase, push reject, dirty rebase)
|
|
59
|
+
// 3 transport / IO failure (git command itself errored, not the result)
|
|
60
|
+
|
|
61
|
+
import { appendFile } from 'node:fs/promises';
|
|
62
|
+
import { join } from 'node:path';
|
|
63
|
+
|
|
64
|
+
import {
|
|
65
|
+
addAll,
|
|
66
|
+
addPaths,
|
|
67
|
+
aheadBehind,
|
|
68
|
+
checkoutOursDuringRebase,
|
|
69
|
+
commit,
|
|
70
|
+
currentBranch,
|
|
71
|
+
fetchOrigin,
|
|
72
|
+
hasStagedChanges,
|
|
73
|
+
listConflictedPaths,
|
|
74
|
+
pullRebase,
|
|
75
|
+
push,
|
|
76
|
+
rebaseAbort,
|
|
77
|
+
rebaseContinue,
|
|
78
|
+
statusPorcelain,
|
|
79
|
+
} from './git.mjs';
|
|
80
|
+
import { acquire } from './lock.mjs';
|
|
81
|
+
import {
|
|
82
|
+
classifyConflicts,
|
|
83
|
+
loadRegeneratedArtifacts,
|
|
84
|
+
} from './regenerated-artifacts.mjs';
|
|
85
|
+
import { computeCommitScope, filterInScopePaths, renderScopeSection } from './registry-scope.mjs';
|
|
86
|
+
import { composeBody } from './runs-digest.mjs';
|
|
87
|
+
|
|
88
|
+
function log(line, { stderr = process.stderr } = {}) {
|
|
89
|
+
stderr.write(`[estate-sync] ${line}\n`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function writeAudit(runsLog, record, stderr) {
|
|
93
|
+
try {
|
|
94
|
+
await appendFile(runsLog, JSON.stringify(record) + '\n');
|
|
95
|
+
} catch (err) {
|
|
96
|
+
log(`warning: failed to append audit record to ${runsLog}: ${err.message}`, { stderr });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function runSync({
|
|
101
|
+
repoRoot,
|
|
102
|
+
logDir,
|
|
103
|
+
runsLog,
|
|
104
|
+
lockPath,
|
|
105
|
+
dryRun = false,
|
|
106
|
+
runner,
|
|
107
|
+
stderr = process.stderr,
|
|
108
|
+
lockOptions = {},
|
|
109
|
+
// Test-only injection seam: overrides os.hostname() inside
|
|
110
|
+
// computeCommitScope's device resolution. Production callers never pass
|
|
111
|
+
// this — computeCommitScope defaults to the real os.hostname().
|
|
112
|
+
hostnameFn,
|
|
113
|
+
} = {}) {
|
|
114
|
+
if (!runner) throw new Error('runSync: runner is required');
|
|
115
|
+
if (!repoRoot) throw new Error('runSync: repoRoot is required');
|
|
116
|
+
if (!runsLog) throw new Error('runSync: runsLog is required');
|
|
117
|
+
if (!lockPath) throw new Error('runSync: lockPath is required');
|
|
118
|
+
|
|
119
|
+
const tsIso = runner.tsIso();
|
|
120
|
+
const startedAtMs = Date.now();
|
|
121
|
+
|
|
122
|
+
if (dryRun) {
|
|
123
|
+
log('DRY RUN — would:', { stderr });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Step 1 — lock.
|
|
127
|
+
const lock = await acquire(lockPath, lockOptions);
|
|
128
|
+
if (!lock.acquired) {
|
|
129
|
+
log(`another sync is running: ${lock.reason} — exiting noop`, { stderr });
|
|
130
|
+
if (!dryRun) {
|
|
131
|
+
await writeAudit(runsLog, {
|
|
132
|
+
ts: tsIso,
|
|
133
|
+
status: 'noop-locked',
|
|
134
|
+
exitCode: 0,
|
|
135
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
136
|
+
reason: `lock contention: ${lock.reason}`,
|
|
137
|
+
}, stderr);
|
|
138
|
+
}
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// Step 2 — fetch upstream FIRST so ahead/behind reflects what origin
|
|
144
|
+
// actually has. Without this, a clean tree with no local commits
|
|
145
|
+
// short-circuits to the step-4 noop using whatever origin/<branch>
|
|
146
|
+
// pointed at on the last fetch — which on a long-idle clone is
|
|
147
|
+
// arbitrarily stale. Fetch failures (network, auth) are logged but
|
|
148
|
+
// not blocking: we'd rather proceed on stale refs for one cycle than
|
|
149
|
+
// miss a chance to commit local work. The next tick will retry.
|
|
150
|
+
//
|
|
151
|
+
// Skip in dry-run mode — even though fetch only updates remote-
|
|
152
|
+
// tracking refs (no local branch / working tree mutation), the
|
|
153
|
+
// dry-run contract is "no git commands spawned." Dry-run's
|
|
154
|
+
// ahead/behind reporting may therefore be stale by up to one cron
|
|
155
|
+
// tick; that's an acceptable cost for the cleaner contract.
|
|
156
|
+
let upfrontFetch = { code: 0 };
|
|
157
|
+
if (!dryRun) {
|
|
158
|
+
upfrontFetch = await fetchOrigin(runner, { cwd: repoRoot });
|
|
159
|
+
if (upfrontFetch.code !== 0) {
|
|
160
|
+
log(`git fetch (up-front) failed — proceeding with stale upstream refs: ${upfrontFetch.stderr.trim()}`, { stderr });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Step 3 — examine working tree and upstream relation.
|
|
165
|
+
const status = await statusPorcelain(runner, { cwd: repoRoot });
|
|
166
|
+
if (status.code !== 0) {
|
|
167
|
+
log(`git status failed: ${status.stderr.trim()}`, { stderr });
|
|
168
|
+
if (!dryRun) {
|
|
169
|
+
await writeAudit(runsLog, {
|
|
170
|
+
ts: tsIso,
|
|
171
|
+
status: 'fail-git',
|
|
172
|
+
exitCode: 3,
|
|
173
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
174
|
+
reason: `git status failed: ${status.stderr.trim()}`,
|
|
175
|
+
}, stderr);
|
|
176
|
+
}
|
|
177
|
+
return 3;
|
|
178
|
+
}
|
|
179
|
+
const dirty = status.stdout.trim().length > 0;
|
|
180
|
+
const { ahead, behind } = await aheadBehind(runner, { cwd: repoRoot });
|
|
181
|
+
const aheadCount = Number.isFinite(ahead) ? ahead : 0;
|
|
182
|
+
const behindCount = Number.isFinite(behind) ? behind : 0;
|
|
183
|
+
log(`tree dirty=${dirty}, ahead=${aheadCount}, behind=${behindCount}`, { stderr });
|
|
184
|
+
|
|
185
|
+
const branch = await currentBranch(runner, { cwd: repoRoot });
|
|
186
|
+
|
|
187
|
+
// Step 4 — noop short-circuit.
|
|
188
|
+
if (!dirty && aheadCount === 0 && behindCount === 0) {
|
|
189
|
+
log('nothing to do — tree clean, no local commits ahead, up-to-date', { stderr });
|
|
190
|
+
if (!dryRun) {
|
|
191
|
+
await writeAudit(runsLog, {
|
|
192
|
+
ts: tsIso,
|
|
193
|
+
status: 'noop-nothing-to-do',
|
|
194
|
+
exitCode: 0,
|
|
195
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
196
|
+
dirty,
|
|
197
|
+
ahead: aheadCount,
|
|
198
|
+
behind: behindCount,
|
|
199
|
+
branch,
|
|
200
|
+
}, stderr);
|
|
201
|
+
}
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Step 5 — compose body (cheap; pure file read) + registry-scoped
|
|
206
|
+
// commit-scope decision (also cheap; two small JSON reads at most).
|
|
207
|
+
const digestBody = await composeBody({ repoRoot });
|
|
208
|
+
const scope = computeCommitScope(hostnameFn ? { repoRoot, hostnameFn } : { repoRoot });
|
|
209
|
+
|
|
210
|
+
if (dryRun) {
|
|
211
|
+
log(` branch: ${branch}`, { stderr });
|
|
212
|
+
log(` dirty: ${dirty}`, { stderr });
|
|
213
|
+
log(` ahead/behind: ${aheadCount}/${behindCount}`, { stderr });
|
|
214
|
+
log(` commit title: estate-sync ${tsIso}`, { stderr });
|
|
215
|
+
log(' commit body (preview):', { stderr });
|
|
216
|
+
for (const line of digestBody.split('\n')) {
|
|
217
|
+
log(` ${line}`, { stderr });
|
|
218
|
+
}
|
|
219
|
+
log(' would: git fetch origin (real run does this BEFORE the noop check; dry-run skipped to keep this side-effect-free)', { stderr });
|
|
220
|
+
if (dirty) {
|
|
221
|
+
if (scope.registryPresent) {
|
|
222
|
+
const { inScope, excludedCount } = filterInScopePaths(status.stdout, scope.excludeDirs);
|
|
223
|
+
log(` would: git add -- <${inScope.length} in-scope path(s), device=${scope.deviceId}, ${excludedCount} excluded by registry scope>`, { stderr });
|
|
224
|
+
} else {
|
|
225
|
+
log(' would: git add -A (no registry found — unscoped commit)', { stderr });
|
|
226
|
+
}
|
|
227
|
+
log(` would: git commit -m "estate-sync ${tsIso}" -m "<body above>"`, { stderr });
|
|
228
|
+
}
|
|
229
|
+
log(` would: git pull --rebase origin ${branch}`, { stderr });
|
|
230
|
+
log(' would: git push (with one rebase-and-retry on rejection)', { stderr });
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Step 6 — stage + commit (commit-first ordering).
|
|
235
|
+
//
|
|
236
|
+
// We commit BEFORE pulling because `git pull --rebase` refuses to
|
|
237
|
+
// run when the index or working tree has uncommitted changes — it
|
|
238
|
+
// aborts with "cannot pull with rebase: Your index contains
|
|
239
|
+
// uncommitted changes". By committing first, the subsequent rebase
|
|
240
|
+
// replays a clean local commit on top of origin's history. This is
|
|
241
|
+
// the same shape a human follows when integrating: stage, commit,
|
|
242
|
+
// then rebase onto upstream.
|
|
243
|
+
let didCommit = false;
|
|
244
|
+
if (dirty) {
|
|
245
|
+
let addRes;
|
|
246
|
+
let stagedCount = 0;
|
|
247
|
+
let excludedPathCount = 0;
|
|
248
|
+
if (scope.registryPresent) {
|
|
249
|
+
// Registry-scoped staging: explicit pathspecs of in-scope dirty
|
|
250
|
+
// paths only — NEVER a bare `git add -A`. Untracked files inside
|
|
251
|
+
// an excluded subtree are simply never passed to `git add`, so
|
|
252
|
+
// they can never be staged (pathspec approach handles this by
|
|
253
|
+
// construction, not by a follow-up filter).
|
|
254
|
+
const { inScope, excludedCount } = filterInScopePaths(status.stdout, scope.excludeDirs);
|
|
255
|
+
stagedCount = inScope.length;
|
|
256
|
+
excludedPathCount = excludedCount;
|
|
257
|
+
if (inScope.length > 0) {
|
|
258
|
+
addRes = await addPaths(runner, { cwd: repoRoot, paths: inScope });
|
|
259
|
+
} else {
|
|
260
|
+
addRes = { code: 0, stdout: '', stderr: '' };
|
|
261
|
+
log('all dirty paths are excluded by registry scope — nothing to stage this tick', { stderr });
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
// No registry present at this repo root — previous (pre-rewrite)
|
|
265
|
+
// behaviour: unscoped `git add -A`, but LOUD about it, both in
|
|
266
|
+
// the run's own log output and in the commit body (below).
|
|
267
|
+
const reason = scope.warnings.length > 0 ? ` (${scope.warnings.join('; ')})` : '';
|
|
268
|
+
log(`estate-sync: no registry found, unscoped commit${reason}`, { stderr });
|
|
269
|
+
addRes = await addAll(runner, { cwd: repoRoot });
|
|
270
|
+
}
|
|
271
|
+
const scopeSection = renderScopeSection(scope, { staged: stagedCount, excludedPaths: excludedPathCount });
|
|
272
|
+
const body = `${scopeSection}\n\n${digestBody}`;
|
|
273
|
+
|
|
274
|
+
if (addRes.code !== 0) {
|
|
275
|
+
log(`git add failed: ${addRes.stderr.trim()}`, { stderr });
|
|
276
|
+
await writeAudit(runsLog, {
|
|
277
|
+
ts: tsIso,
|
|
278
|
+
status: 'fail-git',
|
|
279
|
+
exitCode: 3,
|
|
280
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
281
|
+
reason: `git add failed: ${addRes.stderr.trim()}`,
|
|
282
|
+
}, stderr);
|
|
283
|
+
return 3;
|
|
284
|
+
}
|
|
285
|
+
// Belt-and-braces: if add somehow produced no staged content (e.g.
|
|
286
|
+
// every dirty path was ignored), skip the commit and continue.
|
|
287
|
+
const haveStaged = await hasStagedChanges(runner, { cwd: repoRoot });
|
|
288
|
+
if (haveStaged) {
|
|
289
|
+
const title = `estate-sync ${tsIso}`;
|
|
290
|
+
const c = await commit(runner, { cwd: repoRoot, title, body });
|
|
291
|
+
if (c.code !== 0) {
|
|
292
|
+
log(`git commit failed: ${c.stderr.trim()}`, { stderr });
|
|
293
|
+
await writeAudit(runsLog, {
|
|
294
|
+
ts: tsIso,
|
|
295
|
+
status: 'fail-git',
|
|
296
|
+
exitCode: 3,
|
|
297
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
298
|
+
reason: `git commit failed: ${c.stderr.trim()}`,
|
|
299
|
+
}, stderr);
|
|
300
|
+
return 3;
|
|
301
|
+
}
|
|
302
|
+
didCommit = true;
|
|
303
|
+
log('committed', { stderr });
|
|
304
|
+
} else {
|
|
305
|
+
log('nothing staged after add — skipping commit', { stderr });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Step 7 — rebase. Fetch already done in step 2. Rebase failures
|
|
310
|
+
// are operational, not transport. Now safe to rebase because the
|
|
311
|
+
// tree is clean (either we committed in step 6, or it was clean to
|
|
312
|
+
// begin with — behind-only). If rebase conflicts on a path our
|
|
313
|
+
// fresh commit touches, we abort and surface; the commit stays
|
|
314
|
+
// local and the next tick will retry.
|
|
315
|
+
//
|
|
316
|
+
// We only attempt pull --rebase if the up-front fetch succeeded.
|
|
317
|
+
// If fetch failed earlier, we'd be rebasing onto stale refs which
|
|
318
|
+
// either no-ops (if local refs already match) or fails spuriously.
|
|
319
|
+
let autoResolvedPaths = [];
|
|
320
|
+
if (upfrontFetch.code === 0) {
|
|
321
|
+
const prRes = await pullRebase(runner, { cwd: repoRoot, branch });
|
|
322
|
+
if (prRes.code !== 0) {
|
|
323
|
+
log(`git pull --rebase failed: ${prRes.stderr.trim()}`, { stderr });
|
|
324
|
+
// Before aborting, see if the conflict is limited to files that
|
|
325
|
+
// distillation regenerates on every host. Those files are
|
|
326
|
+
// byte-different but semantically equivalent across hosts; a
|
|
327
|
+
// conflict just means two hosts pushed regenerations in the same
|
|
328
|
+
// window. Auto-resolve by preferring origin's version (see the
|
|
329
|
+
// rebase-inversion note on `checkoutOursDuringRebase`) and
|
|
330
|
+
// continue. If ANY conflict is outside the allowlist, we still
|
|
331
|
+
// abort and surface fail-rebase — real conflicts need a human.
|
|
332
|
+
const resolved = await tryAutoResolveRebase({
|
|
333
|
+
runner,
|
|
334
|
+
repoRoot,
|
|
335
|
+
stderr,
|
|
336
|
+
});
|
|
337
|
+
if (resolved.ok) {
|
|
338
|
+
autoResolvedPaths = resolved.paths;
|
|
339
|
+
log(
|
|
340
|
+
`auto-resolved rebase conflict on ${resolved.paths.length} regenerated artifact(s): ${resolved.paths.join(', ')}`,
|
|
341
|
+
{ stderr },
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
log(`aborting rebase: ${resolved.reason}`, { stderr });
|
|
345
|
+
// Defensive abort. If no rebase is in progress (auto-resolve
|
|
346
|
+
// already ended it, or nothing was in progress), abort exits
|
|
347
|
+
// non-zero — we ignore that case.
|
|
348
|
+
await rebaseAbort(runner, { cwd: repoRoot });
|
|
349
|
+
await writeAudit(runsLog, {
|
|
350
|
+
ts: tsIso,
|
|
351
|
+
status: 'fail-rebase',
|
|
352
|
+
exitCode: 2,
|
|
353
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
354
|
+
reason: `git pull --rebase failed — rebase aborted; ${resolved.reason}`,
|
|
355
|
+
committed: didCommit,
|
|
356
|
+
...(resolved.disallowed ? { conflictedPaths: resolved.disallowed } : {}),
|
|
357
|
+
}, stderr);
|
|
358
|
+
return 2;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Step 7 — push (only if we have something to push). Note: after a
|
|
364
|
+
// pull --rebase that fast-forwarded, we may have new local refs
|
|
365
|
+
// pointing at remote refs (nothing to push). If we committed, we have
|
|
366
|
+
// at least one local-only commit. If we were just behind, pull
|
|
367
|
+
// brought us up to date — nothing to push.
|
|
368
|
+
const autoResolveFields = autoResolvedPaths.length > 0
|
|
369
|
+
? { autoResolved: autoResolvedPaths.length, autoResolvedPaths }
|
|
370
|
+
: {};
|
|
371
|
+
|
|
372
|
+
const { ahead: aheadAfter } = await aheadBehind(runner, { cwd: repoRoot });
|
|
373
|
+
const needsPush = Number.isFinite(aheadAfter) && aheadAfter > 0;
|
|
374
|
+
if (!needsPush) {
|
|
375
|
+
log('nothing to push (up-to-date with origin)', { stderr });
|
|
376
|
+
await writeAudit(runsLog, {
|
|
377
|
+
ts: tsIso,
|
|
378
|
+
status: 'ok',
|
|
379
|
+
exitCode: 0,
|
|
380
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
381
|
+
committed: didCommit,
|
|
382
|
+
pushed: false,
|
|
383
|
+
branch,
|
|
384
|
+
...autoResolveFields,
|
|
385
|
+
}, stderr);
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const p1 = await push(runner, { cwd: repoRoot });
|
|
390
|
+
if (p1.code === 0) {
|
|
391
|
+
log('pushed', { stderr });
|
|
392
|
+
await writeAudit(runsLog, {
|
|
393
|
+
ts: tsIso,
|
|
394
|
+
status: 'ok',
|
|
395
|
+
exitCode: 0,
|
|
396
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
397
|
+
committed: didCommit,
|
|
398
|
+
pushed: true,
|
|
399
|
+
branch,
|
|
400
|
+
...autoResolveFields,
|
|
401
|
+
}, stderr);
|
|
402
|
+
return 0;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
log(`git push rejected — fetching and rebasing for one retry: ${p1.stderr.trim()}`, { stderr });
|
|
406
|
+
const f2 = await fetchOrigin(runner, { cwd: repoRoot });
|
|
407
|
+
if (f2.code !== 0) {
|
|
408
|
+
log(`git fetch (for push-retry) failed: ${f2.stderr.trim()}`, { stderr });
|
|
409
|
+
await writeAudit(runsLog, {
|
|
410
|
+
ts: tsIso,
|
|
411
|
+
status: 'fail-push',
|
|
412
|
+
exitCode: 2,
|
|
413
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
414
|
+
reason: 'push rejected and follow-up fetch failed; commit is local-only',
|
|
415
|
+
}, stderr);
|
|
416
|
+
return 2;
|
|
417
|
+
}
|
|
418
|
+
const pr2 = await pullRebase(runner, { cwd: repoRoot, branch });
|
|
419
|
+
if (pr2.code !== 0) {
|
|
420
|
+
log(`git pull --rebase (for push-retry) failed: ${pr2.stderr.trim()}`, { stderr });
|
|
421
|
+
// Symmetric with step 7: if the push-retry rebase conflicts only
|
|
422
|
+
// on regenerated artifacts, auto-resolve and continue. Otherwise
|
|
423
|
+
// abort as before.
|
|
424
|
+
const resolved2 = await tryAutoResolveRebase({
|
|
425
|
+
runner,
|
|
426
|
+
repoRoot,
|
|
427
|
+
stderr,
|
|
428
|
+
});
|
|
429
|
+
if (resolved2.ok) {
|
|
430
|
+
autoResolvedPaths = [...autoResolvedPaths, ...resolved2.paths];
|
|
431
|
+
log(
|
|
432
|
+
`auto-resolved push-retry rebase on ${resolved2.paths.length} regenerated artifact(s): ${resolved2.paths.join(', ')}`,
|
|
433
|
+
{ stderr },
|
|
434
|
+
);
|
|
435
|
+
} else {
|
|
436
|
+
log(`aborting push-retry rebase: ${resolved2.reason}`, { stderr });
|
|
437
|
+
await rebaseAbort(runner, { cwd: repoRoot });
|
|
438
|
+
await writeAudit(runsLog, {
|
|
439
|
+
ts: tsIso,
|
|
440
|
+
status: 'fail-rebase',
|
|
441
|
+
exitCode: 2,
|
|
442
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
443
|
+
reason: `push rejected; follow-up rebase aborted; ${resolved2.reason}`,
|
|
444
|
+
...(resolved2.disallowed ? { conflictedPaths: resolved2.disallowed } : {}),
|
|
445
|
+
}, stderr);
|
|
446
|
+
return 2;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const p2 = await push(runner, { cwd: repoRoot });
|
|
450
|
+
if (p2.code !== 0) {
|
|
451
|
+
log(`git push (retry) failed: ${p2.stderr.trim()}`, { stderr });
|
|
452
|
+
await writeAudit(runsLog, {
|
|
453
|
+
ts: tsIso,
|
|
454
|
+
status: 'fail-push',
|
|
455
|
+
exitCode: 2,
|
|
456
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
457
|
+
reason: 'push rejected even after rebase-retry; commit is local-only',
|
|
458
|
+
}, stderr);
|
|
459
|
+
return 2;
|
|
460
|
+
}
|
|
461
|
+
log('pushed after rebase', { stderr });
|
|
462
|
+
// Recompute so any auto-resolve during the push-retry is captured.
|
|
463
|
+
const autoResolveFieldsFinal = autoResolvedPaths.length > 0
|
|
464
|
+
? { autoResolved: autoResolvedPaths.length, autoResolvedPaths }
|
|
465
|
+
: {};
|
|
466
|
+
await writeAudit(runsLog, {
|
|
467
|
+
ts: tsIso,
|
|
468
|
+
status: 'ok',
|
|
469
|
+
exitCode: 0,
|
|
470
|
+
durationSec: Math.round((Date.now() - startedAtMs) / 1000),
|
|
471
|
+
committed: didCommit,
|
|
472
|
+
pushed: true,
|
|
473
|
+
retriedAfterRebase: true,
|
|
474
|
+
branch,
|
|
475
|
+
...autoResolveFieldsFinal,
|
|
476
|
+
}, stderr);
|
|
477
|
+
return 0;
|
|
478
|
+
} finally {
|
|
479
|
+
await lock.release();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Attempt to auto-resolve an in-progress rebase whose conflicts are all
|
|
484
|
+
// on regenerated knowledge artifacts (see `regenerated-artifacts.mjs`).
|
|
485
|
+
// Preferring origin's version is safe here because the next distillation
|
|
486
|
+
// cycle will regenerate again with combined inputs — nothing is lost.
|
|
487
|
+
//
|
|
488
|
+
// Returns:
|
|
489
|
+
// { ok: true, paths: [...] } — resolved, rebase continued
|
|
490
|
+
// { ok: false, reason: '<short>', disallowed?: [...] } — do NOT continue
|
|
491
|
+
async function tryAutoResolveRebase({ runner, repoRoot, stderr }) {
|
|
492
|
+
const conflicts = await listConflictedPaths(runner, { cwd: repoRoot });
|
|
493
|
+
if (conflicts.code !== 0) {
|
|
494
|
+
return { ok: false, reason: `git status (post-rebase) failed: ${conflicts.stderr.trim()}` };
|
|
495
|
+
}
|
|
496
|
+
if (conflicts.paths.length === 0) {
|
|
497
|
+
// pull --rebase failed but no unmerged entries — not a content
|
|
498
|
+
// conflict. Could be untracked-would-be-overwritten, empty commit,
|
|
499
|
+
// etc. Nothing safe to auto-resolve.
|
|
500
|
+
return { ok: false, reason: 'no unmerged paths detected — not a content conflict' };
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const cfg = await loadRegeneratedArtifacts({ repoRoot });
|
|
504
|
+
if (cfg.warning) {
|
|
505
|
+
log(`.sync-config.json warning: ${cfg.warning} — falling back to defaults`, { stderr });
|
|
506
|
+
}
|
|
507
|
+
const { allowed, disallowed } = classifyConflicts(conflicts.paths, cfg.patterns);
|
|
508
|
+
if (disallowed.length > 0) {
|
|
509
|
+
return {
|
|
510
|
+
ok: false,
|
|
511
|
+
reason: `conflict on ${disallowed.length} path(s) outside the regenerated-artifacts allowlist (${disallowed.join(', ')}); manual reconciliation required`,
|
|
512
|
+
disallowed,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// All conflicts are allowlisted. Prefer origin's version, stage, and
|
|
517
|
+
// continue the rebase.
|
|
518
|
+
const co = await checkoutOursDuringRebase(runner, { cwd: repoRoot, paths: allowed });
|
|
519
|
+
if (co.code !== 0) {
|
|
520
|
+
return { ok: false, reason: `git checkout --ours failed: ${co.stderr.trim()}` };
|
|
521
|
+
}
|
|
522
|
+
const add = await addPaths(runner, { cwd: repoRoot, paths: allowed });
|
|
523
|
+
if (add.code !== 0) {
|
|
524
|
+
return { ok: false, reason: `git add (post-resolve) failed: ${add.stderr.trim()}` };
|
|
525
|
+
}
|
|
526
|
+
const cont = await rebaseContinue(runner, { cwd: repoRoot });
|
|
527
|
+
if (cont.code !== 0) {
|
|
528
|
+
return { ok: false, reason: `git rebase --continue failed: ${cont.stderr.trim()}` };
|
|
529
|
+
}
|
|
530
|
+
return { ok: true, paths: allowed };
|
|
531
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// sync — chokepoint for external effects.
|
|
2
|
+
//
|
|
3
|
+
// Every process spawn and clock read funnels through here. This module is
|
|
4
|
+
// the ONLY place that touches the outside world at runtime. Tests inject a
|
|
5
|
+
// fake `spawner` to assert on argv without spawning real processes.
|
|
6
|
+
//
|
|
7
|
+
// Contract (mirrors distill/src/runner.mjs):
|
|
8
|
+
// - spawn(cmd, args, { cwd?, env?, capture? }) -> { code, stdout, stderr }
|
|
9
|
+
// * No shell interpolation. Arguments are passed as an array with
|
|
10
|
+
// shell:false. User-supplied data (paths) flows through untouched.
|
|
11
|
+
// * capture:true (default) collects stdout/stderr into strings.
|
|
12
|
+
// - now() -> Date — single source of wall-clock time, for testing.
|
|
13
|
+
// - tsIso() -> strict RFC-3339 / ISO-8601 UTC timestamp `YYYY-MM-DDTHH:MM:SSZ`
|
|
14
|
+
// (second precision). Safe to round-trip via `new Date()`.
|
|
15
|
+
|
|
16
|
+
import { spawn as realSpawn } from 'node:child_process';
|
|
17
|
+
|
|
18
|
+
export function createRunner({ spawner = realSpawn, clock = () => new Date() } = {}) {
|
|
19
|
+
return {
|
|
20
|
+
async spawn(cmd, args, { cwd, env, capture = true } = {}) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const child = spawner(cmd, args, {
|
|
23
|
+
shell: false,
|
|
24
|
+
cwd,
|
|
25
|
+
env: env ?? process.env,
|
|
26
|
+
stdio: capture ? ['pipe', 'pipe', 'pipe'] : ['pipe', 'inherit', 'inherit'],
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
let stdout = '';
|
|
30
|
+
let stderr = '';
|
|
31
|
+
|
|
32
|
+
if (capture) {
|
|
33
|
+
child.stdout.on('data', (d) => {
|
|
34
|
+
stdout += d.toString('utf8');
|
|
35
|
+
});
|
|
36
|
+
child.stderr.on('data', (d) => {
|
|
37
|
+
stderr += d.toString('utf8');
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
child.on('error', (err) => reject(err));
|
|
42
|
+
child.on('close', (code) => resolve({ code: code ?? 0, stdout, stderr }));
|
|
43
|
+
child.stdin.end();
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
now() {
|
|
48
|
+
return clock();
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
tsIso() {
|
|
52
|
+
const d = clock();
|
|
53
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
54
|
+
return (
|
|
55
|
+
`${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
|
|
56
|
+
`T${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}Z`
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const defaultRunner = createRunner();
|