@lsctech/polaris 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/README.md +87 -0
- package/dist/__mocks__/tool-server-linear.js +34 -0
- package/dist/cli/adopt-approve.js +60 -0
- package/dist/cli/adopt-cognition.js +199 -0
- package/dist/cli/adopt-instructions.js +143 -0
- package/dist/cli/adopt-rules.js +169 -0
- package/dist/cli/adopt-scan.js +25 -0
- package/dist/cli/adopt-smartdocs.js +192 -0
- package/dist/cli/adoption-inventory.js +350 -0
- package/dist/cli/adoption-plan.js +222 -0
- package/dist/cli/args.js +28 -0
- package/dist/cli/args.test.js +30 -0
- package/dist/cli/branding.js +41 -0
- package/dist/cli/graph.js +358 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/index.test.js +162 -0
- package/dist/cli/init-detect.js +61 -0
- package/dist/cli/init.js +492 -0
- package/dist/cli/init.test.js +259 -0
- package/dist/cli/librarian.js +32 -0
- package/dist/cli/medic.js +96 -0
- package/dist/cli/startup.test.js +23 -0
- package/dist/cli/tracker.js +130 -0
- package/dist/cli/tracker.test.js +126 -0
- package/dist/cli/version.js +10 -0
- package/dist/cli/version.test.js +11 -0
- package/dist/cli/worker.js +287 -0
- package/dist/cluster-state/fixtures/test-clusters.json +19 -0
- package/dist/cluster-state/store.js +302 -0
- package/dist/cluster-state/store.test.js +145 -0
- package/dist/cluster-state/types.js +2 -0
- package/dist/cognition/archive.js +158 -0
- package/dist/cognition/closeout-librarian-types.js +161 -0
- package/dist/cognition/cognition.test.js +623 -0
- package/dist/cognition/index.js +41 -0
- package/dist/cognition/librarian-dispatch.js +391 -0
- package/dist/cognition/librarian-dispatch.test.js +247 -0
- package/dist/cognition/librarian-packet.js +313 -0
- package/dist/cognition/librarian-types.js +57 -0
- package/dist/cognition/route-cognition-delta.js +350 -0
- package/dist/cognition/summary-delta.js +279 -0
- package/dist/cognition/validate.js +162 -0
- package/dist/config/defaults.js +98 -0
- package/dist/config/doctor.js +273 -0
- package/dist/config/index.js +14 -0
- package/dist/config/loader.js +58 -0
- package/dist/config/provider-detect.js +60 -0
- package/dist/config/provider-detect.test.js +119 -0
- package/dist/config/schema.js +2 -0
- package/dist/config/show.js +44 -0
- package/dist/config/show.test.js +42 -0
- package/dist/config/validator.js +675 -0
- package/dist/config/validator.test.js +77 -0
- package/dist/docs/canon-check.js +334 -0
- package/dist/docs/canon-check.test.js +207 -0
- package/dist/docs/doctrine.js +133 -0
- package/dist/docs/doctrine.test.js +139 -0
- package/dist/docs/index.js +168 -0
- package/dist/docs/index.test.js +83 -0
- package/dist/docs/ingest.js +295 -0
- package/dist/docs/ingest.test.js +71 -0
- package/dist/docs/migrate.js +165 -0
- package/dist/docs/migrate.test.js +136 -0
- package/dist/docs/seed-instructions.js +147 -0
- package/dist/docs/seed-instructions.test.js +137 -0
- package/dist/docs/validate-instructions.js +267 -0
- package/dist/docs/validate-instructions.test.js +238 -0
- package/dist/finalize/artifact-policy.js +214 -0
- package/dist/finalize/artifact-policy.test.js +84 -0
- package/dist/finalize/delivery-integrity.js +141 -0
- package/dist/finalize/finalize.test.js +210 -0
- package/dist/finalize/github.js +41 -0
- package/dist/finalize/index.js +474 -0
- package/dist/finalize/linear.js +213 -0
- package/dist/finalize/run-report.js +47 -0
- package/dist/finalize/steps/01-map-update.js +11 -0
- package/dist/finalize/steps/02-map-validate.js +11 -0
- package/dist/finalize/steps/03-schema-validate.js +11 -0
- package/dist/finalize/steps/04-run-checks.js +118 -0
- package/dist/finalize/steps/05-generate-report.js +14 -0
- package/dist/finalize/steps/06-commit.js +39 -0
- package/dist/finalize/steps/07-push.js +10 -0
- package/dist/finalize/steps/08-create-pr.js +9 -0
- package/dist/finalize/steps/09-update-state.js +9 -0
- package/dist/finalize/steps/10-append-jsonl.js +23 -0
- package/dist/finalize/steps/11-update-linear.js +22 -0
- package/dist/finalize/steps/12-archive.js +21 -0
- package/dist/graph/adapter/c/adapter.js +45 -0
- package/dist/graph/adapter/c/extract.js +135 -0
- package/dist/graph/adapter/c/index.js +10 -0
- package/dist/graph/adapter/c/runtime.js +67 -0
- package/dist/graph/adapter/cpp/adapter.js +45 -0
- package/dist/graph/adapter/cpp/extract.js +159 -0
- package/dist/graph/adapter/cpp/index.js +10 -0
- package/dist/graph/adapter/cpp/runtime.js +61 -0
- package/dist/graph/adapter/csharp/adapter.js +45 -0
- package/dist/graph/adapter/csharp/extract.js +127 -0
- package/dist/graph/adapter/csharp/index.js +10 -0
- package/dist/graph/adapter/csharp/runtime.js +61 -0
- package/dist/graph/adapter/dart/adapter.js +56 -0
- package/dist/graph/adapter/dart/extract.js +130 -0
- package/dist/graph/adapter/dart/index.js +10 -0
- package/dist/graph/adapter/dart/runtime.js +61 -0
- package/dist/graph/adapter/go/adapter.js +45 -0
- package/dist/graph/adapter/go/extract.js +112 -0
- package/dist/graph/adapter/go/index.js +10 -0
- package/dist/graph/adapter/go/runtime.js +58 -0
- package/dist/graph/adapter/kotlin-java/extract-common.js +83 -0
- package/dist/graph/adapter/kotlin-java/index.js +15 -0
- package/dist/graph/adapter/kotlin-java/java-adapter.js +45 -0
- package/dist/graph/adapter/kotlin-java/java-extract.js +42 -0
- package/dist/graph/adapter/kotlin-java/kotlin-adapter.js +48 -0
- package/dist/graph/adapter/kotlin-java/kotlin-extract.js +58 -0
- package/dist/graph/adapter/kotlin-java/runtime.js +75 -0
- package/dist/graph/adapter/python/adapter.js +45 -0
- package/dist/graph/adapter/python/extract.js +121 -0
- package/dist/graph/adapter/python/index.js +10 -0
- package/dist/graph/adapter/python/runtime.js +70 -0
- package/dist/graph/adapter/registry.js +82 -0
- package/dist/graph/adapter/rust/adapter.js +45 -0
- package/dist/graph/adapter/rust/extract.js +110 -0
- package/dist/graph/adapter/rust/index.js +10 -0
- package/dist/graph/adapter/rust/runtime.js +74 -0
- package/dist/graph/adapter/shell/adapter.js +34 -0
- package/dist/graph/adapter/shell/extract.js +98 -0
- package/dist/graph/adapter/shell/index.js +8 -0
- package/dist/graph/adapter/svelte/adapter.js +91 -0
- package/dist/graph/adapter/svelte/extract.js +41 -0
- package/dist/graph/adapter/svelte/index.js +8 -0
- package/dist/graph/adapter/swift/adapter.js +45 -0
- package/dist/graph/adapter/swift/extract.js +120 -0
- package/dist/graph/adapter/swift/index.js +10 -0
- package/dist/graph/adapter/swift/runtime.js +70 -0
- package/dist/graph/adapter/types.js +2 -0
- package/dist/graph/adapter/typescript-javascript/adapter.js +72 -0
- package/dist/graph/adapter/typescript-javascript/extract.js +115 -0
- package/dist/graph/adapter/typescript-javascript/index.js +10 -0
- package/dist/graph/adapter/typescript-javascript/runtime.js +97 -0
- package/dist/graph/capability/index.js +105 -0
- package/dist/graph/governance.js +104 -0
- package/dist/graph/parser/extract.js +5 -0
- package/dist/graph/parser/loader.js +2 -0
- package/dist/graph/parser/pipeline.js +194 -0
- package/dist/graph/query/index.js +277 -0
- package/dist/graph/query/types.js +5 -0
- package/dist/graph/resolver/build-edges.js +211 -0
- package/dist/graph/resolver/index.js +23 -0
- package/dist/graph/resolver/resolve-imports.js +168 -0
- package/dist/graph/store/adapter.js +132 -0
- package/dist/graph/store/queries.js +124 -0
- package/dist/graph/store/types.js +4 -0
- package/dist/ignore/defaults.js +41 -0
- package/dist/ignore/parser.js +39 -0
- package/dist/lint/map-reference-validator.js +72 -0
- package/dist/loop/abort.js +188 -0
- package/dist/loop/abort.test.js +151 -0
- package/dist/loop/adapters/__tests__/terminal-cli.test.js +92 -0
- package/dist/loop/adapters/agent-subtask.js +137 -0
- package/dist/loop/adapters/agent-subtask.test.js +151 -0
- package/dist/loop/adapters/cli-subtask-bridge.js +108 -0
- package/dist/loop/adapters/index.js +9 -0
- package/dist/loop/adapters/registry.js +17 -0
- package/dist/loop/adapters/terminal-cli.js +403 -0
- package/dist/loop/adapters/types.js +2 -0
- package/dist/loop/adapters/worker-instructions.js +85 -0
- package/dist/loop/body-parser.js +226 -0
- package/dist/loop/bootstrap-packet.js +97 -0
- package/dist/loop/bootstrap-packet.test.js +171 -0
- package/dist/loop/branch-governance.js +54 -0
- package/dist/loop/budget.js +63 -0
- package/dist/loop/budget.test.js +163 -0
- package/dist/loop/checkpoint.js +325 -0
- package/dist/loop/compact-return.js +59 -0
- package/dist/loop/continue.js +645 -0
- package/dist/loop/continue.test.js +337 -0
- package/dist/loop/dispatch-boundary.js +335 -0
- package/dist/loop/dispatch-state.js +223 -0
- package/dist/loop/dispatch-state.test.js +186 -0
- package/dist/loop/dispatch.js +1236 -0
- package/dist/loop/dispatch.test.js +499 -0
- package/dist/loop/ephemeral-smoke.test.js +160 -0
- package/dist/loop/evidence-backfill.js +216 -0
- package/dist/loop/execution-adapter.js +141 -0
- package/dist/loop/execution-adapter.test.js +66 -0
- package/dist/loop/finalize-evidence.js +169 -0
- package/dist/loop/git-custody.js +250 -0
- package/dist/loop/impl-packet-input.js +38 -0
- package/dist/loop/index.js +270 -0
- package/dist/loop/ledger.js +49 -0
- package/dist/loop/ledger.test.js +127 -0
- package/dist/loop/lifecycle-dispatch.js +348 -0
- package/dist/loop/lifecycle-dispatch.test.js +318 -0
- package/dist/loop/lifecycle.js +93 -0
- package/dist/loop/lifecycle.test.js +150 -0
- package/dist/loop/orphan-recovery.js +276 -0
- package/dist/loop/orphan-recovery.test.js +236 -0
- package/dist/loop/parent.js +1566 -0
- package/dist/loop/parent.test.js +738 -0
- package/dist/loop/provider-sealed-packet.test.js +164 -0
- package/dist/loop/resume.js +295 -0
- package/dist/loop/resume.test.js +172 -0
- package/dist/loop/run-bootstrap.js +234 -0
- package/dist/loop/run-preflight.js +203 -0
- package/dist/loop/status.js +665 -0
- package/dist/loop/status.test.js +235 -0
- package/dist/loop/wait.js +86 -0
- package/dist/loop/worker-packet.js +406 -0
- package/dist/loop/worker-packet.test.js +243 -0
- package/dist/loop/worker-prompt.js +260 -0
- package/dist/loop/worker-prompt.test.js +247 -0
- package/dist/loop/worker.js +498 -0
- package/dist/loop/worker.test.js +372 -0
- package/dist/map/atlas.js +77 -0
- package/dist/map/backfill.js +171 -0
- package/dist/map/backfill.test.js +120 -0
- package/dist/map/index.js +255 -0
- package/dist/map/inference.js +73 -0
- package/dist/map/inference.test.js +35 -0
- package/dist/map/query.js +157 -0
- package/dist/map/query.test.js +174 -0
- package/dist/map/update.js +355 -0
- package/dist/map/update.test.js +67 -0
- package/dist/map/validate.js +153 -0
- package/dist/map/validate.test.js +90 -0
- package/dist/map/welfare.js +86 -0
- package/dist/mcp/lib/invoke.js +35 -0
- package/dist/mcp/lib/redact.js +24 -0
- package/dist/mcp/lib/redact.test.js +112 -0
- package/dist/mcp/lib/root.js +41 -0
- package/dist/mcp/lib/root.test.js +114 -0
- package/dist/mcp/server.js +15 -0
- package/dist/mcp/tools/claim-child.js +119 -0
- package/dist/mcp/tools/claim-child.test.js +69 -0
- package/dist/mcp/tools/current-state.js +73 -0
- package/dist/mcp/tools/current-state.test.js +114 -0
- package/dist/mcp/tools/dispatch-result.js +186 -0
- package/dist/mcp/tools/dispatch-result.test.js +109 -0
- package/dist/mcp/tools/index.js +222 -0
- package/dist/mcp/tools/index.test.js +172 -0
- package/dist/mcp/tools/linear.js +74 -0
- package/dist/mcp/tools/loop-continue.js +159 -0
- package/dist/mcp/tools/loop-continue.test.js +186 -0
- package/dist/mcp/tools/loop-dry-run.js +15 -0
- package/dist/mcp/tools/status.js +65 -0
- package/dist/medic/chart-id.js +75 -0
- package/dist/medic/chart-schema.js +139 -0
- package/dist/runs/index.js +119 -0
- package/dist/runs/index.test.js +184 -0
- package/dist/runs/ledger-tail.js +15 -0
- package/dist/runs/list.js +29 -0
- package/dist/runs/reconcile.js +74 -0
- package/dist/runs/show.js +10 -0
- package/dist/runtime/audit/logger.js +15 -0
- package/dist/runtime/audit/reader.js +42 -0
- package/dist/runtime/checkpoint.js +89 -0
- package/dist/runtime/continuation/confirmed.js +225 -0
- package/dist/runtime/continuation/confirmed.test.js +421 -0
- package/dist/runtime/continuation/dry-run.js +74 -0
- package/dist/runtime/execution-window.js +106 -0
- package/dist/runtime/scheduling/child-selector.js +16 -0
- package/dist/runtime/state.js +43 -0
- package/dist/runtime/status.js +46 -0
- package/dist/runtime/verification/envelope.js +80 -0
- package/dist/runtime/verification/fingerprint.js +26 -0
- package/dist/skill-packet/cli.test.js +105 -0
- package/dist/skill-packet/generator.js +225 -0
- package/dist/skill-packet/generator.test.js +159 -0
- package/dist/skill-packet/index.js +49 -0
- package/dist/skill-packet/types.js +2 -0
- package/dist/smartdocs-engine/audit.js +194 -0
- package/dist/smartdocs-engine/audit.test.js +122 -0
- package/dist/smartdocs-engine/canon-check.js +340 -0
- package/dist/smartdocs-engine/canon-check.test.js +234 -0
- package/dist/smartdocs-engine/doctrine.js +633 -0
- package/dist/smartdocs-engine/doctrine.test.js +374 -0
- package/dist/smartdocs-engine/index.js +456 -0
- package/dist/smartdocs-engine/index.test.js +82 -0
- package/dist/smartdocs-engine/ingest.js +495 -0
- package/dist/smartdocs-engine/ingest.test.js +163 -0
- package/dist/smartdocs-engine/migrate.js +217 -0
- package/dist/smartdocs-engine/migrate.test.js +200 -0
- package/dist/smartdocs-engine/seed-instructions.js +335 -0
- package/dist/smartdocs-engine/seed-instructions.test.js +275 -0
- package/dist/smartdocs-engine/smartdoc-ignore.js +258 -0
- package/dist/smartdocs-engine/smartdoc-ignore.test.js +146 -0
- package/dist/smartdocs-engine/validate-instructions.js +366 -0
- package/dist/smartdocs-engine/validate-instructions.test.js +283 -0
- package/dist/tracker/adapters/github/index.js +305 -0
- package/dist/tracker/adapters/jira/index.js +293 -0
- package/dist/tracker/adapters/linear/http-errors.test.js +51 -0
- package/dist/tracker/adapters/linear/index.js +621 -0
- package/dist/tracker/adapters/linear/index.test.js +238 -0
- package/dist/tracker/adapters/linear.js +50 -0
- package/dist/tracker/adapters/local/index.js +10 -0
- package/dist/tracker/adapters/local-file/index.js +159 -0
- package/dist/tracker/adapters/mcp-bridge.js +158 -0
- package/dist/tracker/adapters/mcp-bridge.test.js +12 -0
- package/dist/tracker/adapters/spec/index.js +146 -0
- package/dist/tracker/capabilities.js +2 -0
- package/dist/tracker/index.js +49 -0
- package/dist/tracker/lifecycle-policy.js +139 -0
- package/dist/tracker/lifecycle-transition.js +164 -0
- package/dist/tracker/local-graph.js +98 -0
- package/dist/tracker/migration.js +56 -0
- package/dist/tracker/schema.js +86 -0
- package/dist/tracker/sync/index.js +404 -0
- package/dist/tracker/sync/queue-store.js +95 -0
- package/dist/tracker/sync/queue-store.test.js +104 -0
- package/dist/tracker/types.js +11 -0
- package/dist/types/linear.js +3 -0
- package/dist/types/result-packet.js +12 -0
- package/dist/types/runtime-state.js +2 -0
- package/dist/types/work-contract.js +2 -0
- package/dist/utils/monotonic-timestamp.js +13 -0
- package/dist/utils/monotonic-timestamp.test.js +19 -0
- package/package.json +64 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Issue body section parser.
|
|
4
|
+
*
|
|
5
|
+
* Parses a markdown issue body into structured fields used by the packet
|
|
6
|
+
* generator. Sections are identified by `## Header` lines (case-insensitive).
|
|
7
|
+
* List items are lines starting with `- ` or `* `.
|
|
8
|
+
*
|
|
9
|
+
* ## Canonical section format
|
|
10
|
+
*
|
|
11
|
+
* All implementation issues (parents and children) must use this exact format:
|
|
12
|
+
*
|
|
13
|
+
* ## Objective — one-sentence statement of what this issue accomplishes
|
|
14
|
+
* ## Context — why this issue exists
|
|
15
|
+
* ## Goal — specific implementation outcome
|
|
16
|
+
* ## Scope — machine-readable list of allowed paths/globs (REQUIRED)
|
|
17
|
+
* ## Acceptance Criteria — checklist of observable completion requirements
|
|
18
|
+
* ## Validation — commands that must be run (REQUIRED)
|
|
19
|
+
* ## Ordering — dependencies or sequencing relative to siblings
|
|
20
|
+
* ## Non-goals — what this issue must not change
|
|
21
|
+
*
|
|
22
|
+
* Use `## Scope` exactly — not "Implementation scope", "Expected code areas",
|
|
23
|
+
* or other variants. Aliases are supported for backward compatibility only.
|
|
24
|
+
*
|
|
25
|
+
* If scope cannot be determined, write:
|
|
26
|
+
* ## Scope
|
|
27
|
+
* - TBD — BLOCKED: scope missing
|
|
28
|
+
* and mark the Linear issue as Blocked. Do NOT invent paths.
|
|
29
|
+
*
|
|
30
|
+
* ## Scope inheritance precedence
|
|
31
|
+
*
|
|
32
|
+
* When `buildPacket` resolves `allowed_scope` for a worker packet, it applies
|
|
33
|
+
* the following rules in order (first non-empty result wins):
|
|
34
|
+
*
|
|
35
|
+
* 1. Explicit `allowedScope` passed to `compileImplPacket` by the caller.
|
|
36
|
+
* 2. Child issue body `## Scope` section.
|
|
37
|
+
* 3. Cluster-root (parent) body `## Scope` section — fallback only when the
|
|
38
|
+
* child body has no scope section at all.
|
|
39
|
+
* 4. Empty or TBD-blocked → preflight gate halts dispatch with
|
|
40
|
+
* `preflight-scope-missing`.
|
|
41
|
+
*
|
|
42
|
+
* Rule 3 means a parent implementation plan can declare a cluster-wide scope
|
|
43
|
+
* that all child issues inherit when they omit their own scope section.
|
|
44
|
+
* A child that DOES declare its own scope section always overrides the parent —
|
|
45
|
+
* the parent scope is never merged with or appended to the child scope.
|
|
46
|
+
*
|
|
47
|
+
* Analyze children are exempt from scope preflight (they don't produce impl
|
|
48
|
+
* packets and don't need code-area constraints).
|
|
49
|
+
*/
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.parseIssueBody = parseIssueBody;
|
|
52
|
+
exports.validateCanonicalSections = validateCanonicalSections;
|
|
53
|
+
/**
|
|
54
|
+
* Canonical scope header. Aliases kept for backward compatibility only.
|
|
55
|
+
* New issues must use `## Scope` exactly.
|
|
56
|
+
*/
|
|
57
|
+
const SCOPE_HEADERS = new Set([
|
|
58
|
+
'scope',
|
|
59
|
+
'expected code areas',
|
|
60
|
+
'code areas',
|
|
61
|
+
'files to change',
|
|
62
|
+
'files',
|
|
63
|
+
]);
|
|
64
|
+
const VALIDATION_HEADERS = new Set([
|
|
65
|
+
'validation',
|
|
66
|
+
'validation commands',
|
|
67
|
+
'test commands',
|
|
68
|
+
'verify',
|
|
69
|
+
]);
|
|
70
|
+
const REQUIREMENTS_HEADERS = new Set([
|
|
71
|
+
'acceptance criteria',
|
|
72
|
+
'requirements',
|
|
73
|
+
'criteria',
|
|
74
|
+
]);
|
|
75
|
+
const ORDERING_HEADERS = new Set([
|
|
76
|
+
'ordering',
|
|
77
|
+
'dependencies / blockers',
|
|
78
|
+
'dependencies',
|
|
79
|
+
'order',
|
|
80
|
+
]);
|
|
81
|
+
const NON_GOALS_HEADERS = new Set([
|
|
82
|
+
'non-goals',
|
|
83
|
+
'non goals',
|
|
84
|
+
'out of scope',
|
|
85
|
+
]);
|
|
86
|
+
const OBJECTIVE_HEADERS = new Set(['objective']);
|
|
87
|
+
const CONTEXT_HEADERS = new Set(['context']);
|
|
88
|
+
const GOAL_HEADERS = new Set(['goal', 'goals']);
|
|
89
|
+
/** Pattern that identifies a TBD-BLOCKED scope marker. */
|
|
90
|
+
const TBD_BLOCKED_RE = /^tbd\b/i;
|
|
91
|
+
/**
|
|
92
|
+
* Canonical sections required on every implementation issue.
|
|
93
|
+
* Used by `validateCanonicalSections` to report missing sections.
|
|
94
|
+
*/
|
|
95
|
+
const CANONICAL_SECTION_CHECKS = [
|
|
96
|
+
{ name: 'Objective', headers: OBJECTIVE_HEADERS },
|
|
97
|
+
{ name: 'Context', headers: CONTEXT_HEADERS },
|
|
98
|
+
{ name: 'Goal', headers: GOAL_HEADERS },
|
|
99
|
+
{ name: 'Scope', headers: SCOPE_HEADERS },
|
|
100
|
+
{ name: 'Acceptance Criteria', headers: REQUIREMENTS_HEADERS },
|
|
101
|
+
{ name: 'Validation', headers: VALIDATION_HEADERS },
|
|
102
|
+
{ name: 'Ordering', headers: ORDERING_HEADERS },
|
|
103
|
+
{ name: 'Non-goals', headers: NON_GOALS_HEADERS },
|
|
104
|
+
];
|
|
105
|
+
/**
|
|
106
|
+
* Split a markdown text into sections keyed by their normalized `##` header lines.
|
|
107
|
+
*
|
|
108
|
+
* @returns A Map where each key is a section header normalized by trimming and lowercasing, and each value is the raw content following that header (excluding the header line).
|
|
109
|
+
*/
|
|
110
|
+
function parseSections(body) {
|
|
111
|
+
const sections = new Map();
|
|
112
|
+
const parts = body.split(/^##\s+/m);
|
|
113
|
+
for (const part of parts) {
|
|
114
|
+
const nl = part.indexOf('\n');
|
|
115
|
+
if (nl === -1)
|
|
116
|
+
continue;
|
|
117
|
+
const header = part.slice(0, nl).trim().toLowerCase();
|
|
118
|
+
const content = part.slice(nl + 1);
|
|
119
|
+
sections.set(header, content);
|
|
120
|
+
}
|
|
121
|
+
return sections;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Extracts bullet list items from the given text and returns them as trimmed strings.
|
|
125
|
+
*
|
|
126
|
+
* @param text - Section content to scan for bullet list lines (lines starting with `- ` or `* `)
|
|
127
|
+
* @returns The extracted list item strings, trimmed of whitespace; empty items are omitted.
|
|
128
|
+
*/
|
|
129
|
+
function parseListItems(text) {
|
|
130
|
+
return text
|
|
131
|
+
.split('\n')
|
|
132
|
+
.filter((line) => /^\s*[-*]\s/.test(line))
|
|
133
|
+
.map((line) => line.replace(/^\s*[-*]\s+/, '').trim())
|
|
134
|
+
.filter((s) => s.length > 0);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Extracts list items from the first section whose header matches a provided set.
|
|
138
|
+
*
|
|
139
|
+
* @param sections - Map of normalized header names to their section content; iteration follows the map's order
|
|
140
|
+
* @param headers - Set of normalized header names to match against section headers
|
|
141
|
+
* @returns An array of parsed list-item strings from the first matching section, or an empty array if no match is found
|
|
142
|
+
*/
|
|
143
|
+
function findSection(sections, headers) {
|
|
144
|
+
for (const [header, content] of sections) {
|
|
145
|
+
if (headers.has(header)) {
|
|
146
|
+
return parseListItems(content);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Returns trimmed prose text from the first section matching a header set.
|
|
153
|
+
* Used for single-paragraph sections (Objective, Context, Goal).
|
|
154
|
+
*/
|
|
155
|
+
function findSectionText(sections, headers) {
|
|
156
|
+
for (const [header, content] of sections) {
|
|
157
|
+
if (headers.has(header)) {
|
|
158
|
+
return content.trim();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Parse a markdown issue body into structured fields.
|
|
165
|
+
*
|
|
166
|
+
* Recognizes `##` sections and extracts bullet list items (`-` or `*`) from the first matching header for each field.
|
|
167
|
+
* Prose sections (Objective, Context, Goal) are returned as trimmed text.
|
|
168
|
+
*
|
|
169
|
+
* TBD-blocked scope: if every item in the `## Scope` section begins with "TBD"
|
|
170
|
+
* the scope is treated as empty (`scope: []`) and `scopeBlocked` is set to `true`.
|
|
171
|
+
* This ensures the preflight gate fires instead of dispatching a worker with an
|
|
172
|
+
* unusable scope list.
|
|
173
|
+
*
|
|
174
|
+
* @param body - The markdown issue body to parse.
|
|
175
|
+
* @returns Structured fields; each array/string is empty when the input is empty/whitespace or no matching section is found.
|
|
176
|
+
*/
|
|
177
|
+
function parseIssueBody(body) {
|
|
178
|
+
if (!body || !body.trim()) {
|
|
179
|
+
return {
|
|
180
|
+
scope: [],
|
|
181
|
+
scopeBlocked: false,
|
|
182
|
+
validationCommands: [],
|
|
183
|
+
requirements: [],
|
|
184
|
+
objective: '',
|
|
185
|
+
context: '',
|
|
186
|
+
goal: '',
|
|
187
|
+
ordering: [],
|
|
188
|
+
nonGoals: [],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const sections = parseSections(body);
|
|
192
|
+
const rawScope = findSection(sections, SCOPE_HEADERS);
|
|
193
|
+
const scopeBlocked = rawScope.length > 0 && rawScope.every((item) => TBD_BLOCKED_RE.test(item));
|
|
194
|
+
const filteredScope = rawScope.filter((item) => !TBD_BLOCKED_RE.test(item));
|
|
195
|
+
return {
|
|
196
|
+
scope: scopeBlocked ? [] : filteredScope,
|
|
197
|
+
scopeBlocked,
|
|
198
|
+
validationCommands: findSection(sections, VALIDATION_HEADERS),
|
|
199
|
+
requirements: findSection(sections, REQUIREMENTS_HEADERS),
|
|
200
|
+
objective: findSectionText(sections, OBJECTIVE_HEADERS),
|
|
201
|
+
context: findSectionText(sections, CONTEXT_HEADERS),
|
|
202
|
+
goal: findSectionText(sections, GOAL_HEADERS),
|
|
203
|
+
ordering: findSection(sections, ORDERING_HEADERS),
|
|
204
|
+
nonGoals: findSection(sections, NON_GOALS_HEADERS),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Checks a markdown issue body for the presence of all 8 required canonical sections.
|
|
209
|
+
*
|
|
210
|
+
* @param body - The markdown issue body to validate.
|
|
211
|
+
* @returns Names of any canonical sections that are absent. Empty array means the body is fully canonical.
|
|
212
|
+
*/
|
|
213
|
+
function validateCanonicalSections(body) {
|
|
214
|
+
if (!body || !body.trim()) {
|
|
215
|
+
return CANONICAL_SECTION_CHECKS.map((c) => c.name);
|
|
216
|
+
}
|
|
217
|
+
const sections = parseSections(body);
|
|
218
|
+
const sectionKeys = [...sections.keys()];
|
|
219
|
+
const missing = [];
|
|
220
|
+
for (const { name, headers } of CANONICAL_SECTION_CHECKS) {
|
|
221
|
+
if (!sectionKeys.some((key) => headers.has(key))) {
|
|
222
|
+
missing.push(name);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return missing;
|
|
226
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildBootstrapPacket = buildBootstrapPacket;
|
|
4
|
+
exports.writeBootstrapPacket = writeBootstrapPacket;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const execution_adapter_js_1 = require("./execution-adapter.js");
|
|
9
|
+
function getCurrentBranch(repoRoot) {
|
|
10
|
+
try {
|
|
11
|
+
return (0, node_child_process_1.execFileSync)("git", ["branch", "--show-current"], {
|
|
12
|
+
cwd: repoRoot,
|
|
13
|
+
encoding: "utf-8",
|
|
14
|
+
}).trim();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "unknown";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function getHeadSha(repoRoot) {
|
|
21
|
+
try {
|
|
22
|
+
return (0, node_child_process_1.execFileSync)("git", ["rev-parse", "HEAD"], {
|
|
23
|
+
cwd: repoRoot,
|
|
24
|
+
encoding: "utf-8",
|
|
25
|
+
}).trim();
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function buildBootstrapPacket(state, stateFile, currentStateSha, repoRoot, completedChild, adapterMode, executionConfig, compactConfig) {
|
|
32
|
+
const branch = getCurrentBranch(repoRoot);
|
|
33
|
+
const nextChild = state.open_children[0] ?? null;
|
|
34
|
+
const maxChildren = state.context_budget.max_children_per_session ?? 3;
|
|
35
|
+
const filesTouched = state.context_budget.files_touched_total ?? 0;
|
|
36
|
+
const stopThresholdRemaining = maxChildren - state.context_budget.children_completed;
|
|
37
|
+
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
38
|
+
const telemetryFile = (0, node_path_1.join)(artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
39
|
+
// Resolve compact_mode: explicit level wins, then orchestratorMode, then default "standard"
|
|
40
|
+
const compactMode = compactConfig?.level ?? compactConfig?.orchestratorMode ?? "standard";
|
|
41
|
+
const resumeInstructions = nextChild
|
|
42
|
+
? `Run \`polaris loop resume ${state.run_id}\` on branch \`${branch}\` to continue with ${nextChild}.`
|
|
43
|
+
: `All children complete. Run \`polaris loop status\` to verify cluster state.`;
|
|
44
|
+
const adapterSelection = (0, execution_adapter_js_1.selectExecutionAdapter)({
|
|
45
|
+
configuredAdapter: adapterMode,
|
|
46
|
+
insideAgentSession: process.env.POLARIS_AGENT_SESSION === "1",
|
|
47
|
+
nativeSubtaskAvailable: process.env.POLARIS_NATIVE_SUBTASK === "1",
|
|
48
|
+
crossAgentConfigured: executionConfig?.allowCrossAgentFallback === true ||
|
|
49
|
+
process.env.POLARIS_ALLOW_CROSS_AGENT === "1",
|
|
50
|
+
tokenBudgetLow: process.env.POLARIS_TOKEN_BUDGET_LOW === "1",
|
|
51
|
+
});
|
|
52
|
+
const compactBootstrapState = (0, execution_adapter_js_1.buildCompactBootstrapState)({
|
|
53
|
+
runId: state.run_id,
|
|
54
|
+
clusterId: state.cluster_id,
|
|
55
|
+
childId: nextChild,
|
|
56
|
+
stateFile,
|
|
57
|
+
telemetryFile,
|
|
58
|
+
currentStateSha,
|
|
59
|
+
branch,
|
|
60
|
+
allowAnalyzeChildren: executionConfig?.allow_analyze_children,
|
|
61
|
+
compactMode,
|
|
62
|
+
});
|
|
63
|
+
// Normalize artifact_pointers to repo-relative paths
|
|
64
|
+
const relStateFile = stateFile.startsWith("/") ? (0, node_path_1.relative)(repoRoot, stateFile) : stateFile;
|
|
65
|
+
const relTelemetryFile = telemetryFile.startsWith("/") ? (0, node_path_1.relative)(repoRoot, telemetryFile) : telemetryFile;
|
|
66
|
+
return {
|
|
67
|
+
run_id: state.run_id,
|
|
68
|
+
skill: state.skill ?? "polaris-run",
|
|
69
|
+
branch,
|
|
70
|
+
base_commit_sha: getHeadSha(repoRoot),
|
|
71
|
+
last_completed_step: state.step_cursor,
|
|
72
|
+
last_completed_child: completedChild,
|
|
73
|
+
next_step: nextChild ? "03-execute-child" : "CLUSTER-COMPLETE",
|
|
74
|
+
open_children: state.open_children,
|
|
75
|
+
artifact_pointers: {
|
|
76
|
+
current_state: relStateFile,
|
|
77
|
+
telemetry: relTelemetryFile,
|
|
78
|
+
},
|
|
79
|
+
context_budget: {
|
|
80
|
+
children_completed: state.context_budget.children_completed,
|
|
81
|
+
files_touched_total: filesTouched,
|
|
82
|
+
stop_threshold_remaining: stopThresholdRemaining,
|
|
83
|
+
},
|
|
84
|
+
current_state_sha: currentStateSha,
|
|
85
|
+
resume_instructions: resumeInstructions,
|
|
86
|
+
compact_mode: compactMode,
|
|
87
|
+
execution_adapter: (0, execution_adapter_js_1.buildExecutionAdapterContract)(adapterSelection, compactBootstrapState),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function writeBootstrapPacket(packet, bootstrapDir) {
|
|
91
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
92
|
+
const filename = `${packet.run_id}-${timestamp}.json`;
|
|
93
|
+
const outPath = (0, node_path_1.join)(bootstrapDir, filename);
|
|
94
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(outPath), { recursive: true });
|
|
95
|
+
(0, node_fs_1.writeFileSync)(outPath, JSON.stringify(packet, null, 2), "utf-8");
|
|
96
|
+
return outPath;
|
|
97
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for src/loop/bootstrap-packet.ts
|
|
4
|
+
*
|
|
5
|
+
* Covers:
|
|
6
|
+
* - compact_mode propagation from compact config (all three levels)
|
|
7
|
+
* - compact_mode default (no config) → "standard"
|
|
8
|
+
* - level shorthand wins over orchestratorMode when level is set
|
|
9
|
+
* - packet shape sanity checks
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
const vitest_1 = require("vitest");
|
|
46
|
+
const child_process = __importStar(require("node:child_process"));
|
|
47
|
+
const node_path_1 = require("node:path");
|
|
48
|
+
const bootstrap_packet_js_1 = require("./bootstrap-packet.js");
|
|
49
|
+
vitest_1.vi.mock("node:child_process", async (importOriginal) => {
|
|
50
|
+
const actual = await importOriginal();
|
|
51
|
+
return { ...actual, execFileSync: vitest_1.vi.fn() };
|
|
52
|
+
});
|
|
53
|
+
const mockedExecFileSync = vitest_1.vi.mocked(child_process.execFileSync);
|
|
54
|
+
(0, vitest_1.beforeEach)(() => {
|
|
55
|
+
vitest_1.vi.resetAllMocks();
|
|
56
|
+
// Default git mocks: branch + HEAD sha
|
|
57
|
+
mockedExecFileSync.mockImplementation((_cmd, args) => {
|
|
58
|
+
const argsArr = args;
|
|
59
|
+
if (argsArr && argsArr[0] === "branch") {
|
|
60
|
+
return Buffer.from("feature/pol-120\n");
|
|
61
|
+
}
|
|
62
|
+
if (argsArr && argsArr[0] === "rev-parse") {
|
|
63
|
+
return Buffer.from("abc1234567890\n");
|
|
64
|
+
}
|
|
65
|
+
return Buffer.from("");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
const BASE_STATE = {
|
|
69
|
+
schema_version: "1.0",
|
|
70
|
+
run_id: "run-test-001",
|
|
71
|
+
cluster_id: "POL-114",
|
|
72
|
+
skill: "polaris-run",
|
|
73
|
+
active_child: "",
|
|
74
|
+
completed_children: ["POL-115"],
|
|
75
|
+
open_children: ["POL-120"],
|
|
76
|
+
step_cursor: "checkpoint",
|
|
77
|
+
context_budget: {
|
|
78
|
+
children_completed: 5,
|
|
79
|
+
files_touched_total: 20,
|
|
80
|
+
max_children_per_session: 6,
|
|
81
|
+
},
|
|
82
|
+
status: "running",
|
|
83
|
+
next_open_child: "POL-120",
|
|
84
|
+
artifact_dir: "/repo/.taskchain_artifacts/polaris-run",
|
|
85
|
+
};
|
|
86
|
+
(0, vitest_1.describe)("buildBootstrapPacket — compact_mode propagation", () => {
|
|
87
|
+
(0, vitest_1.it)("defaults compact_mode to 'standard' when no compactConfig is provided", () => {
|
|
88
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119");
|
|
89
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("standard");
|
|
90
|
+
});
|
|
91
|
+
(0, vitest_1.it)("defaults compact_mode to 'standard' when compactConfig is empty object", () => {
|
|
92
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, {});
|
|
93
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("standard");
|
|
94
|
+
});
|
|
95
|
+
(0, vitest_1.it)("sets compact_mode to 'standard' when level is 'standard'", () => {
|
|
96
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "standard" });
|
|
97
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("standard");
|
|
98
|
+
});
|
|
99
|
+
(0, vitest_1.it)("sets compact_mode to 'strict' when level is 'strict'", () => {
|
|
100
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "strict" });
|
|
101
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("strict");
|
|
102
|
+
});
|
|
103
|
+
(0, vitest_1.it)("sets compact_mode to 'minimal' when level is 'minimal'", () => {
|
|
104
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "minimal" });
|
|
105
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("minimal");
|
|
106
|
+
});
|
|
107
|
+
(0, vitest_1.it)("uses orchestratorMode when level is not set", () => {
|
|
108
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { orchestratorMode: "strict" });
|
|
109
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("strict");
|
|
110
|
+
});
|
|
111
|
+
(0, vitest_1.it)("level wins over orchestratorMode when both are set", () => {
|
|
112
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "minimal", orchestratorMode: "strict" });
|
|
113
|
+
// level takes priority per bootstrap-packet.ts implementation
|
|
114
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("minimal");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
(0, vitest_1.describe)("buildBootstrapPacket — execution_adapter carries compact_mode", () => {
|
|
118
|
+
(0, vitest_1.it)("execution_adapter.compact_bootstrap_state.compact_mode matches packet compact_mode (standard)", () => {
|
|
119
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "standard" });
|
|
120
|
+
(0, vitest_1.expect)(packet.execution_adapter?.compact_bootstrap_state.compact_mode).toBe("standard");
|
|
121
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("standard");
|
|
122
|
+
});
|
|
123
|
+
(0, vitest_1.it)("execution_adapter.compact_bootstrap_state.compact_mode matches packet compact_mode (strict)", () => {
|
|
124
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "strict" });
|
|
125
|
+
(0, vitest_1.expect)(packet.execution_adapter?.compact_bootstrap_state.compact_mode).toBe("strict");
|
|
126
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("strict");
|
|
127
|
+
});
|
|
128
|
+
(0, vitest_1.it)("execution_adapter.compact_bootstrap_state.compact_mode matches packet compact_mode (minimal)", () => {
|
|
129
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119", undefined, undefined, { level: "minimal" });
|
|
130
|
+
(0, vitest_1.expect)(packet.execution_adapter?.compact_bootstrap_state.compact_mode).toBe("minimal");
|
|
131
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("minimal");
|
|
132
|
+
});
|
|
133
|
+
(0, vitest_1.it)("compact_bootstrap_state and packet compact_mode are in sync for default case", () => {
|
|
134
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119");
|
|
135
|
+
(0, vitest_1.expect)(packet.compact_mode).toBe("standard");
|
|
136
|
+
(0, vitest_1.expect)(packet.execution_adapter?.compact_bootstrap_state.compact_mode).toBe("standard");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
(0, vitest_1.describe)("buildBootstrapPacket — packet shape", () => {
|
|
140
|
+
(0, vitest_1.it)("sets run_id, skill, and open_children from state", () => {
|
|
141
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119");
|
|
142
|
+
(0, vitest_1.expect)(packet.run_id).toBe("run-test-001");
|
|
143
|
+
(0, vitest_1.expect)(packet.skill).toBe("polaris-run");
|
|
144
|
+
(0, vitest_1.expect)(packet.open_children).toEqual(["POL-120"]);
|
|
145
|
+
(0, vitest_1.expect)(packet.last_completed_child).toBe("POL-119");
|
|
146
|
+
});
|
|
147
|
+
(0, vitest_1.it)("sets next_step to CLUSTER-COMPLETE when no open children remain", () => {
|
|
148
|
+
const state = {
|
|
149
|
+
...BASE_STATE,
|
|
150
|
+
open_children: [],
|
|
151
|
+
next_open_child: null,
|
|
152
|
+
};
|
|
153
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(state, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-120");
|
|
154
|
+
(0, vitest_1.expect)(packet.next_step).toBe("CLUSTER-COMPLETE");
|
|
155
|
+
});
|
|
156
|
+
(0, vitest_1.it)("sets next_step to '03-execute-child' when open children remain", () => {
|
|
157
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119");
|
|
158
|
+
(0, vitest_1.expect)(packet.next_step).toBe("03-execute-child");
|
|
159
|
+
});
|
|
160
|
+
(0, vitest_1.it)("includes artifact_pointers with relative paths", () => {
|
|
161
|
+
const packet = (0, bootstrap_packet_js_1.buildBootstrapPacket)(BASE_STATE, "/repo/.taskchain_artifacts/polaris-run/current-state.json", "sha256abc", "/repo", "POL-119");
|
|
162
|
+
(0, vitest_1.expect)(packet.artifact_pointers.current_state).toMatch(/\.taskchain_artifacts/);
|
|
163
|
+
(0, vitest_1.expect)(packet.artifact_pointers.telemetry).toMatch(/telemetry\.jsonl$/);
|
|
164
|
+
// Ensure paths are not absolute
|
|
165
|
+
(0, vitest_1.expect)((0, node_path_1.isAbsolute)(packet.artifact_pointers.current_state)).toBe(false);
|
|
166
|
+
(0, vitest_1.expect)((0, node_path_1.isAbsolute)(packet.artifact_pointers.telemetry)).toBe(false);
|
|
167
|
+
// Ensure paths do not start with /repo/ prefix
|
|
168
|
+
(0, vitest_1.expect)(packet.artifact_pointers.current_state).not.toMatch(/^\/repo\//);
|
|
169
|
+
(0, vitest_1.expect)(packet.artifact_pointers.telemetry).not.toMatch(/^\/repo\//);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isProtectedBranch = isProtectedBranch;
|
|
4
|
+
exports.deriveClusterBranch = deriveClusterBranch;
|
|
5
|
+
exports.getCurrentBranch = getCurrentBranch;
|
|
6
|
+
exports.ensureClusterBranch = ensureClusterBranch;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const PROTECTED_BRANCHES = new Set(["", "HEAD", "main", "master"]);
|
|
9
|
+
function isProtectedBranch(branch) {
|
|
10
|
+
return PROTECTED_BRANCHES.has((branch ?? "").trim());
|
|
11
|
+
}
|
|
12
|
+
function deriveClusterBranch(clusterId) {
|
|
13
|
+
return `polaris/${clusterId}`;
|
|
14
|
+
}
|
|
15
|
+
function getCurrentBranch(repoRoot) {
|
|
16
|
+
try {
|
|
17
|
+
return (0, node_child_process_1.execFileSync)("git", ["branch", "--show-current"], {
|
|
18
|
+
cwd: repoRoot,
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
}).trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function branchExists(repoRoot, branch) {
|
|
27
|
+
try {
|
|
28
|
+
(0, node_child_process_1.execFileSync)("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
29
|
+
cwd: repoRoot,
|
|
30
|
+
stdio: "ignore",
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function ensureClusterBranch(repoRoot, clusterId, preferredBranch) {
|
|
39
|
+
const targetBranch = !isProtectedBranch(preferredBranch)
|
|
40
|
+
? preferredBranch.trim()
|
|
41
|
+
: deriveClusterBranch(clusterId);
|
|
42
|
+
const currentBranch = getCurrentBranch(repoRoot);
|
|
43
|
+
if (currentBranch === targetBranch) {
|
|
44
|
+
return targetBranch;
|
|
45
|
+
}
|
|
46
|
+
const args = branchExists(repoRoot, targetBranch)
|
|
47
|
+
? ["switch", targetBranch]
|
|
48
|
+
: ["switch", "-c", targetBranch];
|
|
49
|
+
(0, node_child_process_1.execFileSync)("git", args, {
|
|
50
|
+
cwd: repoRoot,
|
|
51
|
+
stdio: "ignore",
|
|
52
|
+
});
|
|
53
|
+
return targetBranch;
|
|
54
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Budget policy for the parent scheduler loop.
|
|
4
|
+
*
|
|
5
|
+
* The parent checks budget after each worker dispatch. If the budget is
|
|
6
|
+
* exhausted, the parent halts and writes a checkpoint to current-state.json.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.checkBudget = checkBudget;
|
|
10
|
+
exports.policyFromConfig = policyFromConfig;
|
|
11
|
+
exports.policyFromState = policyFromState;
|
|
12
|
+
/**
|
|
13
|
+
* Check whether the context budget allows continuing to the next child.
|
|
14
|
+
*
|
|
15
|
+
* Returns `{ status: 'ok' }` if the loop should continue, or
|
|
16
|
+
* `{ status: 'exhausted', reason }` if the loop must halt.
|
|
17
|
+
*/
|
|
18
|
+
function checkBudget(input) {
|
|
19
|
+
const { childrenCompleted, lastChildStatus, policy } = input;
|
|
20
|
+
// stop-on-fail: halt immediately when any child fails
|
|
21
|
+
if (policy.stopOnFail && lastChildStatus === 'failed') {
|
|
22
|
+
return {
|
|
23
|
+
status: 'exhausted',
|
|
24
|
+
reason: `Budget halted: stop_on_fail is enabled and a child returned status "failed".`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
switch (policy.mode) {
|
|
28
|
+
case 'run-until-done':
|
|
29
|
+
// No cap — always continue until open_children is empty
|
|
30
|
+
return { status: 'ok' };
|
|
31
|
+
case 'stop-on-fail':
|
|
32
|
+
// The stop-on-fail mode implies run-until-done cap-wise;
|
|
33
|
+
// actual fail detection is handled above via stopOnFail flag.
|
|
34
|
+
return { status: 'ok' };
|
|
35
|
+
case 'fixed-cap':
|
|
36
|
+
default:
|
|
37
|
+
if (childrenCompleted >= policy.maxChildrenPerSession) {
|
|
38
|
+
return {
|
|
39
|
+
status: 'exhausted',
|
|
40
|
+
reason: `Budget exhausted: ${childrenCompleted} of ${policy.maxChildrenPerSession} ` +
|
|
41
|
+
`children completed this session.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { status: 'ok' };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Derive a BudgetPolicy from the state's context_budget field and the loaded config.
|
|
49
|
+
* Config takes precedence over state fields; falls back to a default of 3-child fixed-cap.
|
|
50
|
+
*/
|
|
51
|
+
function policyFromConfig(contextBudget, budgetConfig) {
|
|
52
|
+
const mode = budgetConfig?.mode ?? 'fixed-cap';
|
|
53
|
+
const maxChildrenPerSession = budgetConfig?.max_children ?? contextBudget.max_children_per_session ?? 3;
|
|
54
|
+
const stopOnFail = budgetConfig?.stop_on_fail ?? (mode === 'stop-on-fail');
|
|
55
|
+
return { mode, maxChildrenPerSession, stopOnFail };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @deprecated Use policyFromConfig instead.
|
|
59
|
+
* Kept for backwards compatibility with callers that only have state context_budget.
|
|
60
|
+
*/
|
|
61
|
+
function policyFromState(contextBudget) {
|
|
62
|
+
return policyFromConfig(contextBudget, undefined);
|
|
63
|
+
}
|