Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
id: 8qp20k
title: t-18h-done-task-filtering-assignment
type: standard
tags: [assign, workflow, release]
created_at: "2026-03-26 01:20:38"
status: active
---

# t-18h-done-task-filtering-assignment

## What Went Well

- Scoped assignment drive (`8qp1p5@010.01`) stayed deterministic: each sub-step was completed with explicit reports and verified state transitions.
- Implementation stayed narrowly aligned to the task boundary: `assign/prepare`, `assign/create`, and `ace-assign` usage docs were updated without touching unrelated runtime code.
- Verification was fast and reliable (`ace-test ace-assign`, profile run in verify-test), which kept confidence high before release.
- Release detection correctly identified only `ace-assign` from `origin/main...HEAD`, preventing accidental multi-package releases.

## What Could Be Improved

- Pre-commit review fallback (`ace-lint`) surfaced many pre-existing markdown/frontmatter issues, which reduced signal for newly introduced risk.
- The coordinated release workflow asked for one release commit, but `ace-git-commit` split package/root artifacts into two scope-based commits.
- Release-step context switching (manual workflow start before explicit skill handoff) added avoidable execution churn.

## Key Learnings

- For assignment subtrees with clean working trees, release package detection should still rely on `git diff origin/main...HEAD` to capture already-committed step output.
- This task confirmed the ownership boundary: done-task filtering behavior belongs in prepare/create workflow contracts rather than queue runtime execution.
- Review fallback behavior should be tuned for docs-heavy steps; otherwise lint noise can mask truly actionable findings.

## Action Items

- Add guidance to release workflows for handling multi-scope commit generation when strict single-commit output is required.
- Define a lower-noise pre-commit lint strategy for workflow/docs-only changes (or baseline-known lint debt) in assignment review steps.
- Add contract-focused regression coverage for done-task filtering language in `assign/prepare` and `assign/create` workflow files.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: 8qp.t.18h
status: in-progress
status: done
priority: medium
created_at: "2026-03-26 00:49:25"
estimate: TBD
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

## [0.9.928] - 2026-03-26

### Changed
- **ace-assign v0.38.2**: Updated `work-on-task` prepare/create contracts to filter all terminal task statuses (`done`, `skipped`, `cancelled`) and to no-op assignment creation when all requested refs are terminal.

### Technical
- **ace-assign v0.38.2**: Strengthened terminal-filter contract coverage and moved the workflow contract test into the standard `test/organisms/` bucket.

## [0.9.927] - 2026-03-26

### Technical
- **ace-assign v0.38.1**: Added regression tests to lock `work-on-task` done-task filtering contracts for mixed/all-done inputs and create-flow no-op behavior.

## [0.9.926] - 2026-03-26

### Changed
- **ace-assign v0.38.0**: Updated assignment prepare/create workflow contracts to filter already-done `work-on-task` refs before expansion, continue mixed sets with skip reporting, and abort all-done sets without creating assignments.

## [0.9.925] - 2026-03-23

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: ace-assign
specs:
ace-assign (0.37.0)
ace-assign (0.38.2)
ace-b36ts (~> 0.7)
ace-llm (~> 0.26)
ace-support-cli (~> 0.3)
Expand Down
24 changes: 24 additions & 0 deletions ace-assign/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.38.2] - 2026-03-26

### Changed
- Updated `work-on-task` prepare/create filtering guidance to treat terminal task statuses (`done`, `skipped`, `cancelled`) as filtered refs and abort all-terminal requests before assignment creation.
- Updated no-op messaging and edge-case language across prepare/create workflow instructions and usage docs to match the terminal-status contract.

### Technical
- Replaced fragile done-filter workflow contract assertions with stronger section-scoped terminal-contract checks.
- Moved the contract test into the standard `test/organisms/` bucket and removed the nonstandard `test/workflows/` location.

## [0.38.1] - 2026-03-26

### Technical
- Added regression coverage for `work-on-task` done-task filtering contracts, including mixed done/non-done handling, all-done no-op behavior, and create-flow no hidden-spec/`ace-assign create` guard assertions.

## [0.38.0] - 2026-03-26

### Added
- Added explicit done-task filtering rules to `assign/prepare` so `work-on-task` requests resolve refs first, skip `status: done` refs, continue mixed sets, and abort all-done sets before queue generation.

