flower-trellis 0.5.2-beta.0 → 0.5.2

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.
Files changed (78) hide show
  1. package/enhancements/0.6/.agents/skills/trellis-auto-loop/SKILL.md +16 -3
  2. package/enhancements/0.6/.agents/skills/trellis-check-all/references/document-drift-auto-remediation.md +7 -0
  3. package/enhancements/0.6/.agents/skills/trellis-check-all/references/reporting-and-disposition.md +2 -2
  4. package/enhancements/0.6/.agents/skills/trellis-push/SKILL.md +14 -9
  5. package/enhancements/0.6/.agents/skills/trellis-task-brief/SKILL.md +25 -5
  6. package/enhancements/0.6/.claude/skills/trellis-auto-loop/SKILL.md +16 -3
  7. package/enhancements/0.6/.claude/skills/trellis-check-all/references/document-drift-auto-remediation.md +7 -0
  8. package/enhancements/0.6/.claude/skills/trellis-check-all/references/reporting-and-disposition.md +2 -2
  9. package/enhancements/0.6/.claude/skills/trellis-push/SKILL.md +14 -9
  10. package/enhancements/0.6/.claude/skills/trellis-task-brief/SKILL.md +25 -5
  11. package/enhancements/0.6/overrides/bundles/control-plane-integrity.json +2 -1
  12. package/enhancements/0.6/overrides/bundles/trellis-meta.json +12 -0
  13. package/enhancements/0.6/overrides/conflicts.json +558 -3
  14. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/constants-selector.py +6 -0
  15. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/helpers-selector.py +150 -0
  16. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/imports-content.py +4 -0
  17. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/imports-selector.py +8 -0
  18. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/output-content.py +7 -0
  19. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/output-selector.py +13 -0
  20. package/enhancements/0.6/overrides/patches/scripts/session-context-update-boundary/patch.json +53 -0
  21. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/bundled-overrides-baseline.md +16 -0
  22. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/bundled-overrides-content.md +10 -0
  23. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/customization-principles-baseline.md +7 -0
  24. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/customization-principles-content.md +9 -0
  25. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/file-boundaries-baseline.md +19 -0
  26. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/file-boundaries-content.md +13 -0
  27. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/patch.json +157 -0
  28. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/skill-taxonomy-baseline.md +11 -0
  29. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/skill-taxonomy-content.md +15 -0
  30. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/system-model-baseline.md +9 -0
  31. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/system-model-content.md +13 -0
  32. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/template-hashes-baseline.md +11 -0
  33. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-architecture-and-ownership/template-hashes-content.md +11 -0
  34. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/customization-entry-content.md +1 -0
  35. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/customization-entry-selector.md +1 -0
  36. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/customization-order-baseline.md +7 -0
  37. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/customization-order-content.md +10 -0
  38. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/patch.json +179 -0
  39. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/platform-edit-route-baseline.md +11 -0
  40. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/platform-edit-route-content.md +11 -0
  41. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/skill-classification-content.md +9 -0
  42. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/skill-classification-selector.md +8 -0
  43. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/skill-edit-route-baseline.md +13 -0
  44. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/skill-edit-route-content.md +13 -0
  45. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/workflow-edit-route-baseline.md +7 -0
  46. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/workflow-edit-route-content.md +8 -0
  47. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/workflow-entry-content.md +1 -0
  48. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-customization-routing/workflow-entry-selector.md +1 -0
  49. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/current-rules-baseline.md +12 -0
  50. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/current-rules-content.md +11 -0
  51. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/description-content.md +1 -0
  52. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/description-selector.md +1 -0
  53. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/how-to-use-baseline.md +7 -0
  54. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/how-to-use-content.md +7 -0
  55. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/patch.json +105 -0
  56. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/scope-content.md +10 -0
  57. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-mode-precedence/scope-selector.md +9 -0
  58. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/owner-routing-baseline.md +8 -0
  59. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/owner-routing-content.md +20 -0
  60. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/patch.json +106 -0
  61. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/state-boundary-baseline.md +20 -0
  62. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/state-boundary-content.md +7 -0
  63. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/workflow-change-map-baseline.md +13 -0
  64. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/workflow-change-map-content.md +16 -0
  65. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/workflow-source-content.md +1 -0
  66. package/enhancements/0.6/overrides/patches/skills/trellis-meta/managed-workflow-owners/workflow-source-selector.md +1 -0
  67. package/enhancements/0.6/overrides/patches/workflow/intent-routing/request-triage/content.md +3 -2
  68. package/enhancements/0.6/overrides/patches/workflow/task-brief-review/phase-1-activate-content.md +2 -2
  69. package/enhancements/0.6/scripts/auto_loop.py +247 -33
  70. package/enhancements/0.6/scripts/spec_router.py +486 -25
  71. package/enhancements/MANIFEST.json +56 -2
  72. package/package.json +3 -3
  73. package/src/builtin-plugins/skill-garden/content-adapter.js +29 -6
  74. package/src/builtin-plugins/skill-garden/provider.js +13 -2
  75. package/src/lib/patch-conflicts.js +28 -2
  76. package/src/lib/patch-engine.js +41 -3
  77. package/src/lib/platform-patch-adapters.js +8 -7
  78. package/src/lib/trellis-python-command.js +95 -0
