@blxzer/cursor-trellis 0.4.1 → 0.4.3
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/CHANGELOG.md +49 -0
- package/README.md +25 -14
- package/README.zh-CN.md +14 -10
- package/bin/smart-search.js +10 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +36 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +42 -153
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +10 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +38 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +144 -41
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/cursor.d.ts +2 -1
- package/dist/configurators/cursor.d.ts.map +1 -1
- package/dist/configurators/cursor.js +4 -2
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/index.js +4 -4
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/shared.d.ts +8 -0
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +27 -1
- package/dist/configurators/shared.js.map +1 -1
- package/dist/configurators/workflow.d.ts +0 -9
- package/dist/configurators/workflow.d.ts.map +1 -1
- package/dist/configurators/workflow.js +12 -45
- package/dist/configurators/workflow.js.map +1 -1
- package/dist/constants/paths.d.ts +13 -0
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +17 -0
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.3.0.json +1 -1
- package/dist/templates/common/bundled-skills/cstl-check/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/hooks-and-settings.md +2 -2
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/overview.md +1 -1
- package/dist/templates/common/bundled-skills/cstl-meta/references/platform-files/skills-and-commands.md +2 -4
- package/dist/templates/common/bundled-skills/cstl-skill-creator/SKILL.md +10 -7
- package/dist/templates/common/bundled-skills/cstl-skill-creator/references/authoring-rules.md +93 -19
- package/dist/templates/common/bundled-skills/cstl-skill-creator/references/general-authoring.md +189 -0
- package/dist/templates/common/bundled-skills/cstl-skill-creator/references/review-checklist.md +10 -5
- package/dist/templates/common/commands/continue.md +6 -5
- package/dist/templates/common/index.d.ts +20 -0
- package/dist/templates/common/index.d.ts.map +1 -1
- package/dist/templates/common/index.js +21 -2
- package/dist/templates/common/index.js.map +1 -1
- package/dist/templates/common/optional-skills/chrome-cdp/SKILL.md +179 -0
- package/dist/templates/common/optional-skills/chrome-cdp/examples/fetch-hook-api-capture.md +149 -0
- package/dist/templates/common/optional-skills/chrome-cdp/scripts/cdp.mjs +903 -0
- package/dist/templates/common/skills/brainstorm.md +2 -2
- package/dist/templates/cursor/agents/cstl-check.md +2 -2
- package/dist/templates/cursor/agents/cstl-implement.md +1 -1
- package/dist/templates/cursor/agents/cstl-research.md +2 -2
- package/dist/templates/cursor/fixtures/expected-rules.d.ts +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.d.ts.map +1 -1
- package/dist/templates/cursor/fixtures/expected-rules.js +7 -24
- package/dist/templates/cursor/fixtures/expected-rules.js.map +1 -1
- package/dist/templates/cursor/hooks.json +4 -0
- package/dist/templates/cursor/index.d.ts +1 -1
- package/dist/templates/cursor/index.js +1 -1
- package/dist/templates/cursor/rules/cstl-bootstrap.mdc +17 -0
- package/dist/templates/markdown/agents.md +36 -3
- package/dist/templates/markdown/{spec/guides → framework}/cursor-native-modes-guide.md.txt +13 -10
- package/dist/templates/markdown/{spec/guides → framework}/cursor-subagent-policy.md.txt +63 -127
- package/dist/templates/markdown/framework/dogfood-only-surfaces.md.txt +29 -0
- package/dist/templates/markdown/{spec/guides → framework}/execution-strategy.md.txt +2 -0
- package/dist/templates/markdown/framework/index.md.txt +31 -0
- package/dist/templates/markdown/framework/internal-skills-cursor-reachability.md.txt +45 -0
- package/dist/templates/markdown/framework/middleware-protocol.md.txt +83 -0
- package/dist/templates/markdown/framework/parallel-first-execution.md.txt +78 -0
- package/dist/templates/markdown/framework/prd-grill-frontier.md.txt +83 -0
- package/dist/templates/markdown/framework/release-boundary.md.txt +30 -0
- package/dist/templates/markdown/{spec/guides → framework}/retrieval-daily-guide.md.txt +1 -1
- package/dist/templates/markdown/framework/upgrade.md.txt +15 -0
- package/dist/templates/markdown/{spec/guides → framework}/verification-strength-guide.md.txt +2 -2
- package/dist/templates/markdown/index.d.ts +26 -10
- package/dist/templates/markdown/index.d.ts.map +1 -1
- package/dist/templates/markdown/index.js +87 -11
- package/dist/templates/markdown/index.js.map +1 -1
- package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +1 -1
- package/dist/templates/markdown/spec/guides/index.md.txt +5 -19
- package/dist/templates/markdown/spec/guides/prototype-guide.md.txt +10 -10
- package/dist/templates/markdown/spec/guides/test-discipline-guide.md.txt +1 -1
- package/dist/templates/shared-hooks/event-bridge.py +60 -0
- package/dist/templates/shared-hooks/index.d.ts +1 -1
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +1 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/session-start.py +19 -2
- package/dist/templates/trellis/CONTEXT.md +63 -3
- package/dist/templates/trellis/config.yaml +1 -1
- package/dist/templates/trellis/gitignore.txt +39 -39
- package/dist/templates/trellis/index.d.ts +12 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +73 -15
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/pool/README.md +12 -3
- package/dist/templates/trellis/pool/plan.md +24 -5
- package/dist/templates/trellis/scripts/common/adapter_middleware.py +158 -0
- package/dist/templates/trellis/scripts/common/artifact_locale.py +12 -3
- package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +5 -2
- package/dist/templates/trellis/scripts/common/full_quality.py +237 -0
- package/dist/templates/trellis/scripts/common/git.py +6 -0
- package/dist/templates/trellis/scripts/common/git_context.py +17 -5
- package/dist/templates/trellis/scripts/common/kernel_command.py +367 -0
- package/dist/templates/trellis/scripts/common/lite_context.py +280 -0
- package/dist/templates/trellis/scripts/common/ondemand_topology.py +225 -0
- package/dist/templates/trellis/scripts/common/parent_orchestration.py +1 -1
- package/dist/templates/trellis/scripts/common/pool_store.py +16 -4
- package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +2 -2
- package/dist/templates/trellis/scripts/common/session_context.py +28 -2
- package/dist/templates/trellis/scripts/common/task_gates.py +73 -12
- package/dist/templates/trellis/scripts/common/task_map.py +42 -9
- package/dist/templates/trellis/scripts/common/task_store.py +293 -61
- package/dist/templates/trellis/scripts/common/test_adapter_middleware.py +56 -0
- package/dist/templates/trellis/scripts/common/test_depends_mode_block.py +9 -0
- package/dist/templates/trellis/scripts/common/test_full_quality.py +197 -0
- package/dist/templates/trellis/scripts/common/test_kernel_command.py +326 -0
- package/dist/templates/trellis/scripts/common/test_lite_path.py +190 -0
- package/dist/templates/trellis/scripts/common/test_observable_defaults.py +10 -4
- package/dist/templates/trellis/scripts/common/test_ondemand_topology.py +122 -0
- package/dist/templates/trellis/scripts/common/test_pool_store.py +17 -0
- package/dist/templates/trellis/scripts/common/test_task_store_kernel_patch.py +163 -0
- package/dist/templates/trellis/scripts/common/workflow_phase.py +3 -0
- package/dist/templates/trellis/scripts/cursor_retrieval_probe_prompt.md +4 -5
- package/dist/templates/trellis/scripts/get_context.py +1 -0
- package/dist/templates/trellis/scripts/pool.py +5 -0
- package/dist/templates/trellis/scripts/task.py +83 -7
- package/dist/templates/trellis/workflow.md +109 -23
- package/dist/utils/cursor-retrieval-env.d.ts +2 -1
- package/dist/utils/cursor-retrieval-env.d.ts.map +1 -1
- package/dist/utils/cursor-retrieval-env.js +3 -2
- package/dist/utils/cursor-retrieval-env.js.map +1 -1
- package/dist/utils/cursor2plus-residue-cleanup.d.ts +38 -0
- package/dist/utils/cursor2plus-residue-cleanup.d.ts.map +1 -0
- package/dist/utils/cursor2plus-residue-cleanup.js +145 -0
- package/dist/utils/cursor2plus-residue-cleanup.js.map +1 -0
- package/dist/utils/manifest-prune.d.ts.map +1 -1
- package/dist/utils/manifest-prune.js +7 -2
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/p36-upgrade.d.ts +62 -0
- package/dist/utils/p36-upgrade.d.ts.map +1 -0
- package/dist/utils/p36-upgrade.js +160 -0
- package/dist/utils/p36-upgrade.js.map +1 -0
- package/dist/utils/project-capabilities.d.ts +1 -12
- package/dist/utils/project-capabilities.d.ts.map +1 -1
- package/dist/utils/project-capabilities.js +3 -100
- package/dist/utils/project-capabilities.js.map +1 -1
- package/dist/utils/readiness.d.ts +10 -0
- package/dist/utils/readiness.d.ts.map +1 -1
- package/dist/utils/readiness.js +58 -26
- package/dist/utils/readiness.js.map +1 -1
- package/dist/utils/retrieval-agent-instructions.js +1 -1
- package/dist/utils/retrieval-agent-instructions.js.map +1 -1
- package/dist/utils/retrieval-execution-telemetry.js +1 -1
- package/dist/utils/retrieval-execution-telemetry.js.map +1 -1
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +11 -1
- package/dist/utils/template-hash.js.map +1 -1
- package/dist/utils/uninstall-scrubbers.d.ts.map +1 -1
- package/dist/utils/uninstall-scrubbers.js +52 -14
- package/dist/utils/uninstall-scrubbers.js.map +1 -1
- package/dist/utils/update-rollout-report.d.ts +14 -0
- package/dist/utils/update-rollout-report.d.ts.map +1 -1
- package/dist/utils/update-rollout-report.js +1 -0
- package/dist/utils/update-rollout-report.js.map +1 -1
- package/dist/utils/workflow-ownership.d.ts +1 -1
- package/dist/utils/workflow-ownership.js +4 -4
- package/dist/utils/workflow-ownership.js.map +1 -1
- package/package.json +5 -4
- package/scripts/postinstall.js +6 -7
- package/dist/commands/campaign/canvas-render.d.ts +0 -68
- package/dist/commands/campaign/canvas-render.d.ts.map +0 -1
- package/dist/commands/campaign/canvas-render.js +0 -414
- package/dist/commands/campaign/canvas-render.js.map +0 -1
- package/dist/commands/campaign/compose.d.ts +0 -15
- package/dist/commands/campaign/compose.d.ts.map +0 -1
- package/dist/commands/campaign/compose.js +0 -110
- package/dist/commands/campaign/compose.js.map +0 -1
- package/dist/commands/campaign/index.d.ts +0 -13
- package/dist/commands/campaign/index.d.ts.map +0 -1
- package/dist/commands/campaign/index.js +0 -134
- package/dist/commands/campaign/index.js.map +0 -1
- package/dist/commands/campaign/kind-map.d.ts +0 -6
- package/dist/commands/campaign/kind-map.d.ts.map +0 -1
- package/dist/commands/campaign/kind-map.js +0 -21
- package/dist/commands/campaign/kind-map.js.map +0 -1
- package/dist/commands/campaign/mcp-server.d.ts +0 -6
- package/dist/commands/campaign/mcp-server.d.ts.map +0 -1
- package/dist/commands/campaign/mcp-server.js +0 -167
- package/dist/commands/campaign/mcp-server.js.map +0 -1
- package/dist/commands/campaign/render.d.ts +0 -5
- package/dist/commands/campaign/render.d.ts.map +0 -1
- package/dist/commands/campaign/render.js +0 -103
- package/dist/commands/campaign/render.js.map +0 -1
- package/dist/commands/campaign/trellis-load.d.ts +0 -13
- package/dist/commands/campaign/trellis-load.d.ts.map +0 -1
- package/dist/commands/campaign/trellis-load.js +0 -122
- package/dist/commands/campaign/trellis-load.js.map +0 -1
- package/dist/commands/campaign/types.d.ts +0 -61
- package/dist/commands/campaign/types.d.ts.map +0 -1
- package/dist/commands/campaign/types.js +0 -3
- package/dist/commands/campaign/types.js.map +0 -1
- package/dist/commands/goal/index.d.ts +0 -3
- package/dist/commands/goal/index.d.ts.map +0 -1
- package/dist/commands/goal/index.js +0 -159
- package/dist/commands/goal/index.js.map +0 -1
- package/dist/commands/goal/review.d.ts +0 -6
- package/dist/commands/goal/review.d.ts.map +0 -1
- package/dist/commands/goal/review.js +0 -28
- package/dist/commands/goal/review.js.map +0 -1
- package/dist/commands/rpc/client.d.ts +0 -51
- package/dist/commands/rpc/client.d.ts.map +0 -1
- package/dist/commands/rpc/client.js +0 -147
- package/dist/commands/rpc/client.js.map +0 -1
- package/dist/commands/rpc/index.d.ts +0 -10
- package/dist/commands/rpc/index.d.ts.map +0 -1
- package/dist/commands/rpc/index.js +0 -157
- package/dist/commands/rpc/index.js.map +0 -1
- package/dist/commands/rpc/serve.d.ts +0 -23
- package/dist/commands/rpc/serve.d.ts.map +0 -1
- package/dist/commands/rpc/serve.js +0 -60
- package/dist/commands/rpc/serve.js.map +0 -1
- package/dist/commands/rpc/smoke.d.ts +0 -16
- package/dist/commands/rpc/smoke.d.ts.map +0 -1
- package/dist/commands/rpc/smoke.js +0 -92
- package/dist/commands/rpc/smoke.js.map +0 -1
- package/dist/commands/sdk/index.d.ts +0 -9
- package/dist/commands/sdk/index.d.ts.map +0 -1
- package/dist/commands/sdk/index.js +0 -58
- package/dist/commands/sdk/index.js.map +0 -1
- package/dist/commands/sdk/run.d.ts +0 -42
- package/dist/commands/sdk/run.d.ts.map +0 -1
- package/dist/commands/sdk/run.js +0 -220
- package/dist/commands/sdk/run.js.map +0 -1
- package/dist/commands/sdk/status.d.ts +0 -14
- package/dist/commands/sdk/status.d.ts.map +0 -1
- package/dist/commands/sdk/status.js +0 -69
- package/dist/commands/sdk/status.js.map +0 -1
- package/dist/configurators/cursor2plus-local.d.ts +0 -6
- package/dist/configurators/cursor2plus-local.d.ts.map +0 -1
- package/dist/configurators/cursor2plus-local.js +0 -21
- package/dist/configurators/cursor2plus-local.js.map +0 -1
- package/dist/goal/action-packet.d.ts +0 -89
- package/dist/goal/action-packet.d.ts.map +0 -1
- package/dist/goal/action-packet.js +0 -113
- package/dist/goal/action-packet.js.map +0 -1
- package/dist/goal/audit.d.ts +0 -14
- package/dist/goal/audit.d.ts.map +0 -1
- package/dist/goal/audit.js +0 -12
- package/dist/goal/audit.js.map +0 -1
- package/dist/goal/constants.d.ts +0 -26
- package/dist/goal/constants.d.ts.map +0 -1
- package/dist/goal/constants.js +0 -24
- package/dist/goal/constants.js.map +0 -1
- package/dist/goal/paths.d.ts +0 -8
- package/dist/goal/paths.d.ts.map +0 -1
- package/dist/goal/paths.js +0 -36
- package/dist/goal/paths.js.map +0 -1
- package/dist/goal/preflight.d.ts +0 -21
- package/dist/goal/preflight.d.ts.map +0 -1
- package/dist/goal/preflight.js +0 -97
- package/dist/goal/preflight.js.map +0 -1
- package/dist/goal/review-seam.d.ts +0 -44
- package/dist/goal/review-seam.d.ts.map +0 -1
- package/dist/goal/review-seam.js +0 -153
- package/dist/goal/review-seam.js.map +0 -1
- package/dist/goal/reviewer-rules.d.ts +0 -13
- package/dist/goal/reviewer-rules.d.ts.map +0 -1
- package/dist/goal/reviewer-rules.js +0 -153
- package/dist/goal/reviewer-rules.js.map +0 -1
- package/dist/goal/reviewer.d.ts +0 -9
- package/dist/goal/reviewer.d.ts.map +0 -1
- package/dist/goal/reviewer.js +0 -54
- package/dist/goal/reviewer.js.map +0 -1
- package/dist/goal/runtime.d.ts +0 -32
- package/dist/goal/runtime.d.ts.map +0 -1
- package/dist/goal/runtime.js +0 -242
- package/dist/goal/runtime.js.map +0 -1
- package/dist/goal/sdk-client.d.ts +0 -10
- package/dist/goal/sdk-client.d.ts.map +0 -1
- package/dist/goal/sdk-client.js +0 -34
- package/dist/goal/sdk-client.js.map +0 -1
- package/dist/goal/state.d.ts +0 -40
- package/dist/goal/state.d.ts.map +0 -1
- package/dist/goal/state.js +0 -37
- package/dist/goal/state.js.map +0 -1
- package/dist/goal/task-triggers.d.ts +0 -15
- package/dist/goal/task-triggers.d.ts.map +0 -1
- package/dist/goal/task-triggers.js +0 -23
- package/dist/goal/task-triggers.js.map +0 -1
- package/dist/goal/walls.d.ts +0 -12
- package/dist/goal/walls.d.ts.map +0 -1
- package/dist/goal/walls.js +0 -65
- package/dist/goal/walls.js.map +0 -1
- package/dist/goal/worker-prompt.d.ts +0 -13
- package/dist/goal/worker-prompt.d.ts.map +0 -1
- package/dist/goal/worker-prompt.js +0 -41
- package/dist/goal/worker-prompt.js.map +0 -1
- package/dist/goal/worker.d.ts +0 -42
- package/dist/goal/worker.d.ts.map +0 -1
- package/dist/goal/worker.js +0 -180
- package/dist/goal/worker.js.map +0 -1
- package/dist/templates/common/bundled-skills/cstl-cursor2plus-setup/SKILL.md +0 -63
- package/dist/templates/cursor/commands/cursor2plus-setup.md +0 -82
- package/dist/templates/cursor/rules/cstl-cursor-modes.mdc +0 -32
- package/dist/templates/cursor/rules/cstl-session-rename.mdc +0 -31
- package/dist/templates/cursor/rules/cstl-subagent-dispatch.mdc +0 -34
- package/dist/templates/cursor/rules/cstl-triage.mdc +0 -36
- package/dist/templates/cursor/rules/retrieval-routing.mdc +0 -78
- package/dist/templates/markdown/spec/guides/cursor-trellis-release-coexistence-guide.md.txt +0 -166
- package/dist/templates/markdown/spec/guides/goal-release-regression-runbook.md.txt +0 -132
- package/dist/templates/trellis/local/README.md +0 -85
- package/dist/templates/trellis/local/config.local.json.example +0 -6
- package/dist/templates/trellis/local/index.d.ts +0 -8
- package/dist/templates/trellis/local/index.d.ts.map +0 -1
- package/dist/templates/trellis/local/index.js +0 -15
- package/dist/templates/trellis/local/index.js.map +0 -1
- package/dist/templates/trellis/local/patch_wpelc8.py +0 -421
- package/dist/templates/trellis/local/smoke.py +0 -94
- package/dist/templates/trellis/local/subagent-models.json.example +0 -4
- package/dist/templates/trellis/local/trellis-task-models.json.example +0 -10
- package/dist/templates/trellis/local/trellis-task-models.json5.example +0 -45
- package/dist/templates/trellis/local/trellis_task_models_config.py +0 -200
- package/dist/utils/cursor-sdk-gate.d.ts +0 -10
- package/dist/utils/cursor-sdk-gate.d.ts.map +0 -1
- package/dist/utils/cursor-sdk-gate.js +0 -32
- package/dist/utils/cursor-sdk-gate.js.map +0 -1
- /package/dist/templates/markdown/{spec/guides → framework}/artifact-locale-guide.md.txt +0 -0
- /package/dist/templates/markdown/{spec/guides → framework}/cursor-context-injection-guide.md.txt +0 -0
- /package/dist/templates/markdown/{spec/guides → framework}/cursor-semantic-compliance.md.txt +0 -0
- /package/dist/templates/markdown/{spec/guides → framework}/injection-budget-guide.md.txt +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Stage 5 Topology + typed graph + On-demand (Python projection).
|
|
2
|
+
|
|
3
|
+
Kernel extras remain authoritative. This module renders and validates the
|
|
4
|
+
same contract; it does not schedule workers or write a second graph.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
STAGE5_SOURCE = "stage5-ondemand-topology"
|
|
12
|
+
STAGE5_SCHEMA_VERSION = 1
|
|
13
|
+
|
|
14
|
+
STAGE5_ONDEMAND_MODULES = (
|
|
15
|
+
"candidate-pool",
|
|
16
|
+
"define-extended",
|
|
17
|
+
"worker-orchestration",
|
|
18
|
+
"parent-child",
|
|
19
|
+
"debug-recovery",
|
|
20
|
+
"integration-handoff",
|
|
21
|
+
"session-transfer",
|
|
22
|
+
"spec-learning",
|
|
23
|
+
"vcs-integration",
|
|
24
|
+
"personal-memory",
|
|
25
|
+
"retention-storage",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
ONDEMAND_OWNERS = {
|
|
29
|
+
"integration-handoff": "parent-child",
|
|
30
|
+
"session-transfer": "session-transfer",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
LITE_DORMANT_ONDEMAND = (
|
|
34
|
+
"parent-child",
|
|
35
|
+
"vcs-integration",
|
|
36
|
+
"personal-memory",
|
|
37
|
+
"retention-storage",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TopologyError(RuntimeError):
|
|
42
|
+
"""Topology / On-demand contract rejected a request."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def capture_candidate(summary: str) -> dict[str, Any]:
|
|
46
|
+
return {
|
|
47
|
+
"kind": "candidate",
|
|
48
|
+
"summary": (summary or "").strip(),
|
|
49
|
+
"task_created": False,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def propose_decompose(parent_id: str, slices: list[str]) -> dict[str, Any]:
|
|
54
|
+
return {
|
|
55
|
+
"parent_id": parent_id,
|
|
56
|
+
"slices": _unique(slices),
|
|
57
|
+
"confirmed": False,
|
|
58
|
+
"children_created": [],
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def confirm_decompose(proposal: dict[str, Any]) -> dict[str, Any]:
|
|
63
|
+
slices = _unique(proposal.get("slices") or [])
|
|
64
|
+
return {
|
|
65
|
+
**proposal,
|
|
66
|
+
"confirmed": True,
|
|
67
|
+
"children_created": slices,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def apply_retention(outcome: str) -> str:
|
|
72
|
+
return outcome
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def default_topology(
|
|
76
|
+
parent: str | None = None,
|
|
77
|
+
children: list[str] | None = None,
|
|
78
|
+
) -> dict[str, Any]:
|
|
79
|
+
"""Seed Topology. ``parent_id`` + empty children stays ``single``."""
|
|
80
|
+
kids = _unique(children or [])
|
|
81
|
+
parent_id = (parent or "").strip() or None
|
|
82
|
+
return {
|
|
83
|
+
"schema_version": STAGE5_SCHEMA_VERSION,
|
|
84
|
+
"kind": "parent-child" if kids else "single",
|
|
85
|
+
"parent_id": parent_id,
|
|
86
|
+
"children": kids,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def assign_parent(topology: dict[str, Any], parent_id: str) -> dict[str, Any]:
|
|
91
|
+
current = topology.get("parent_id")
|
|
92
|
+
next_parent = (parent_id or "").strip()
|
|
93
|
+
if not next_parent:
|
|
94
|
+
raise TopologyError("parent_id must be a non-empty string")
|
|
95
|
+
if current and current != next_parent:
|
|
96
|
+
raise TopologyError(
|
|
97
|
+
f"single-parent tree: second parent rejected ({current} vs {next_parent})"
|
|
98
|
+
)
|
|
99
|
+
children = list(topology.get("children") or [])
|
|
100
|
+
return {
|
|
101
|
+
"schema_version": STAGE5_SCHEMA_VERSION,
|
|
102
|
+
"kind": "parent-child" if children else "single",
|
|
103
|
+
"parent_id": next_parent,
|
|
104
|
+
"children": children,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def assert_integration_authority(actor_role: str) -> None:
|
|
109
|
+
if actor_role == "parent":
|
|
110
|
+
return
|
|
111
|
+
raise TopologyError(
|
|
112
|
+
"Integration authority stays with Parent; Child cannot integrate-child"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def expand_depends_on_to_requires(
|
|
117
|
+
graph: dict[str, Any],
|
|
118
|
+
from_id: str,
|
|
119
|
+
depends_on: list[str],
|
|
120
|
+
) -> dict[str, Any]:
|
|
121
|
+
edges = list(graph.get("edges") or [])
|
|
122
|
+
for dep in _unique(depends_on):
|
|
123
|
+
if any(
|
|
124
|
+
edge.get("from") == from_id
|
|
125
|
+
and edge.get("to") == dep
|
|
126
|
+
and edge.get("type") == "requires"
|
|
127
|
+
for edge in edges
|
|
128
|
+
if isinstance(edge, dict)
|
|
129
|
+
):
|
|
130
|
+
continue
|
|
131
|
+
edges.append({"from": from_id, "to": dep, "type": "requires"})
|
|
132
|
+
return {"schema_version": STAGE5_SCHEMA_VERSION, "edges": edges}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def unmet_requires(
|
|
136
|
+
graph: dict[str, Any],
|
|
137
|
+
satisfied: list[str],
|
|
138
|
+
from_id: str | None = None,
|
|
139
|
+
) -> list[str]:
|
|
140
|
+
done = set(satisfied)
|
|
141
|
+
missing: list[str] = []
|
|
142
|
+
for edge in graph.get("edges") or []:
|
|
143
|
+
if not isinstance(edge, dict) or edge.get("type") != "requires":
|
|
144
|
+
continue
|
|
145
|
+
if from_id and edge.get("from") != from_id:
|
|
146
|
+
continue
|
|
147
|
+
target = edge.get("to")
|
|
148
|
+
if isinstance(target, str) and target not in done and target not in missing:
|
|
149
|
+
missing.append(target)
|
|
150
|
+
return missing
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def project_task_map_graph(
|
|
154
|
+
topology: dict[str, Any],
|
|
155
|
+
graph: dict[str, Any],
|
|
156
|
+
) -> dict[str, Any]:
|
|
157
|
+
children = []
|
|
158
|
+
for child_id in topology.get("children") or []:
|
|
159
|
+
if not isinstance(child_id, str):
|
|
160
|
+
continue
|
|
161
|
+
depends = [
|
|
162
|
+
edge.get("to")
|
|
163
|
+
for edge in graph.get("edges") or []
|
|
164
|
+
if isinstance(edge, dict)
|
|
165
|
+
and edge.get("type") == "requires"
|
|
166
|
+
and edge.get("from") == child_id
|
|
167
|
+
]
|
|
168
|
+
children.append({"id": child_id, "depends_on": depends})
|
|
169
|
+
return {
|
|
170
|
+
"graph_authority": "kernel-extras",
|
|
171
|
+
"topology_kind": topology.get("kind") or "single",
|
|
172
|
+
"parent_id": topology.get("parent_id"),
|
|
173
|
+
"children": children,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def apply_kernel_graph_projection(data: dict[str, Any], extras: dict[str, Any]) -> None:
|
|
178
|
+
"""Stamp Parent task-map frontmatter from Kernel extras. Projection only."""
|
|
179
|
+
topology = extras.get("topology")
|
|
180
|
+
graph = extras.get("dependency_graph")
|
|
181
|
+
if not isinstance(topology, dict) and not isinstance(graph, dict):
|
|
182
|
+
return
|
|
183
|
+
projection = project_task_map_graph(
|
|
184
|
+
topology if isinstance(topology, dict) else {},
|
|
185
|
+
graph if isinstance(graph, dict) else {"edges": []},
|
|
186
|
+
)
|
|
187
|
+
data["graph_authority"] = "kernel-extras"
|
|
188
|
+
data["topology_kind"] = projection["topology_kind"]
|
|
189
|
+
if projection.get("parent_id"):
|
|
190
|
+
data["parent_id"] = projection["parent_id"]
|
|
191
|
+
existing = {
|
|
192
|
+
child.get("id"): child
|
|
193
|
+
for child in data.get("children") or []
|
|
194
|
+
if isinstance(child, dict)
|
|
195
|
+
}
|
|
196
|
+
children = data.setdefault("children", [])
|
|
197
|
+
if not isinstance(children, list):
|
|
198
|
+
data["children"] = []
|
|
199
|
+
children = data["children"]
|
|
200
|
+
for child in projection["children"]:
|
|
201
|
+
current = existing.get(child["id"])
|
|
202
|
+
if current is None:
|
|
203
|
+
current = {"id": child["id"], "state": "open", "depends_on": []}
|
|
204
|
+
children.append(current)
|
|
205
|
+
existing[child["id"]] = current
|
|
206
|
+
current["depends_on"] = child["depends_on"]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def resident_on_demand_modules(extras: dict[str, Any]) -> list[str]:
|
|
210
|
+
modules = extras.get("ondemand_modules")
|
|
211
|
+
if not isinstance(modules, dict):
|
|
212
|
+
return []
|
|
213
|
+
active = modules.get("active") or []
|
|
214
|
+
return [name for name in active if isinstance(name, str)]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _unique(values: list[Any]) -> list[str]:
|
|
218
|
+
out: list[str] = []
|
|
219
|
+
for value in values:
|
|
220
|
+
if not isinstance(value, str):
|
|
221
|
+
continue
|
|
222
|
+
item = value.strip()
|
|
223
|
+
if item and item not in out:
|
|
224
|
+
out.append(item)
|
|
225
|
+
return out
|
|
@@ -271,7 +271,7 @@ def build_child_prompt(
|
|
|
271
271
|
f"Selected task: {child_rel}",
|
|
272
272
|
"",
|
|
273
273
|
"Delivery mode: subagent (Cursor)",
|
|
274
|
-
"- **Default:** Parent session dispatches **Task** with `subagent_type=cstl-implement` and this prompt as the task description (writable sub-agent).
|
|
274
|
+
"- **Default:** Parent session dispatches **Task** with `subagent_type=cstl-implement` and this prompt as the task description (writable sub-agent). Native Cursor `model:` frontmatter still applies. Cursor++ / cstl-byok are not Native install surfaces.",
|
|
275
275
|
"- **Exception:** If parent `child-prompts.md` or the user names this child for a **separate writable Agent chat**, open a new Agent session, pick the model manually, paste this prompt — do not use Task from Parent.",
|
|
276
276
|
"- Parent retains `review-child` / `integrate-child`. Child must not nest further `cstl-research` / `cstl-implement` / `cstl-check` Task dispatches.",
|
|
277
277
|
f"- Parent orchestration: `execution_topology={topo}`, `merge_limit={merge_lim}`.",
|
|
@@ -12,7 +12,7 @@ Frontmatter writes splice a re-serialized header between the raw `---`
|
|
|
12
12
|
fences so the body is preserved byte-for-byte (including the blank line
|
|
13
13
|
after the closing fence and the final newline), and key order stays stable:
|
|
14
14
|
known keys first (id, title, status, type, locale, created, approved,
|
|
15
|
-
linked_tasks), unknown keys keep their original relative order.
|
|
15
|
+
priority, linked_tasks), unknown keys keep their original relative order.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
@@ -22,7 +22,8 @@ from dataclasses import dataclass, field
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
24
|
from .artifact_search import parse_frontmatter, split_frontmatter
|
|
25
|
-
from .io import read_json
|
|
25
|
+
from .io import read_json
|
|
26
|
+
from .kernel_command import kernel_expected_revision, kernel_patch, kernel_projection_extras
|
|
26
27
|
from .paths import get_repo_root, get_tasks_dir
|
|
27
28
|
|
|
28
29
|
POOL_STATUSES = frozenset({"inbox", "review", "accepted", "rejected", "rework"})
|
|
@@ -39,6 +40,7 @@ KNOWN_FRONTMATTER_KEY_ORDER = (
|
|
|
39
40
|
"locale",
|
|
40
41
|
"created",
|
|
41
42
|
"approved",
|
|
43
|
+
"priority",
|
|
42
44
|
LINKED_TASKS_KEY,
|
|
43
45
|
)
|
|
44
46
|
|
|
@@ -328,7 +330,7 @@ def _iter_task_dirs(tasks_dir: Path):
|
|
|
328
330
|
|
|
329
331
|
|
|
330
332
|
def _task_meta_with_pool_items(task_dir: Path, item_id: str, *, remove: bool) -> bool:
|
|
331
|
-
"""Add/remove item_id in task.json meta.pool_items
|
|
333
|
+
"""Add/remove item_id in task.json meta.pool_items via Kernel patch."""
|
|
332
334
|
data = read_json(task_dir / "task.json")
|
|
333
335
|
if not isinstance(data, dict):
|
|
334
336
|
return False
|
|
@@ -346,7 +348,17 @@ def _task_meta_with_pool_items(task_dir: Path, item_id: str, *, remove: bool) ->
|
|
|
346
348
|
pool_items.append(item_id)
|
|
347
349
|
meta[POOL_ITEMS_KEY] = pool_items
|
|
348
350
|
data[META_KEY] = meta
|
|
349
|
-
|
|
351
|
+
action = "unlink" if remove else "link"
|
|
352
|
+
kernel_patch(
|
|
353
|
+
task_dir,
|
|
354
|
+
data,
|
|
355
|
+
kernel_projection_extras(data),
|
|
356
|
+
expected_revision=kernel_expected_revision(task_dir),
|
|
357
|
+
actor="pool.py link" if not remove else "pool.py unlink",
|
|
358
|
+
idempotency_key=f"patch:pool-{action}:{task_dir.name}:{item_id}",
|
|
359
|
+
evidence="pool_store._task_meta_with_pool_items",
|
|
360
|
+
)
|
|
361
|
+
return True
|
|
350
362
|
|
|
351
363
|
|
|
352
364
|
def link_item_task(
|
|
@@ -312,11 +312,11 @@ def render_agent_instructions(
|
|
|
312
312
|
label = (
|
|
313
313
|
"**语义合规(cursorEnv 未知,保守 BYOK):**"
|
|
314
314
|
if cursor_env == ENV_UNKNOWN
|
|
315
|
-
else "**语义合规(
|
|
315
|
+
else "**语义合规(BYOK env):**"
|
|
316
316
|
)
|
|
317
317
|
if locale != "zh":
|
|
318
318
|
lines.append(
|
|
319
|
-
"**Semantic compliance (
|
|
319
|
+
"**Semantic compliance (BYOK env):**"
|
|
320
320
|
if cursor_env != ENV_UNKNOWN
|
|
321
321
|
else "**Semantic compliance (cursorEnv unknown, conservative BYOK):**"
|
|
322
322
|
)
|
|
@@ -22,7 +22,6 @@ from pathlib import Path
|
|
|
22
22
|
from .active_task import resolve_context_key
|
|
23
23
|
from .artifact_locale import artifact_locale_summary, resolve_artifact_locale
|
|
24
24
|
from .config import get_git_packages, get_journal_snippet_enabled
|
|
25
|
-
from .session_memory import get_latest_journal_summary
|
|
26
25
|
from .git import run_git
|
|
27
26
|
from .packages_context import get_packages_section
|
|
28
27
|
from .tasks import iter_active_tasks, load_task, get_all_statuses, children_progress
|
|
@@ -48,7 +47,30 @@ from .paths import (
|
|
|
48
47
|
# =============================================================================
|
|
49
48
|
|
|
50
49
|
_PACKAGE_NAME = "@blxzer/cursor-trellis"
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _resolve_python_command() -> str:
|
|
53
|
+
"""Pick the platform-appropriate python command.
|
|
54
|
+
|
|
55
|
+
Mirrors test resolvePython() (win32 prefers python, POSIX prefers
|
|
56
|
+
python3) so rendered guidance matches what integration tests assert.
|
|
57
|
+
"""
|
|
58
|
+
candidates = ["python", "python3"] if os.name == "nt" else ["python3", "python"]
|
|
59
|
+
for exe in candidates:
|
|
60
|
+
try:
|
|
61
|
+
subprocess.run(
|
|
62
|
+
[exe, "--version"],
|
|
63
|
+
capture_output=True,
|
|
64
|
+
timeout=2,
|
|
65
|
+
check=True,
|
|
66
|
+
)
|
|
67
|
+
return exe
|
|
68
|
+
except (OSError, subprocess.SubprocessError):
|
|
69
|
+
continue
|
|
70
|
+
return "python"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
_PYTHON_CMD = _resolve_python_command()
|
|
52
74
|
_UPDATE_CHECK_TIMEOUT_SECONDS = 1.0
|
|
53
75
|
_VERSION_RE = re.compile(
|
|
54
76
|
r"^\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?\s*$"
|
|
@@ -842,6 +864,10 @@ def _resolve_journal_snippet(
|
|
|
842
864
|
) -> str:
|
|
843
865
|
if not journal_file or not developer:
|
|
844
866
|
return ""
|
|
867
|
+
try:
|
|
868
|
+
from .session_memory import get_latest_journal_summary
|
|
869
|
+
except ImportError:
|
|
870
|
+
return ""
|
|
845
871
|
return get_latest_journal_summary(journal_file, repo_root, developer)
|
|
846
872
|
|
|
847
873
|
|
|
@@ -26,6 +26,7 @@ from .task_map import (
|
|
|
26
26
|
validate_child_archive_state,
|
|
27
27
|
validate_parent_children_complete,
|
|
28
28
|
)
|
|
29
|
+
from .full_quality import full_quality_archive_errors, rigor_from_required_controls
|
|
29
30
|
|
|
30
31
|
SCHEMA_VERSION = 1
|
|
31
32
|
BASELINE_GATE = "baseline-check"
|
|
@@ -182,13 +183,45 @@ def normalize_result(result: str) -> str:
|
|
|
182
183
|
return result.strip().upper()
|
|
183
184
|
|
|
184
185
|
|
|
186
|
+
def wave_c_stop_read_confirmed(start: Path | None = None) -> bool:
|
|
187
|
+
"""Return True after ``cstl update`` wrote the Wave C confirm flag.
|
|
188
|
+
|
|
189
|
+
Unconfirmed projects keep leftover dual-read. Missing ``.cstl`` is
|
|
190
|
+
unconfirmed (unit fixtures without a repo root stay dual-read).
|
|
191
|
+
"""
|
|
192
|
+
from .paths import DIR_WORKFLOW, get_repo_root
|
|
193
|
+
|
|
194
|
+
root = get_repo_root(start)
|
|
195
|
+
path = root / DIR_WORKFLOW / ".p36-wave-c.json"
|
|
196
|
+
if not path.is_file():
|
|
197
|
+
return False
|
|
198
|
+
try:
|
|
199
|
+
parsed = json.loads(path.read_text(encoding="utf-8"))
|
|
200
|
+
except (OSError, json.JSONDecodeError):
|
|
201
|
+
return False
|
|
202
|
+
return isinstance(parsed, dict) and parsed.get("confirmed") is True
|
|
203
|
+
|
|
204
|
+
|
|
185
205
|
def task_closeout_profile(task_dir: Path, task_data: dict | None = None) -> str:
|
|
186
|
-
"""Return lite, full, or parent closeout profile.
|
|
206
|
+
"""Return lite, full, or parent closeout profile.
|
|
207
|
+
|
|
208
|
+
New Tasks use persisted ``required_controls`` / Topology only. Missing
|
|
209
|
+
contract is explicit Lite. File existence and ``children[]`` are not
|
|
210
|
+
used to infer Rigor or Parent. Leftover kind/mode/classification are
|
|
211
|
+
dual-read only until Wave C is confirmed.
|
|
212
|
+
"""
|
|
187
213
|
data = task_data or {}
|
|
188
|
-
|
|
189
|
-
if
|
|
214
|
+
topology = data.get("topology") if isinstance(data.get("topology"), dict) else {}
|
|
215
|
+
if topology.get("kind") == "parent-child":
|
|
190
216
|
return "parent"
|
|
191
217
|
|
|
218
|
+
persisted = rigor_from_required_controls(data)
|
|
219
|
+
if persisted in ("lite", "full"):
|
|
220
|
+
return persisted
|
|
221
|
+
|
|
222
|
+
if wave_c_stop_read_confirmed(task_dir):
|
|
223
|
+
return "lite"
|
|
224
|
+
|
|
192
225
|
meta = data.get("meta") if isinstance(data.get("meta"), dict) else {}
|
|
193
226
|
candidates = [
|
|
194
227
|
data.get("task_kind"),
|
|
@@ -200,8 +233,6 @@ def task_closeout_profile(task_dir: Path, task_data: dict | None = None) -> str:
|
|
|
200
233
|
meta.get("classification"),
|
|
201
234
|
meta.get("mode"),
|
|
202
235
|
]
|
|
203
|
-
|
|
204
|
-
explicit_lite = False
|
|
205
236
|
for value in candidates:
|
|
206
237
|
if not isinstance(value, str):
|
|
207
238
|
continue
|
|
@@ -211,13 +242,8 @@ def task_closeout_profile(task_dir: Path, task_data: dict | None = None) -> str:
|
|
|
211
242
|
if "full" in normalized:
|
|
212
243
|
return "full"
|
|
213
244
|
if "lite" in normalized:
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if explicit_lite:
|
|
217
|
-
return "lite"
|
|
245
|
+
return "lite"
|
|
218
246
|
|
|
219
|
-
if (task_dir / "design.md").is_file() and (task_dir / "implement.md").is_file():
|
|
220
|
-
return "full"
|
|
221
247
|
return "lite"
|
|
222
248
|
|
|
223
249
|
|
|
@@ -478,7 +504,12 @@ def make_baseline_record(
|
|
|
478
504
|
|
|
479
505
|
|
|
480
506
|
def write_gate_record(task_data: dict, transition: str, gate: str, record: dict) -> None:
|
|
481
|
-
"""Write a gate record into task_data in-place.
|
|
507
|
+
"""Write a gate record into task_data in-place.
|
|
508
|
+
|
|
509
|
+
This mutates memory only. Stage 2 persists `quality_gate_results` as a
|
|
510
|
+
Kernel Command projection (same command as `record-gate` / start / archive).
|
|
511
|
+
Do not treat this helper as an independent `task.json` writer.
|
|
512
|
+
"""
|
|
482
513
|
qgr = task_data.get("quality_gate_results")
|
|
483
514
|
if not isinstance(qgr, dict):
|
|
484
515
|
qgr = {}
|
|
@@ -499,6 +530,35 @@ def write_gate_record(task_data: dict, transition: str, gate: str, record: dict)
|
|
|
499
530
|
transition_records[gate] = record
|
|
500
531
|
|
|
501
532
|
|
|
533
|
+
KERNEL_PROJECTION_EXTRA_KEYS = (
|
|
534
|
+
"quality_gate_results",
|
|
535
|
+
"execution_approval",
|
|
536
|
+
"required_controls",
|
|
537
|
+
"ac_evidence_ledger",
|
|
538
|
+
"independent_check",
|
|
539
|
+
"topology",
|
|
540
|
+
"dependency_graph",
|
|
541
|
+
"ondemand_modules",
|
|
542
|
+
"depends_on",
|
|
543
|
+
"dependency_satisfied",
|
|
544
|
+
"decompose_proposal",
|
|
545
|
+
"profile_health",
|
|
546
|
+
"event_bridge",
|
|
547
|
+
"middleware_providers",
|
|
548
|
+
"capability_router",
|
|
549
|
+
"required_capabilities",
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def collect_kernel_projection_extras(task_data: dict) -> dict:
|
|
554
|
+
"""Non-canonical task.json fields owned by the same Kernel Command."""
|
|
555
|
+
extras: dict = {}
|
|
556
|
+
for key in KERNEL_PROJECTION_EXTRA_KEYS:
|
|
557
|
+
if key in task_data:
|
|
558
|
+
extras[key] = task_data[key]
|
|
559
|
+
return extras
|
|
560
|
+
|
|
561
|
+
|
|
502
562
|
def build_reviewer_gate_record(
|
|
503
563
|
task_dir: Path,
|
|
504
564
|
task_data: dict,
|
|
@@ -998,6 +1058,7 @@ def validate_archive(
|
|
|
998
1058
|
|
|
999
1059
|
errors.extend(_required_file_errors(task_dir, ["verify.md"]))
|
|
1000
1060
|
errors.extend(_verify_evidence_errors(task_dir, task_data))
|
|
1061
|
+
errors.extend(full_quality_archive_errors(task_dir, task_data))
|
|
1001
1062
|
|
|
1002
1063
|
parent_name = task_data.get("parent")
|
|
1003
1064
|
if isinstance(parent_name, str) and parent_name:
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Parent/Child task-map helpers.
|
|
4
4
|
|
|
5
|
-
task-map.md is the
|
|
6
|
-
|
|
5
|
+
task-map.md is a projection of the Kernel Topology / Dependency Graph plus
|
|
6
|
+
Parent orchestration fields (child worker state, event log). Graph authority
|
|
7
|
+
is Kernel extras (`topology`, `dependency_graph`); this module may render,
|
|
8
|
+
not invent, typed edges.
|
|
7
9
|
"""
|
|
8
10
|
|
|
9
11
|
from __future__ import annotations
|
|
@@ -75,7 +77,7 @@ def ensure_task_map(
|
|
|
75
77
|
data = {
|
|
76
78
|
"parent_id": parent_data.get("id") or parent_dir.name,
|
|
77
79
|
"contract_epoch": 1,
|
|
78
|
-
"execution_topology": "
|
|
80
|
+
"execution_topology": "parallel",
|
|
79
81
|
"merge_limit": 1,
|
|
80
82
|
"children": [],
|
|
81
83
|
"stages": [],
|
|
@@ -85,7 +87,7 @@ def ensure_task_map(
|
|
|
85
87
|
|
|
86
88
|
data.setdefault("parent_id", parent_data.get("id") or parent_dir.name)
|
|
87
89
|
data.setdefault("contract_epoch", 1)
|
|
88
|
-
data.setdefault("execution_topology", "
|
|
90
|
+
data.setdefault("execution_topology", "parallel")
|
|
89
91
|
data.setdefault("merge_limit", 1)
|
|
90
92
|
data.setdefault("children", [])
|
|
91
93
|
data.setdefault("stages", [])
|
|
@@ -102,6 +104,16 @@ def ensure_task_map(
|
|
|
102
104
|
data["children"].append(child)
|
|
103
105
|
existing[child_name] = child
|
|
104
106
|
|
|
107
|
+
extras = {
|
|
108
|
+
key: parent_data[key]
|
|
109
|
+
for key in ("topology", "dependency_graph")
|
|
110
|
+
if key in parent_data
|
|
111
|
+
}
|
|
112
|
+
if extras:
|
|
113
|
+
from .ondemand_topology import apply_kernel_graph_projection
|
|
114
|
+
|
|
115
|
+
apply_kernel_graph_projection(data, extras)
|
|
116
|
+
|
|
105
117
|
write_task_map(parent_dir, data, body, event)
|
|
106
118
|
return data
|
|
107
119
|
|
|
@@ -492,12 +504,21 @@ def _default_body() -> str:
|
|
|
492
504
|
return (
|
|
493
505
|
"# Task Map\n\n"
|
|
494
506
|
"## Orchestration notes\n\n"
|
|
495
|
-
"-
|
|
496
|
-
"
|
|
507
|
+
"- **parallel-first:** Prefer Cursor Multitask / Build in Parallel / Agent `Task` / native "
|
|
508
|
+
"worktree. Trellis does not schedule workers or replace Multitask.\n"
|
|
509
|
+
"- `execution_topology: parallel` — default when ≥2 children have empty `depends_on`. "
|
|
510
|
+
"Parent `integrate-child`, HITL, `start-execution --approved`, and reviewer gates stay "
|
|
511
|
+
"serial (`merge_limit`).\n"
|
|
512
|
+
"- `serial_reason` — required when topology is `serial`. One of: shared write-set / "
|
|
513
|
+
"HITL or reviewer gate / `depends_on` unmet / user asked serial / conflict surface "
|
|
514
|
+
"cannot isolate.\n"
|
|
515
|
+
"- `stages:` (or `parallel_groups`) — concurrently runnable child id groups.\n"
|
|
516
|
+
"- `merge_points` — where serial join happens (usually `integrate-child` + gates). "
|
|
517
|
+
"Default `merge_limit: 1`.\n"
|
|
518
|
+
"- `touches` (or `conflict_surface`) — write-sets that can collide; declare before dispatch.\n"
|
|
497
519
|
"- Child-reported states: `open` → `working` → `blocked` | `review`.\n"
|
|
498
520
|
"- Parent-controlled states: `review` → `changes` | `accepted` → `integrating` → "
|
|
499
521
|
"`integrated` | `cancelled`.\n"
|
|
500
|
-
"- Declare `touches` per child before dispatch to reduce merge conflicts.\n"
|
|
501
522
|
"- `isolation: git-worktree` — run `prepare-child-worktree` from the **git package root** "
|
|
502
523
|
"(not a non-git harness root).\n\n"
|
|
503
524
|
"## Event Log\n\n"
|
|
@@ -594,10 +615,22 @@ def _format_frontmatter(data: dict) -> str:
|
|
|
594
615
|
lines = [
|
|
595
616
|
f"parent_id: {_format_value(data.get('parent_id'))}",
|
|
596
617
|
f"contract_epoch: {_format_value(data.get('contract_epoch', 1))}",
|
|
597
|
-
f"execution_topology: {_format_value(data.get('execution_topology', '
|
|
618
|
+
f"execution_topology: {_format_value(data.get('execution_topology', 'parallel'))}",
|
|
598
619
|
f"merge_limit: {_format_value(data.get('merge_limit', 1))}",
|
|
599
|
-
"children:",
|
|
600
620
|
]
|
|
621
|
+
if data.get("serial_reason"):
|
|
622
|
+
lines.append(f"serial_reason: {_format_value(data.get('serial_reason'))}")
|
|
623
|
+
if data.get("parallel_groups"):
|
|
624
|
+
lines.append(f"parallel_groups: {_format_value(data.get('parallel_groups'))}")
|
|
625
|
+
if data.get("merge_points"):
|
|
626
|
+
lines.append(f"merge_points: {_format_value(data.get('merge_points'))}")
|
|
627
|
+
if data.get("conflict_surface"):
|
|
628
|
+
lines.append(f"conflict_surface: {_format_value(data.get('conflict_surface'))}")
|
|
629
|
+
if data.get("graph_authority"):
|
|
630
|
+
lines.append(f"graph_authority: {_format_value(data.get('graph_authority'))}")
|
|
631
|
+
if data.get("topology_kind"):
|
|
632
|
+
lines.append(f"topology_kind: {_format_value(data.get('topology_kind'))}")
|
|
633
|
+
lines.append("children:")
|
|
601
634
|
children = data.get("children", [])
|
|
602
635
|
if isinstance(children, list):
|
|
603
636
|
for child in children:
|