### Changed
- Updated `assign/create` workflow guidance so Path B respects filtered taskrefs from prepare and skips hidden-spec render/`ace-assign create` when all requested refs are already done.
- Updated usage docs with `work-on-task` prepare/create filtering behavior and no-assignment outcomes for all-done inputs.

## [0.37.0] - 2026-03-23

### Changed
Expand Down
15 changes: 13 additions & 2 deletions ace-assign/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ doc-type: user
title: ace-assign Usage Guide
purpose: Complete command reference for ace-assign queue orchestration, hierarchy, and fork execution.
ace-docs:
last-updated: 2026-03-22
last-checked: 2026-03-22
last-updated: 2026-03-26
last-checked: 2026-03-26
---

# ace-assign Usage Guide
Expand Down Expand Up @@ -185,6 +185,17 @@ Options:

## Workflow Patterns

### `work-on-task` Input Filtering (Prepare/Create Workflows)

When using workflow-driven assignment creation for `work-on-task` (`/as-assign-prepare` or `/as-assign-create`):

- Requested refs are resolved first (single, comma list, range, pattern).
- Terminal refs (`done`, `skipped`, `cancelled`) are skipped before queue expansion.
- Mixed sets continue with remaining non-terminal refs and report skipped terminal refs.
- If all requested refs are terminal, assignment creation stops with:
- `All requested tasks are already terminal (done/skipped/cancelled): <refs>`
- `No assignment created.`

### Scoped Subtree Execution


Expand Down
19 changes: 15 additions & 4 deletions ace-assign/handbook/workflow-instructions/assign/create.wf.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ If input is an existing `.yml`/`.yaml` file path:

If input is an exact preset/recipe-style request (for example `work-on-task --taskref 123`):
- Run `wfi://assign/prepare` to produce normalized job content
- Continue to hidden-spec rendering (step 4)
- If prepare reports all requested refs are already terminal (`done/skipped/cancelled`), stop and return that no assignment was created
- Otherwise continue to hidden-spec rendering (step 4) using the filtered ref set from prepare

#### Path C: Explicit Step or Freeform Intent (Default)

Expand All @@ -100,7 +101,11 @@ For unmatched phrases:
Skill-backed steps (for example `work-on-task`) stay high-level in rendered YAML.
Runtime `ace-assign create` will materialize `assign.source` sub-steps deterministically.

### 4. Render Hidden Spec (Paths B/C)
### 4. Render Hidden Spec (Paths B/C with Workable Input)

Precondition:
- Path B: prepare returned workable, filtered content (not an all-terminal abort)
- Path C: compose resolved at least one actionable step

Create hidden spec directory if missing:

Expand Down Expand Up @@ -133,6 +138,7 @@ steps:
Rules:
- Each invocation writes a new file (no in-place mutation of prior hidden specs).
- Hidden specs are internal provenance artifacts; users are not required to edit them.
- Do not render a hidden spec for all-terminal `work-on-task` requests that aborted in Path B.

### 5. Create Assignment Deterministically

Expand Down Expand Up @@ -170,6 +176,7 @@ Step 010: ...
|----------|--------|
| Unknown explicit phrase | Return unmatched phrase + closest catalog/skill candidates; no assignment created |
| Conflicting explicit order | Reorder only by hard rule and report the named rule that required it |
| Path B all requested refs already terminal | Return clear no-op result (`All requested tasks are already terminal (done/skipped/cancelled): ...`, `No assignment created.`); skip hidden-spec render and `ace-assign create` |
| Hidden-spec render failure | Return concrete render error; no assignment created |
| `ace-assign create` rejection | Surface CLI error unchanged |
| `--run` requested but no workable step | Keep create success; report why drive did not continue |
Expand All @@ -179,6 +186,8 @@ Step 010: ...
- Re-running the same request creates a new hidden spec file.
- Explicit duplicate steps are normalized unless repetition is clearly requested.
- Explicit steps take precedence over recipe defaults when both are present.
- Path B mixed refs (terminal + non-terminal) continue with filtered non-terminal refs only.
- Path B all-terminal refs produce no assignment and no hidden-spec artifact.
- High-level skill-backed steps may expand into sub-steps at create runtime via `assign.source` metadata.
- `--run` is a workflow-level create-then-drive handoff, not natural-language parsing in `ace-assign create`.
- Quiet mode for `ace-assign create` suppresses non-essential output (including provenance line).
Expand All @@ -187,6 +196,8 @@ Step 010: ...