@@ -0,0 +1,106 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "id": "trellis-meta-managed-workflow-owners",
4
+ "purpose": "managed_workflow_owners",
5
+ "operations": [
6
+ {
7
+ "id": "trellis-meta-managed-workflow-source",
8
+ "operation": "replace",
9
+ "targetPolicy": "each-existing",
10
+ "targets": [
11
+ { "kind": "skill", "path": ".agent/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
12
+ { "kind": "skill", "path": ".agents/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
13
+ { "kind": "skill", "path": ".claude/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
14
+ { "kind": "skill", "path": ".codebuddy/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
15
+ { "kind": "skill", "path": ".cursor/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
16
+ { "kind": "skill", "path": ".devin/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
17
+ { "kind": "skill", "path": ".factory/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
18
+ { "kind": "skill", "path": ".github/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
19
+ { "kind": "skill", "path": ".kilocode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
20
+ { "kind": "skill", "path": ".kiro/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
21
+ { "kind": "skill", "path": ".opencode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
22
+ { "kind": "skill", "path": ".pi/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
23
+ { "kind": "skill", "path": ".qoder/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
24
+ { "kind": "skill", "path": ".reasonix/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
25
+ { "kind": "skill", "path": ".trae/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" }
26
+ ],
27
+ "selector": { "type": "literal", "source": "workflow-source-selector.md", "expectedMatches": 1 },
28
+ "content": { "source": "workflow-source-content.md" }
29
+ },
30
+ {
31
+ "id": "trellis-meta-managed-owner-routing",
32
+ "operation": "replace",
33
+ "targetPolicy": "each-existing",
34
+ "targets": [
35
+ { "kind": "skill", "path": ".agent/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
36
+ { "kind": "skill", "path": ".agents/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
37
+ { "kind": "skill", "path": ".claude/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
38
+ { "kind": "skill", "path": ".codebuddy/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
39
+ { "kind": "skill", "path": ".cursor/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
40
+ { "kind": "skill", "path": ".devin/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
41
+ { "kind": "skill", "path": ".factory/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
42
+ { "kind": "skill", "path": ".github/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
43
+ { "kind": "skill", "path": ".kilocode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
44
+ { "kind": "skill", "path": ".kiro/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
45
+ { "kind": "skill", "path": ".opencode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
46
+ { "kind": "skill", "path": ".pi/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
47
+ { "kind": "skill", "path": ".qoder/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
48
+ { "kind": "skill", "path": ".reasonix/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
49
+ { "kind": "skill", "path": ".trae/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" }
50
+ ],
51
+ "selector": { "type": "markdown-section", "heading": "## Skill Routing" },
52
+ "baselines": ["owner-routing-baseline.md"],
53
+ "content": { "source": "owner-routing-content.md" }
54
+ },
55
+ {
56
+ "id": "trellis-meta-managed-state-boundary",
57
+ "operation": "replace",
58
+ "targetPolicy": "each-existing",
59
+ "targets": [
60
+ { "kind": "skill", "path": ".agent/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
61
+ { "kind": "skill", "path": ".agents/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
62
+ { "kind": "skill", "path": ".claude/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
63
+ { "kind": "skill", "path": ".codebuddy/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
64
+ { "kind": "skill", "path": ".cursor/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
65
+ { "kind": "skill", "path": ".devin/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
66
+ { "kind": "skill", "path": ".factory/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
67
+ { "kind": "skill", "path": ".github/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
68
+ { "kind": "skill", "path": ".kilocode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
69
+ { "kind": "skill", "path": ".kiro/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
70
+ { "kind": "skill", "path": ".opencode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
71
+ { "kind": "skill", "path": ".pi/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
72
+ { "kind": "skill", "path": ".qoder/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
73
+ { "kind": "skill", "path": ".reasonix/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
74
+ { "kind": "skill", "path": ".trae/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" }
75
+ ],
76
+ "selector": { "type": "markdown-section", "heading": "## Workflow-State Prompt Blocks" },
77
+ "baselines": ["state-boundary-baseline.md"],
78
+ "content": { "source": "state-boundary-content.md" }
79
+ },
80
+ {
81
+ "id": "trellis-meta-managed-workflow-change-map",
82
+ "operation": "replace",
83
+ "targetPolicy": "each-existing",
84
+ "targets": [
85
+ { "kind": "skill", "path": ".agent/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
86
+ { "kind": "skill", "path": ".agents/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
87
+ { "kind": "skill", "path": ".claude/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
88
+ { "kind": "skill", "path": ".codebuddy/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
89
+ { "kind": "skill", "path": ".cursor/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
90
+ { "kind": "skill", "path": ".devin/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
91
+ { "kind": "skill", "path": ".factory/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
92
+ { "kind": "skill", "path": ".github/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
93
+ { "kind": "skill", "path": ".kilocode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
94
+ { "kind": "skill", "path": ".kiro/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
95
+ { "kind": "skill", "path": ".opencode/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
96
+ { "kind": "skill", "path": ".pi/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
97
+ { "kind": "skill", "path": ".qoder/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
98
+ { "kind": "skill", "path": ".reasonix/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" },
99
+ { "kind": "skill", "path": ".trae/skills/trellis-meta/references/local-architecture/workflow.md", "missing": "skip" }
100
+ ],
101
+ "selector": { "type": "markdown-section", "heading": "## Local Modification Patterns" },
102
+ "baselines": ["workflow-change-map-baseline.md"],
103
+ "content": { "source": "workflow-change-map-content.md" }
104
+ }
105
+ ]
106
+ }
@@ -0,0 +1,20 @@
1
+ ## Workflow-State Prompt Blocks
2
+
3
+ The bottom of `workflow.md` can contain state blocks like this:
4
+
5
+ ```text
6
+ [workflow-state:no_task]
7
+ ...
8
+ [/workflow-state:no_task]
9
+ ```
10
+
11
+ Hooks choose the right block based on current task status and inject it into the conversation. Common states include:
12
+
13
+ | State | Meaning |
14
+ | --- | --- |
15
+ | `no_task` | The current session has no active task. |
16
+ | `planning` | The task is still in requirements, research, or context configuration. |
17
+ | `in_progress` | The task has entered implementation and checking. |
18
+ | `completed` | The task is complete and waiting for wrap-up or archive. |
19
+
20
+ If the user wants to change policies such as "whether to create a task when there is no task," "when task creation may be skipped," or "whether sub-agents are required," edit these state blocks and the routing table above them.
@@ -0,0 +1,7 @@
1
+ ## Workflow-State Prompt Blocks
2
+
3
+ `workflow.md` may contain paired blocks such as `[workflow-state:no_task]...[/workflow-state:no_task]`. Hooks select the current block and inject it as a one-hop next-action guard.
4
+
5
+ State blocks are not the owner of complete planning, routing, checking, commit, or archive procedures. Keep detailed semantics in the owner skill/helper and keep the state body small enough to identify the immediate gate. If a managed state policy changes, update its Skill-Garden Patch baseline/content and the owning workflow contract together; do not edit only the deployed block.
6
+
7
+ Treat the status names actually present in the local workflow and task runtime as authoritative. Do not copy a fixed state or error matrix into this reference.
@@ -0,0 +1,13 @@
1
+ ## Local Modification Patterns
2
+
3
+ Common changes:
4
+
5
+ | Goal | Edit point |
6
+ | --- | --- |
7
+ | Add a phase | Update the Phase Index, phase body, routing, and state blocks. |
8
+ | Change task creation policy | Update the `no_task` state block and Phase 1 description. |
9
+ | Change the default implementation/check path | Update Phase 2 and skill routing. |
10
+ | Change the wrap-up flow | Update Phase 3 and `finish-work` related descriptions. Note the current split: Phase 3.4 = AI-driven code commits (batched, user-confirmed), Phase 3.5 = `/finish-work` (archive + record session). `/finish-work` refuses to run if the working tree is dirty. |
11
+ | Change platform differences | Update routing descriptions grouped by platform. |
12
+
13
+ After editing, make the AI reread `.trellis/workflow.md`; do not assume the flow from the old conversation is still valid.
@@ -0,0 +1,16 @@
1
+ ## Local Modification Patterns
2
+
3
+ Start from the runtime section, then move to its owner:
4
+
5
+ | Goal | Durable owner route |
6
+ | --- | --- |
7
+ | Add or reorder a phase | Workflow Patch/source plus every affected owner handoff |
8
+ | Change task creation or scope policy | Request Triage, task-intent helper, and the managed workflow/state Patch |
9
+ | Change planning activation | `trellis-task-brief` and the task-start guard |
10
+ | Change implement/check execution | `trellis-route`; Check-All remains the unified check entry |
11
+ | Change automatic continuation | `trellis-auto-loop` and its runner action contract |
12
+ | Change spec/commit/archive behavior | `trellis-update-spec`, `trellis-push`, or `trellis-finish-work` respectively |
13
+ | Change recovery after interruption | `trellis-continue` and task-progress state |
14
+ | Change one platform adapter | The owning platform file/Patch while preserving the shared workflow contract |
15
+
16
+ In managed mode, update the source Patch and owner, run the synchronization and compiled-target checks, then reread the final `.trellis/workflow.md`. In native mode, a narrow local edit remains valid when no Plugin ownership claim applies.
@@ -0,0 +1 @@
1
+ `.trellis/workflow.md` is the runtime semantic contract for the current project. It is sufficient to understand what should happen now, but not always sufficient to locate the durable edit point: Skill-Garden-managed sections must be traced to their Patch and workflow owner before modification.
@@ -0,0 +1 @@
1
+ `.trellis/workflow.md` is the Trellis workflow source of truth inside the user project. An AI does not need Trellis source code to understand how the current project should move tasks forward; this file is enough.
@@ -1,7 +1,8 @@
1
1
  - Infer `discuss`, `inspect`, `direct_edit`, `task_plan`, or `workflow_action` from the whole current request, scope, risk, side effects, active-task state, and latest explicit switch. Do not classify from one keyword alone.
2
+ - Asking for an opinion, expressing discomfort, rejecting a proposal, or asking how something should change is `discuss` unless the current request also explicitly authorizes a concrete edit. Asking to inspect, explain, verify, or locate a cause is `inspect`; read-only findings do not expand into repair authorization.
2
3
  - Repair authorization and permission to skip task planning are separate. If repair scope is unknown, use `inspect` first and reclassify from evidence before editing.
3
- - `direct_edit` requires known, bounded, local, low-risk, reversible scope and simple validation. Permission/authentication/data-scope/security, shared contracts, cross-package/layer or multi-entry behavior, database/migration/configuration/release/external effects, historical regressions, systematic validation, or unknown scope are `task_plan` signals.
4
- - Before choosing an approach for non-trivial project work, run `python3 ./.trellis/scripts/spec_router.py "<short query describing the intended action>"` when project-local SOPs or conventions may change the correct action. Build the query from the request, intended commands, affected files or systems, package/layer, and domain terms; read high-confidence matches before acting and relevant medium-confidence matches when their path, heading, index description, or reason fits.
4
+ - `direct_edit` requires known, bounded, low-risk, reversible scope, no unresolved design choice, and simple validation. Permission/authentication/data-scope/security, database/migration/release/external effects, or unknown scope remain strong `task_plan` boundaries. Shared contracts, cross-package/layer or multi-entry behavior, configuration, historical regressions, and systematic validation raise the evidence and validation bar; they are risk signals, not automatic `task_plan` outcomes. An exact rollback or mechanically synchronized known change may remain `direct_edit` or `workflow_action` when its behavior, scope, side effects, and validation are already determined.
5
+ - Before choosing an approach for non-trivial project work, run `python3 ./.trellis/scripts/spec_router.py "<short query describing the intended action>"` when project-local SOPs or conventions may change the correct action. Build the query from the request, intended commands, affected files or systems, package/layer, and domain terms. For high-confidence matches, follow `load_strategy`: `full` reads the file, `sections` reads the listed ranges, and `outline` inspects headings before reading relevant ranges; expand beyond that plan only when selected context is insufficient. Apply the same plan to medium-confidence matches only when their path, heading, index description, or reason is clearly relevant.
5
6
  - Project Knowledge Discovery applies once per user intent, workflow phase, or decision boundary to Trellis/workflow/config/hooks, CLI behavior, release/publish/deploy/tag, Git history actions, data/migration/rollback, cross-layer design, generated artifacts, and install/sync pipelines. Skip it for pure Q&A, simple read-only inspection, opening local tools, or trivial edits unless project rules may alter the approach.
6
7
  - When an active task exists, apply the Active Task Scope Guard before artifact ownership, task routing, or file edits. If new implementation work does not belong to the active task title/brief, stop and choose from the user's intent: create a new task, explicitly include it here and update artifacts first, or proceed untracked without reusing the current task or progress. `task_intent.py` only executes the already-decided create/discard/current-task safety boundary; it does not decide semantic ownership.
7
8
  - High-confidence reversible steps proceed without a mechanical task-creation question. Clear complex implementation intent authorizes creating a planning task and entering `trellis-brainstorm`; it never authorizes `task.py start` or implementation.
@@ -1,10 +1,10 @@
1
1
  #### 1.4 Activate task `[required · once]`
2
2
 
3
- Before changing task status, load `trellis-task-brief`, refresh `<task>/brief.md`, display the full brief in chat, then stop the current turn and wait for planning review confirmation. Earlier implementation intent is not confirmation.
3
+ Before changing task status, load `trellis-task-brief`, refresh `<task>/brief.md`, and display the full brief in chat. Unless `trellis-task-brief` validates an explicit preauthorization for the current final Brief, stop the current turn and wait for planning review confirmation. Ordinary implementation or task-creation intent is not confirmation.
4
4
 
5
5
  Lightweight tasks need `prd.md`; complex tasks also need `design.md` and `implement.md`. Sub-agent routes require real entries in both JSONL manifests.
6
6
 
7
- Only after the user confirms the displayed brief in a later message, run:
7
+ After a later confirmation, or in the same turn when that explicit preauthorization remains valid, run:
8
8
 
9
9
  ```bash
10
10
  python3 ./.trellis/scripts/task.py start <task-dir>
@@ -26,6 +26,7 @@ LEGACY_SCHEMA_VERSION = 1
26
26
  DEFAULT_PROFILE = "commit-only"
27
27
  MAX_FIX_RECHECK = 3
28
28
  MAX_PLANNING_REPAIR = 3
29
+ MAX_ARTIFACT_RECONCILE = 3
29
30
  DECISION_LOG_LIMIT = 20
30
31
  MANIFEST_AUDIT_TAIL_LIMIT = 3
31
32
  TASK_PROGRESS_NOTE_LIMIT = 500
@@ -40,6 +41,7 @@ ALLOWED_TASK_STATUSES = {"planning", "in_progress"}
40
41
  VALID_IMPLEMENT_ROUTES = {"inline", "subagent"}
41
42
  VALID_CHECK_ROUTES = {"check-all-inline", "check-all-subagent"}
42
43
  VALID_CHECK_DEPTHS = {"auto", "light", "full"}
44
+ CHECK_ACTIONS = {"run_check_all", "run_recheck"}
43
45
  RECOVERABLE_BLOCK_REASONS = {
44
46
  "missing-prd",
45
47
  "open-questions",
@@ -936,7 +938,12 @@ def _manifest_payload(state: dict[str, Any], revision: int) -> dict[str, Any]:
936
938
  }
937
939
 
938
940
 
939
- def _append_manifest_revision(state: dict[str, Any], decision_id: str | None = None) -> dict[str, Any]:
941
+ def _append_manifest_revision(
942
+ state: dict[str, Any],
943
+ decision_id: str | None = None,
944
+ change_source: str | None = None,
945
+ files: list[str] | None = None,
946
+ ) -> dict[str, Any]:
940
947
  """追加并激活一个 run manifest revision。"""
941
948
  revisions = state.get("manifest_revisions")
942
949
  if not isinstance(revisions, list):
@@ -948,6 +955,10 @@ def _append_manifest_revision(state: dict[str, Any], decision_id: str | None = N
948
955
  payload = _manifest_payload(state, next_revision)
949
956
  if decision_id:
950
957
  payload["decision_id"] = decision_id
958
+ if change_source:
959
+ payload["change_source"] = change_source
960
+ if files:
961
+ payload["files"] = sorted(files)
951
962
  encoded = json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
952
963
  payload["sha256"] = hashlib.sha256(encoded.encode("utf-8")).hexdigest()
953
964
  revisions.append(payload)
@@ -1548,7 +1559,7 @@ def _make_item(repo_root: Path, task_ref: str) -> dict[str, Any]:
1548
1559
  "blocked_by": [],
1549
1560
  "owned_dirty": [],
1550
1561
  "decision_count": 0,
1551
- "attempts": {"fix_recheck": 0},
1562
+ "attempts": {"fix_recheck": 0, "artifact_reconcile": 0},
1552
1563
  "last_failure": None,
1553
1564
  "last_check": None,
1554
1565
  "last_action": None,
@@ -1608,11 +1619,17 @@ def _action(action: str, item: dict[str, Any], extra: dict[str, Any] | None = No
1608
1619
  elif action == "run_implement":
1609
1620
  base["instruction"] = "进入 Phase 2.1 implement route,并执行实现。"
1610
1621
  elif action == "run_check_all":
1611
- base["instruction"] = "进入 Phase 2.2 check route,按 requested_check_depth 执行统一 Check-All;完成后立即 record + next。"
1622
+ base["instruction"] = (
1623
+ "进入 Phase 2.2 check route,按 requested_check_depth 执行统一 Check-All;DOC 修复需精确声明 "
1624
+ "--doc-remediation-file。record 成功后立即 next;返回 retryable 时先在同一 action 自纠。"
1625
+ )
1612
1626
  elif action == "run_fix":
1613
1627
  base["instruction"] = "根据最近失败摘要修复问题。"
1614
1628
  elif action == "run_recheck":
1615
- base["instruction"] = "修复后重新执行统一 Check-All,不得低于 minimum_check_depth;完成后立即 record + next。"
1629
+ base["instruction"] = (
1630
+ "修复后重新执行统一 Check-All,不得低于 minimum_check_depth;DOC 修复需精确声明 "
1631
+ "--doc-remediation-file。record 成功后立即 next;返回 retryable 时先在同一 action 自纠。"
1632
+ )
1616
1633
  elif action == "run_spec_update":
1617
1634
  base["instruction"] = (
1618
1635
  "执行 trellis-update-spec 并读取 spec_update_result:no-op/written 时 "
@@ -1626,7 +1643,11 @@ def _action(action: str, item: dict[str, Any], extra: dict[str, Any] | None = No
1626
1643
  return base
1627
1644
 
1628
1645
 
1629
- def _remember_action(item: dict[str, Any], action_data: dict[str, Any]) -> dict[str, Any]:
1646
+ def _remember_action(
1647
+ item: dict[str, Any],
1648
+ action_data: dict[str, Any],
1649
+ artifact_sha256: dict[str, str] | None = None,
1650
+ ) -> dict[str, Any]:
1630
1651
  """记录 runner 已发出的待回写 action。"""
1631
1652
  item["last_action"] = {
1632
1653
  "action": action_data.get("action"),
@@ -1636,6 +1657,8 @@ def _remember_action(item: dict[str, Any], action_data: dict[str, Any]) -> dict[
1636
1657
  for key in ("prd_sha256", "planning_sha256", "handoff_sha256"):
1637
1658
  if action_data.get(key):
1638
1659
  item["last_action"][key] = action_data[key]
1660
+ if artifact_sha256 is not None:
1661
+ item["last_action"]["artifact_sha256"] = artifact_sha256
1639
1662
  item["updated_at"] = _utc_now()
1640
1663
  return action_data
1641
1664
 
@@ -1963,7 +1986,7 @@ def _next_running_v2(repo_root: Path, state: dict[str, Any]) -> tuple[dict[str,
1963
1986
  return item, _remember_action(item, _action("run_check_all", item, {
1964
1987
  "requested_check_depth": _requested_check_depth(state),
1965
1988
  "minimum_check_depth": _minimum_check_depth(item, "run_check_all"),
1966
- }))
1989
+ }), _task_artifact_hashes(repo_root, item))
1967
1990
  if step == "fix":
1968
1991
  attempts = int(item.setdefault("attempts", {}).get("fix_recheck", 0))
1969
1992
  if attempts > MAX_FIX_RECHECK:
@@ -1977,7 +2000,7 @@ def _next_running_v2(repo_root: Path, state: dict[str, Any]) -> tuple[dict[str,
1977
2000
  return item, _remember_action(item, _action("run_recheck", item, {
1978
2001
  "requested_check_depth": _requested_check_depth(state),
1979
2002
  "minimum_check_depth": _minimum_check_depth(item, "run_recheck"),
1980
- }))
2003
+ }), _task_artifact_hashes(repo_root, item))
1981
2004
  if step == "spec_update":
1982
2005
  return item, _remember_action(item, _action("run_spec_update", item))
1983
2006
  if step == "commit_only":
@@ -2294,11 +2317,14 @@ def cmd_retry_blocked(args: argparse.Namespace) -> int:
2294
2317
  item["status"] = "pending"
2295
2318
  item["blocked"] = None
2296
2319
  item["last_action"] = None
2297
- if reason == "retry-budget-exhausted":
2320
+ if reason in {"retry-budget-exhausted", "artifact-drift"}:
2298
2321
  attempts = item.get("attempts")
2299
2322
  if not isinstance(attempts, dict):
2300
2323
  attempts = {}
2301
- attempts["fix_recheck"] = 0
2324
+ if reason == "retry-budget-exhausted":
2325
+ attempts["fix_recheck"] = 0
2326
+ else:
2327
+ attempts["artifact_reconcile"] = 0
2302
2328
  item["attempts"] = attempts
2303
2329
  item["task_status"] = _task_status(repo_root, str(item.get("task")))
2304
2330
  item["updated_at"] = _utc_now()
@@ -3031,6 +3057,175 @@ def _consume_pending_artifact_decision(
3031
3057
  }
3032
3058
 
3033
3059
 
3060
+ def _consume_check_doc_remediation(
3061
+ repo_root: Path,
3062
+ state: dict[str, Any],
3063
+ item: dict[str, Any],
3064
+ action: str,
3065
+ raw_files: list[str],
3066
+ ) -> dict[str, Any] | None:
3067
+ """校验并消费 Check-All 声明的低风险任务文档修复。"""
3068
+ if not raw_files:
3069
+ return None
3070
+ if action not in CHECK_ACTIONS:
3071
+ return {
3072
+ "status": "error",
3073
+ "reason": "doc-remediation-action-not-allowed",
3074
+ "message": "--doc-remediation-file 只允许用于 run_check_all 或 run_recheck。",
3075
+ }
3076
+ if isinstance(item.get("pending_artifact_decision"), dict):
3077
+ return {
3078
+ "status": "error",
3079
+ "reason": "doc-remediation-decision-conflict",
3080
+ "message": "当前 action 同时存在待消费的 decide artifact 授权,不能混用 DOC remediation。",
3081
+ }
3082
+
3083
+ last_action = item.get("last_action")
3084
+ previous = last_action.get("artifact_sha256") if isinstance(last_action, dict) else None
3085
+ if not isinstance(previous, dict):
3086
+ return {
3087
+ "status": "error",
3088
+ "reason": "doc-remediation-baseline-missing",
3089
+ "message": "当前 outstanding check action 没有逐文件 artifact baseline,请重新启动安全流程。",
3090
+ }
3091
+
3092
+ task_dir = _task_dir(repo_root, str(item.get("task") or ""))
3093
+ allowed = {
3094
+ _baseline_key(".", _rel_path(repo_root, task_dir / "implement.md")),
3095
+ _baseline_key(".", _rel_path(repo_root, task_dir / "brief.md")),
3096
+ }
3097
+ declared = {_normalize_record_file(value) for value in raw_files}
3098
+ forbidden = sorted(declared - allowed)
3099
+ if forbidden:
3100
+ return {
3101
+ "status": "error",
3102
+ "reason": "doc-remediation-file-not-allowed",
3103
+ "files": forbidden,
3104
+ "allowed_files": sorted(allowed),
3105
+ }
3106
+
3107
+ current = _task_artifact_hashes(repo_root, item)
3108
+ changed = {
3109
+ key
3110
+ for key in set(previous) | set(current)
3111
+ if previous.get(key) != current.get(key)
3112
+ }
3113
+ if changed != declared:
3114
+ return {
3115
+ "status": "error",
3116
+ "reason": "doc-remediation-files-mismatch",
3117
+ "declared_files": sorted(declared),
3118
+ "changed_files": sorted(changed),
3119
+ }
3120
+
3121
+ planning_hash, handoff_hash = _current_artifact_hashes(repo_root, item)
3122
+ item["planning_sha256"] = planning_hash
3123
+ item["handoff_sha256"] = handoff_hash
3124
+ manifest = _append_manifest_revision(
3125
+ state,
3126
+ change_source="check-doc-remediation",
3127
+ files=sorted(changed),
3128
+ )
3129
+ detail = {
3130
+ "files": sorted(changed),
3131
+ "manifest_revision": manifest["revision"],
3132
+ "manifest_sha256": manifest["sha256"],
3133
+ }
3134
+ _append_item_decision(
3135
+ item,
3136
+ "check_doc_artifacts_rebound",
3137
+ "Check-All 低风险任务文档修复已重新绑定到 manifest",
3138
+ detail,
3139
+ )
3140
+ return detail
3141
+
3142
+
3143
+ def _record_artifact_drift(
3144
+ path: Path,
3145
+ state: dict[str, Any],
3146
+ item: dict[str, Any],
3147
+ action: str,
3148
+ args: argparse.Namespace,
3149
+ summary: str,
3150
+ detail: dict[str, Any],
3151
+ ) -> int:
3152
+ """按 action 风险和预算记录 artifact drift。"""
3153
+ if action not in CHECK_ACTIONS or (
3154
+ args.result == "blocked" and args.failure_type == "artifact-drift"
3155
+ ):
3156
+ item["last_action"] = None
3157
+ _block_item(item, "artifact-drift", summary, detail)
3158
+ _write_state(path, state)
3159
+ return _print({
3160
+ "status": "recorded",
3161
+ "run_id": state.get("run_id"),
3162
+ "task": item.get("task"),
3163
+ "item_status": item.get("status"),
3164
+ "current_step": item.get("current_step"),
3165
+ "summary": _format_summary(state, args),
3166
+ })
3167
+
3168
+ attempts = item.setdefault("attempts", {})
3169
+ attempt = int(attempts.get("artifact_reconcile", 0)) + 1
3170
+ attempts["artifact_reconcile"] = attempt
3171
+ item["last_failure"] = {
3172
+ "action": action,
3173
+ "failure_type": "artifact-drift",
3174
+ "summary": summary,
3175
+ "detail": detail,
3176
+ "failed_at": _utc_now(),
3177
+ }
3178
+ if attempt > MAX_ARTIFACT_RECONCILE:
3179
+ item["last_action"] = None
3180
+ _block_item(
3181
+ item,
3182
+ "artifact-drift",
3183
+ "Check action artifact drift 连续自纠超过 3 次",
3184
+ {**detail, "attempt": attempt, "max_attempts": MAX_ARTIFACT_RECONCILE},
3185
+ )
3186
+ _write_state(path, state)
3187
+ return _print({
3188
+ "status": "recorded",
3189
+ "run_id": state.get("run_id"),
3190
+ "task": item.get("task"),
3191
+ "item_status": item.get("status"),
3192
+ "current_step": item.get("current_step"),
3193
+ "summary": _format_summary(state, args),
3194
+ })
3195
+
3196
+ item["updated_at"] = _utc_now()
3197
+ _append_item_decision(
3198
+ item,
3199
+ "warning",
3200
+ summary,
3201
+ {
3202
+ "action": action,
3203
+ "failure_type": "artifact-drift",
3204
+ "attempt": attempt,
3205
+ "max_attempts": MAX_ARTIFACT_RECONCILE,
3206
+ "detail": detail,
3207
+ },
3208
+ )
3209
+ _write_state(path, state)
3210
+ return _print({
3211
+ "status": "retryable",
3212
+ "reason": "artifact-drift",
3213
+ "run_id": state.get("run_id"),
3214
+ "task": item.get("task"),
3215
+ "item_status": item.get("status"),
3216
+ "current_step": item.get("current_step"),
3217
+ "attempt": attempt,
3218
+ "max_attempts": MAX_ARTIFACT_RECONCILE,
3219
+ "outstanding_action": action,
3220
+ "detail": detail,
3221
+ "instruction": (
3222
+ "不要运行 next;撤回本 action 的误改后重录,或为合法 implement.md/brief.md 修复补充 "
3223
+ "--doc-remediation-file。无法安全归因时用 blocked + artifact-drift 重录。"
3224
+ ),
3225
+ "summary": _format_summary(state, args),
3226
+ })
3227
+
3228
+
3034
3229
  def cmd_record(args: argparse.Namespace) -> int:
3035
3230
  """记录 agent 执行结果。"""
3036
3231
  repo_root = _repo_root()
@@ -3144,24 +3339,47 @@ def cmd_record(args: argparse.Namespace) -> int:
3144
3339
  "current_step": item.get("current_step"),
3145
3340
  "summary": _format_summary(state, args),
3146
3341
  })
3342
+ doc_rebind = _consume_check_doc_remediation(
3343
+ repo_root,
3344
+ state,
3345
+ item,
3346
+ action,
3347
+ args.doc_remediation_file,
3348
+ )
3349
+ if isinstance(doc_rebind, dict) and doc_rebind.get("status") == "error":
3350
+ doc_rebind.update({"task": item.get("task"), "action": action})
3351
+ return _print(doc_rebind)
3147
3352
  artifact_rebind = _consume_pending_artifact_decision(repo_root, state, item)
3148
3353
  if isinstance(artifact_rebind, dict) and artifact_rebind.get("unauthorized"):
3149
- item["last_action"] = None
3150
- _block_item(
3354
+ return _record_artifact_drift(
3355
+ path,
3356
+ state,
3151
3357
  item,
3152
- "artifact-drift",
3358
+ action,
3359
+ args,
3153
3360
  "planning/handoff artifacts 的变化未被当前 AI 决策文件范围授权",
3154
3361
  artifact_rebind,
3155
3362
  )
3156
- _write_state(path, state)
3157
- return _print({
3158
- "status": "recorded",
3159
- "run_id": state.get("run_id"),
3160
- "task": item.get("task"),
3161
- "item_status": item.get("status"),
3162
- "current_step": item.get("current_step"),
3163
- "summary": _format_summary(state, args),
3164
- })
3363
+ if action in CHECK_ACTIONS and doc_rebind is None and artifact_rebind is None:
3364
+ last_action = item.get("last_action")
3365
+ previous = last_action.get("artifact_sha256") if isinstance(last_action, dict) else None
3366
+ if isinstance(previous, dict):
3367
+ current = _task_artifact_hashes(repo_root, item)
3368
+ changed = sorted(
3369
+ key
3370
+ for key in set(previous) | set(current)
3371
+ if previous.get(key) != current.get(key)
3372
+ )
3373
+ if changed:
3374
+ return _record_artifact_drift(
3375
+ path,
3376
+ state,
3377
+ item,
3378
+ action,
3379
+ args,
3380
+ "Check action 发出后 planning/handoff 文件发生未声明变化",
3381
+ {"changed_files": changed},
3382
+ )
3165
3383
  if item.get("planning_sha256"):
3166
3384
  try:
3167
3385
  planning_hash, handoff_hash = _current_artifact_hashes(repo_root, item)
@@ -3179,22 +3397,15 @@ def cmd_record(args: argparse.Namespace) -> int:
3179
3397
  planning_hash != item.get("planning_sha256")
3180
3398
  or handoff_hash != item.get("handoff_sha256")
3181
3399
  ):
3182
- item["last_action"] = None
3183
- _block_item(
3400
+ return _record_artifact_drift(
3401
+ path,
3402
+ state,
3184
3403
  item,
3185
- "artifact-drift",
3404
+ action,
3405
+ args,
3186
3406
  "action 发出后 planning/handoff artifacts 发生未授权变化",
3187
3407
  drift_detail,
3188
3408
  )
3189
- _write_state(path, state)
3190
- return _print({
3191
- "status": "recorded",
3192
- "run_id": state.get("run_id"),
3193
- "task": item.get("task"),
3194
- "item_status": item.get("status"),
3195
- "current_step": item.get("current_step"),
3196
- "summary": _format_summary(state, args),
3197
- })
3198
3409
  conflicts = _protected_path_conflicts(state, args.files)
3199
3410
  if conflicts:
3200
3411
  item["last_action"] = None
@@ -3220,6 +3431,8 @@ def cmd_record(args: argparse.Namespace) -> int:
3220
3431
  return _print(check_error)
3221
3432
 
3222
3433
  _record_check_result(state, item, action, args)
3434
+ if action in CHECK_ACTIONS:
3435
+ item.setdefault("attempts", {})["artifact_reconcile"] = 0
3223
3436
  item["last_action"] = None
3224
3437
  if args.result == "ok":
3225
3438
  _advance_after_ok(item, action, args)
@@ -3411,6 +3624,7 @@ def build_parser() -> argparse.ArgumentParser:
3411
3624
  record.add_argument("--failure-type")
3412
3625
  record.add_argument("--summary")
3413
3626
  record.add_argument("--files", nargs="*")
3627
+ record.add_argument("--doc-remediation-file", action="append", default=[])
3414
3628
  record.add_argument("--retained-files", nargs="*")
3415
3629
  record.add_argument("--commit")
3416
3630
  record.add_argument("--commit-message")