fix(skills): two more skills that contradicted the platform
Same class as the `/workspace/repo` path and the ZeroClaw tool names: the skills were written alongside the platform and never compared to it again. Both found by reading the source of truth before writing a check against it. 1. `decompose-int-items` showed `PLAN_COMPLETE: INT-01..05`. An id is strictly `INT-<digits>`, so the range form is rejected outright — the plan pass records nothing while every item stays open. A live planner emitted exactly that line. Now one id per line. 2. `workspace-repo-commit-protocol` said the task-card parser advances mission state on the INT id in the commit subject. Nothing in the platform reads commit messages; the parser reads `run_events` — the agent's turn output. An agent that believed it could commit with the id and never emit `COMPLETED: INT-NN`, leaving the mission open on an item it had finished. The convention is kept, the mechanism corrected. `no_skill_shows_a_marker_the_parser_would_reject` runs the real parser over every marker in every skill's fenced blocks, negative-controlled against the range form. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018i9Ten1LU4jUr5d7TAWda9
This commit is contained in:
co-authored by
Claude Opus 5
parent
1a6fdfc0e6
commit
d0b657a24b
@@ -273,4 +273,73 @@ mod contradiction_tests {
|
|||||||
offenders.join("\n ")
|
offenders.join("\n ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// No skill may show a marker the real parser rejects.
|
||||||
|
///
|
||||||
|
/// Checked by running `task_card_parser::parse` itself, never a copy of its
|
||||||
|
/// rules — a second implementation of the contract drifts, and then the
|
||||||
|
/// test passes while the mission loop stalls.
|
||||||
|
///
|
||||||
|
/// This is the third instance of one class: the skills were written
|
||||||
|
/// alongside the platform and then never compared to it again. The first
|
||||||
|
/// was a repo path the platform does not mount; the second a tool the agent
|
||||||
|
/// does not have; this one is `PLAN_COMPLETE: INT-01..05` in
|
||||||
|
/// `decompose-int-items`, which a live planner emitted verbatim. Ids are
|
||||||
|
/// strictly `INT-<digits>`, so the range form parses to nothing — the plan
|
||||||
|
/// pass records no completion at all while every item stays open.
|
||||||
|
///
|
||||||
|
/// Scoped to fenced code blocks, which is where a skill puts the text it
|
||||||
|
/// tells an agent to EMIT. A marker named in a sentence is prose.
|
||||||
|
#[test]
|
||||||
|
fn no_skill_shows_a_marker_the_parser_would_reject() {
|
||||||
|
// The templates. `INT-NN` is a placeholder an agent substitutes, not a
|
||||||
|
// literal it emits, so it is not a contradiction.
|
||||||
|
const PLACEHOLDERS: &[&str] = &["INT-NN", "INT-XX", "INT-N", "INT-nn"];
|
||||||
|
let mut offenders = Vec::new();
|
||||||
|
for (name, body) in all_skills() {
|
||||||
|
let mut fenced = false;
|
||||||
|
for line in body.lines() {
|
||||||
|
if line.trim_start().starts_with("```") {
|
||||||
|
fenced = !fenced;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let t = line.trim();
|
||||||
|
if !fenced || !t.contains("INT-") || !t.contains(':') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some((kind, _)) = t.split_once(':') else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !MARKER_KINDS.contains(&kind.trim()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if PLACEHOLDERS.iter().any(|p| t.contains(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if crate::task_card_parser::parse(t).is_empty() {
|
||||||
|
offenders.push(format!("{name}: {t}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
offenders.is_empty(),
|
||||||
|
"{} skill line(s) show a marker the parser rejects — an agent that \
|
||||||
|
follows them exactly is silently ignored:\n {}",
|
||||||
|
offenders.len(),
|
||||||
|
offenders.join("\n ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The marker kinds, as the parser spells them.
|
||||||
|
const MARKER_KINDS: &[&str] = &[
|
||||||
|
"TASK",
|
||||||
|
"PLAN_COMPLETE",
|
||||||
|
"WORK",
|
||||||
|
"HANDOFF",
|
||||||
|
"TEST_PASS",
|
||||||
|
"TEST_FAIL",
|
||||||
|
"REVIEW_APPROVE",
|
||||||
|
"REVIEW_BLOCK",
|
||||||
|
"COMPLETED",
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,13 @@ At the end of the planning turn, emit:
|
|||||||
TASK: INT-01 — <title>
|
TASK: INT-01 — <title>
|
||||||
TASK: INT-02 — <title>
|
TASK: INT-02 — <title>
|
||||||
...
|
...
|
||||||
PLAN_COMPLETE: INT-01..05
|
PLAN_COMPLETE: INT-01
|
||||||
|
PLAN_COMPLETE: INT-02
|
||||||
```
|
```
|
||||||
|
|
||||||
The parser creates `mission_tasks` rows for each TASK line. `PLAN_COMPLETE` records that the plan pass finished so the mission's coding phase can begin iterating.
|
The parser creates `mission_tasks` rows for each TASK line. `PLAN_COMPLETE` records that the plan pass finished so the mission's coding phase can begin iterating.
|
||||||
|
|
||||||
|
**One id per line — never a range.** This section used to show
|
||||||
|
`PLAN_COMPLETE: INT-01..05`, and a live planner emitted exactly that. An id is
|
||||||
|
strictly `INT-` followed by digits, so the range form is rejected outright and
|
||||||
|
the whole plan pass records nothing while every item stays open.
|
||||||
|
|||||||
@@ -48,8 +48,12 @@ Refs: INT-NN
|
|||||||
"
|
"
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Put the INT-XX marker on the subject line.** The task-card parser advances
|
- **Put the INT-XX id on the subject line.** This is for the humans and for
|
||||||
mission state on it.
|
`git log --oneline` — nothing in the platform reads your commit messages.
|
||||||
|
Mission state advances on the marker you emit **in your turn output**
|
||||||
|
(`COMPLETED: INT-NN`, below), which is the only text the task-card parser
|
||||||
|
reads. Committing with the id and never emitting the marker leaves the
|
||||||
|
mission open on an item you have already finished.
|
||||||
- **One INT per commit** unless the change genuinely cannot be split. Split when
|
- **One INT per commit** unless the change genuinely cannot be split. Split when
|
||||||
in doubt: a commit covering three items cannot be reverted for one of them.
|
in doubt: a commit covering three items cannot be reverted for one of them.
|
||||||
- **Never `--force`, never rewrite pushed history** without an explicit
|
- **Never `--force`, never rewrite pushed history** without an explicit
|
||||||
|
|||||||
Reference in New Issue
Block a user