- Hidden spec is written under `.ace-local/assign/jobs/` for generated inputs
- `ace-assign create FILE` receives the rendered spec path
- Path B all-terminal requests do not render a hidden spec and do not call `ace-assign create`
- Path B mixed requests render/create from filtered non-terminal refs only
- Assignment metadata preserves hidden-spec provenance
- Explicit step requests map to expected steps with explainable ordering
- Capability skills remain excluded from assign composition
Expand All @@ -197,7 +208,7 @@ Step 010: ...

```bash
# Validate intent-resolution language exists in create workflow
rg -n "explicit|phrase|advisory|assign.source|--run" ace-assign/handbook/workflow-instructions/assign/create.wf.md
rg -n "explicit|phrase|advisory|assign.source|--run|already terminal|No assignment created" ace-assign/handbook/workflow-instructions/assign/create.wf.md

# Validate hidden-spec references remain present
rg -n "\.ace-local/assign/jobs|Created from hidden spec" ace-assign
Expand All @@ -212,4 +223,4 @@ After assignment creation:

```bash
/as-assign-drive <assignment-id>
```
```
61 changes: 61 additions & 0 deletions ace-assign/handbook/workflow-instructions/assign/prepare.wf.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,38 @@ From informal instructions:
- "tasks 148, 149, 150" β†’ `taskrefs: ["148", "149", "150"]`
- "PR 45" β†’ `pr_number: "45"`

### 3.1 Resolve Requested Taskrefs and Filter Terminal Tasks (`work-on-task` only)

Apply this step when preparing the `work-on-task` preset.

1. Resolve requested refs to concrete task refs first (before any filtering):
- `--taskref` single value
- comma-separated `--taskrefs`
- ranges like `148-152`
- patterns like `240.*`
2. For each resolved ref, run:

```bash
ace-task show <taskref>
```

3. Parse the reported status using `Ace::Task::Atoms::TaskValidationRules::TERMINAL_STATUSES` as the source of truth:
- If status is terminal (`done`, `skipped`, `cancelled`) β†’ add to `skipped_terminal_refs`
- Otherwise (`pending`, `draft`, `in-progress`, `blocked`) β†’ keep in `effective_taskrefs`
4. Preserve existing invalid-ref behavior:
- If a ref cannot be resolved/found, fail with the existing invalid task reference path.
5. Branch by result:
- Mixed set (`effective_taskrefs` non-empty, `skipped_terminal_refs` non-empty):
- Continue with `effective_taskrefs`
- Report skipped refs clearly (example: `Skipped terminal tasks (done/skipped/cancelled): 149`)
- All-terminal set (`effective_taskrefs` empty, `skipped_terminal_refs` non-empty):
- Stop before preset expansion and before job generation
- Report:
- `All requested tasks are already terminal (done/skipped/cancelled): <refs>`
- `No assignment created.`

`effective_taskrefs` is now the source of truth for downstream expansion, hidden spec rendering, and mark-tasks-done behavior.

### 4. Apply Customizations (if prose provided)

Parse modifications:
Expand All @@ -246,6 +278,8 @@ params = { "taskrefs" => ["148", "149", "150"], "review_preset" => "batch" }
steps = Ace::Assign::Atoms::PresetExpander.expand(preset, params)
```

For `work-on-task`, pass `effective_taskrefs` from step 3.1 (not the unfiltered requested list).

### 5.1 Resolve Skill `assign.source` Metadata (Deterministic Runtime Expansion)

After preset expansion, each step with a `skill:` field may declare assignment source metadata via the skill frontmatter:
Expand Down Expand Up @@ -373,6 +407,8 @@ steps:
| Unknown preset | List available presets, ask for selection |
| Missing required parameter | Prompt for value |
| Invalid task reference | Show expected formats |
| Mixed refs include terminal tasks | Skip terminal refs, continue with remaining refs, and report skipped refs |
| All requested refs are terminal | Stop before expansion/job generation and report: `All requested tasks are already terminal (done/skipped/cancelled): ...` + `No assignment created.` |
| Unresolved placeholders | Report which parameters need values |

## Examples
Expand Down Expand Up @@ -437,10 +473,35 @@ Expands range to tasks 148, 149, 150, 151, 152.

Expands pattern to match subtasks (requires resolution at prepare time).

### Example 7: Mixed Set with Terminal Tasks

```
/as-assign-prepare work-on-task --taskrefs 148,149,150
```

If `149` is terminal (for example `done`):
- continue with `148,150`
- report `Skipped terminal tasks (done/skipped/cancelled): 149`

### Example 8: All Requested Tasks Already Terminal

```
/as-assign-prepare work-on-task --taskrefs 148,149
```

If both are terminal:
- report `All requested tasks are already terminal (done/skipped/cancelled): 148,149`
- report `No assignment created.`
- do not generate a job.yaml

## Success Criteria

- [ ] Input correctly parsed (preset, parameters, or prose)
- [ ] Preset loaded and parameters injected
- [ ] Requested refs resolve to concrete task refs before any terminal-status filtering
- [ ] Terminal filtering uses `Ace::Task::Atoms::TaskValidationRules::TERMINAL_STATUSES`
- [ ] Mixed requested sets continue with non-terminal refs and report skipped terminal refs
- [ ] All-terminal requested sets stop before queue/job creation
- [ ] Expansion directives processed (if present)
- [ ] Hierarchical steps numbered correctly
- [ ] Loops expanded into separate steps
Expand Down
2 changes: 1 addition & 1 deletion ace-assign/lib/ace/assign/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

module Ace
module Assign
VERSION = "0.37.0"
VERSION = "0.38.2"
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

require_relative "../test_helper"

class WorkOnTaskTerminalFilterContractTest < AceAssignTestCase
def test_prepare_workflow_uses_shared_terminal_status_contract
section = markdown_section(
prepare_workflow,
"### 3.1 Resolve Requested Taskrefs and Filter Terminal Tasks (`work-on-task` only)"
)

assert_includes section, "Ace::Task::Atoms::TaskValidationRules::TERMINAL_STATUSES"
assert_includes section, "If status is terminal (`done`, `skipped`, `cancelled`) β†’ add to `skipped_terminal_refs`"
assert_includes section, "Mixed set (`effective_taskrefs` non-empty, `skipped_terminal_refs` non-empty):"
assert_includes section, "All-terminal set (`effective_taskrefs` empty, `skipped_terminal_refs` non-empty):"
assert_includes section, "All requested tasks are already terminal (done/skipped/cancelled): <refs>"
end

def test_prepare_examples_cover_mixed_and_all_terminal_paths
mixed_example = markdown_section(prepare_workflow, "### Example 7: Mixed Set with Terminal Tasks")
all_terminal_example = markdown_section(prepare_workflow, "### Example 8: All Requested Tasks Already Terminal")

assert_includes mixed_example, "Skipped terminal tasks (done/skipped/cancelled): 149"
assert_includes all_terminal_example, "All requested tasks are already terminal (done/skipped/cancelled): 148,149"
assert_includes all_terminal_example, "No assignment created."
end

def test_create_workflow_blocks_hidden_spec_and_create_for_all_terminal_requests
content = create_workflow

assert_includes content, "If prepare reports all requested refs are already terminal (`done/skipped/cancelled`), stop and return that no assignment was created"
assert_includes content, "Do not render a hidden spec for all-terminal `work-on-task` requests that aborted in Path B."
assert_includes content, "All requested tasks are already terminal (done/skipped/cancelled): ..."
assert_includes content, "skip hidden-spec render and `ace-assign create`"
refute_includes content, "already done"
refute_includes content, "all-done `work-on-task` requests"
end

def test_usage_docs_match_terminal_filter_contract
content = usage_docs

assert_includes content, "Terminal refs (`done`, `skipped`, `cancelled`) are skipped before queue expansion."
assert_includes content, "Mixed sets continue with remaining non-terminal refs and report skipped terminal refs."
assert_includes content, "All requested tasks are already terminal (done/skipped/cancelled): <refs>"
refute_includes content, "Refs with `status: done` are skipped before queue expansion."
end

private

def prepare_workflow
@prepare_workflow ||= File.read(File.expand_path("../../handbook/workflow-instructions/assign/prepare.wf.md", __dir__))
end

def create_workflow
@create_workflow ||= File.read(File.expand_path("../../handbook/workflow-instructions/assign/create.wf.md", __dir__))
end

def usage_docs
@usage_docs ||= File.read(File.expand_path("../../docs/usage.md", __dir__))
end

def markdown_section(content, heading)
start = content.index(heading)
refute_nil start, "Missing heading: #{heading}"

remainder = content[start..]
next_heading = remainder.index("\n### ", heading.length)
next_heading ? remainder[0...next_heading] : remainder
end
end