Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ flowchart LR

| 단계 | 하는 일 | 되돌아가는 때 |
|---|---|---|
| **1 Intent** | 요청한 것, 에이전트가 이해한 것, 그 이해에 숨은 위험을 적고, 여섯 영역으로 명확도를 매깁니다. 90%에 못 미치면 에이전트가 코드를 살피고, 버그를 재현하고, 스펙을 조사하거나, 티켓·이슈·PR에서 요청자에게 묻습니다. 사람이 확인합니다. | — |
| **1 Intent** | 요청한 것, 에이전트가 이해한 것, 그 이해에 숨은 위험을 적고, 여섯 영역으로 명확도를 매깁니다. 90%에 못 미치면 에이전트가 코드를 살피고, 버그를 재현하고, 스펙을 조사하거나, 티켓·이슈·PR에서 요청자에게 묻습니다. 맨 위 세 줄 요약으로 시작하고 에이전트용 근거는 맨 아래에 둡니다. 사람은 "이게 원하는 것인가?"에 답해 승인합니다. | — |
| **2 Plan** | 여러 scout가 나란히 정본·데이터·로직·사용자 흐름을 조사해 `refs/`에 남깁니다. `PLAN.md` 한 장에 AS-IS → TO-BE, 부작용, QA가 돌릴 시나리오를 담습니다. | 누락 점검에서 빈틈이 나올 때 |
| **3 Build** | 가장 작은 완전한 변경을 만들어 커밋하고, 별도 리뷰어가 공격적으로 검토합니다. | 리뷰 FAIL: 고치고 다시 리뷰 |
| **4 Evidence** | 실제 검사 뒤, 모든 시나리오를 각 페르소나로 실제 인터페이스에서 돌립니다. 점수와 다음 경로는 에이전트가 아니라 `sdlc.sh score`가 정합니다. | 계획이 틀렸거나 같은 실패 반복 → Plan, 코드 실패 → Build |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ flowchart LR

| Phase | What happens | Goes back when |
|---|---|---|
| **1 Intent** | What you asked, what the agent understood, and the risks in it, scored for clarity across six areas. Below 90% the agent explores, reproduces the bug, researches the spec or asks the requester on the ticket, issue or pull request. You confirm it. | — |
| **1 Intent** | What you asked, what the agent understood, and the risks in it, scored for clarity across six areas. Below 90% the agent explores, reproduces the bug, researches the spec or asks the requester on the ticket, issue or pull request. It opens with a three-line bottom line and keeps the agent's evidence at the bottom; you approve it by answering "is this what you want?". | — |
| **2 Plan** | Parallel scouts map source of truth, data, logic and user flow into `refs/`; one `PLAN.md` holds AS-IS → TO-BE, side effects and the scenarios QA will run. | the gap check finds holes |
| **3 Build** | The smallest complete change, committed, then a separate adversarial reviewer. | the review FAILs: fix it, review again |
| **4 Evidence** | Real checks, then every scenario as its persona through the real interface. `sdlc.sh score` scores and routes it; the agent does not judge. | the plan was wrong or the same failure came twice → Plan; code failed → Build |
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.9.0
2.10.0
42 changes: 42 additions & 0 deletions log/changelog-v2.10.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# v2.10.0 — Intent reads top-down for a human

intent.md now opens with a three-line Bottom line and keeps the agent's
working record at the bottom. An answer to a blocking question no longer
counts as approval of the whole intent.

## Why

- **The human could not find the decision.** Long Clarity cells (one AS-IS
cell held ten facts with file:line references) buried what needed the
human. Risks mixed approval-level warnings with evidence limits.
- **A question's answer was recorded as approval.** A requester answered one
blocking question; the agent passed those words to
`approve intent --human` and went on to plan without asking "is this what
you want?".

## Changes

- **`## Bottom line`** at the top: Changes (now the outcome; Understood as
drops its Outcome line), Left out and Needs you, which cite the Out of
scope item or risk instead of restating it.
- **One fact, one place inside intent.md:** other sections cite O2, F3, Q1
or Request instead of restating.
- **Order:** Bottom line, Request, Understood as, Risks, Delivery, Blocking
questions, then `---` and the agent's record: Terms, Clarity, Facts.
- **`## Facts`:** Clarity cells are one sentence citing `F1`-style IDs; the
proof sits one fact per line in Facts.
- **Writing rule** in phase 1: one idea per line, bold lead-in, exact
numbers, warning on the line it guards, the requester's language with
headings, field labels and Clarity words in English.
- **Gate:** show Bottom line, Risks and any assumed Clarity area, ask once,
end the turn. `approve intent --human` refuses a note of 12 bytes or more
found inside a resolved blocking-question line, compared as the parser
reads it (comments dropped, `*` and backticks stripped, case folded).
Limit: a generic phrase that is also part of an answer ("yes, go ahead")
is refused too; record the human's fuller words.

## Compatibility

Sections are still read by heading, in any order; the parser only adds
resolved answers for the Gate check. Older
intent.md files still pass; Bottom line and Facts are optional to the script.
39 changes: 20 additions & 19 deletions phases/1-intent.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ plans how to build it.
(`abc-123-fix-login`, `261012-fix-login`).
2. Read the request and its source (ticket, issue, pull request, document)
with its comments and links, and pages in the `memory` folder with the same words.
3. Fill `intent.md` (`templates/intent.md`):
- **Request** — the human's words, verbatim or a faithful excerpt.
- **Understood as** — the outcome, what is in and out of scope, and
3. Fill `intent.md` (`templates/intent.md`) for a skimming reader: one idea
per line, a bold lead-in so the bold alone carries the gist, exact
numbers, each warning on the line it guards; proof goes to Facts. Each
fact appears once; elsewhere cite it (O2, F3, Q1, Request). Human
sections come first, the agent's record (Terms, Clarity, Facts) last.
Write in the requester's language; headings, field labels and Clarity words stay English.
- **Understood as** — scope (Bottom line Changes is the outcome) and
success criteria `O1..On`, each observable so a scenario can check it.
- **Terms** — match each term the request turns on to `glossary.md` in
the `memory` folder, else the code. A ❓ term, read two ways,
leaves its Clarity area open and is a blocking question; the question
shows each reading with a real value.
- **Clarity** — one row per area: Outcome, Scope, AS-IS, TO-BE, Success,
Constraints. State `clear` only when How settled cites evidence that
the `memory` folder, else the code. A ❓ term leaves its Clarity area
open and is a blocking question showing each reading with a real value.
- **Clarity** — state `clear` only when How settled cites evidence that
leaves one reading: the requester's words, a file:line, a log, a
primary source. Otherwise `assumed` (and why) or `open` (and what is
missing).
- **Facts** — that evidence, one fact per line (`F1:`) with its source.
- **Risks in this intent** — ambiguity, a claim the code or data already
contradicts (with the evidence), a policy the request would change and
who owns it, work that needs authority (rule 3). Before logs, error
Expand All @@ -32,11 +35,8 @@ plans how to build it.
a time; record each answer in place as `— resolved: <answer, source>`
and cite it in its Clarity area. Other uncertainty is written as
`[assumed: why]`.
- **Delivery** — `local`, `pr` or `deploy` alone on the Target line
(`pr` also covers pushing a branch for review with no pull request),
details such as branch or environment on a `Note:` line, and
`Authorized: yes — "<the human's words>"` when they authorized it, else
`no`.
- **Delivery** — `pr` also covers a branch pushed for review; record
`Authorized: yes` only with the human's words.
4. Several independent outcomes → one slug each; list them under Understood
as.
5. A bug report also records what was done, what happened against what was
Expand All @@ -50,7 +50,7 @@ cheapest way that works, in this order:

1. **Explore** — the code path, screen or command the request names, and
earlier tickets or commits on it. Look only as far as what is wanted; how
to change it safely (every caller, data states, side effects) is phase 2.
to change it safely is phase 2.
A question needing more than a few reads goes to a scout
(`roles/scout.md`); phase 2 reuses its `refs/` file.
2. **Reproduce** — drive a reported bug where that is safe: locally or in a
Expand All @@ -77,10 +77,11 @@ cheapest way that works, in this order:
Once clarity is 90% and every blocking question is resolved, dispatch the
reviewer in intent mode (`roles/reviewer.md`); skip it only when every area
cites the requester's own words. An area it shows unsettled goes back to
Raise clarity. Then — human gate (the default): show the human Understood
as, Clarity and Risks, and ask one question — is this what you want? On
explicit approval run
`sdlc.sh approve intent <slug> --human "<their words>"`.
Otherwise check intent.md against the request yourself, run
Raise clarity. Then — human gate (the default): show the human Bottom line,
Risks and any assumed Clarity area, ask one question — is this what you
want? — and end the turn. Only a reply to that question approves:
`sdlc.sh approve intent <slug> --human "<their words>"`. An answer to a
blocking question is not approval; ask again after it.
Without an intent gate, check intent.md against the request yourself, run
`sdlc.sh approve intent <slug> --agent "<what you checked>"`, and post it as
FYI. Then phase 2.
8 changes: 4 additions & 4 deletions sdlc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ parse() {
awk -v mode="$1" -v objectives="$objectives" -v scenarios="$scenarios" -v scored="$scored" -v withdrawn="$withdrawn" "$UNCOMMENT"'
function trim(s){sub(/^[ \t]+/,"",s);sub(/[ \t]+$/,"",s);return s}function problem(s){print "X\034" s}
function area(id, k,s){k=tolower(id);gsub(/[^a-z]/,"",k);s=tolower(a[3]);if(k=="area")return;ca++;if(k!~/^(outcome|scope|asis|tobe|success|constraints)$/){print "Y\034Clarity: unknown area " id;cy++;return}if(k in cs){print "Y\034Clarity: duplicate area " id;cy++;return}cs[k]="open";if(s!~/^(clear|assumed|open)$/){print "Y\034Clarity " id ": State must be clear, assumed or open";cy++}else if(tolower(a[4])~/^(<.*>|[-—–?.]*|n\/a|tbd)$/){print "Y\034Clarity " id ": How settled needs its evidence or reason";cy++}else cs[k]=s}
function question(){if(q!=""){l=tolower(trim(q));if(l!="none"&&l!="n/a"&&l!~/^resolved:/&&l!~/— resolved:/&&l!~/-- resolved:/)print "Q\034" q;q=""}}
function question(){if(q!=""){l=tolower(trim(q));if(l!="none"&&l!="n/a"&&l!~/^resolved:/&&l!~/— resolved:/&&l!~/-- resolved:/)print "Q\034" q;else if(l~/resolved:/)print "V\034" l;q=""}}
function fields(s, c,j){for(j in a)delete a[j];nfields=1;for(j=1;j<=length(s);j++){c=substr(s,j,1);if(c=="\\"&&substr(s,j+1,1)=="|"){a[nfields]=a[nfields] "|";j++}else if(c=="|")nfields++;else a[nfields]=a[nfields] c}for(j=1;j<=nfields;j++)a[j]=trim(a[j]);cells=nfields-1-(a[nfields]=="");return a[2]}
{
if(uncomment())next
Expand Down Expand Up @@ -137,9 +137,9 @@ parse() {
}' "$2"
}
read_intent() {
objectives=; iproblems=; questions=; itarget=; iauth=; cproblems=
objectives=; iproblems=; questions=; itarget=; iauth=; cproblems=; answers=
while IFS=$'\034' read -r type a b; do
case "$type" in O) objectives="$objectives $a";; X) iproblems="${iproblems}${a}"$'\n';; Q) questions="${questions}${a}"$'\n';; Y) cproblems="${cproblems}${a}"$'\n';; T) itarget=$a;; A) iauth=$a;; esac
case "$type" in O) objectives="$objectives $a";; X) iproblems="${iproblems}${a}"$'\n';; Q) questions="${questions}${a}"$'\n';; Y) cproblems="${cproblems}${a}"$'\n';; V) answers="${answers}${a}"$'\n';; T) itarget=$a;; A) iauth=$a;; esac
done < <(parse intent "$F/intent.md")
}
read_plan() {
Expand Down Expand Up @@ -481,7 +481,7 @@ case "$command" in
if [ "$which" = plan ] && [ "$who" = agent ] && stopped; then die "stopped for the human: $stopreason; resume with approve plan --human"; fi
read_intent; problems "$iproblems" || exit 1; problems "$questions" || exit 1
if [ "$which" = intent ]; then
if [ "$who" = agent ] && human_gate intent; then die 'intent needs --human approval'; fi
if [ "$who" = agent ] && human_gate intent; then die 'intent needs --human approval'; fi; if [ "$who" = human ] && lnote=$(printf '%s' "${note//[\`*]/}" | tr '[:upper:]' '[:lower:]') && [ "$(printf '%s' "$lnote" | LC_ALL=C wc -c)" -ge 12 ] && [[ $answers == *"$lnote"* ]]; then die 'that note answers a blocking question; it does not approve this intent: show the human Bottom line, Risks and any assumed Clarity area, ask "is this what you want?" and record their reply'; fi
hash=$(sha < "$F/intent.md"); if [ "$ish" = "$hash" ]; then printf 'intent already approved\n'; exit; fi; problems "$cproblems" || exit 1
event; intent="$hash $who $seq $ts $note"
else
Expand Down
48 changes: 29 additions & 19 deletions templates/intent.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,49 @@
- Requested by: <who>
- Source: <ticket, issue or document link | chat>

## Bottom line
- **Changes:** <the outcome: who can do what once done>
- **Left out:** <largest Out of scope item, cited — or none>
- **Needs you:** <the ⚠️ risk or decision, cited — or none>

## Request
<the human's words, verbatim or a faithful excerpt>
- <the human's words, verbatim or a faithful excerpt>

## Understood as
- Outcome: <one plain sentence: who can do what once this is done>
- In scope: <…>
- Out of scope: <…>
- O1: <observable success criterion>
- Out of scope: <each with why>
- O1: <**short name** — observable success criterion>

## Risks in this intent
<!-- Most consequential first; ⚠️ = needs the human's authority. Resolved risks leave. -->
- <risk — evidence — or none>

## Delivery
- Target: <local | pr | deploy>
- Note: <optional: branch, environment or other delivery detail>
- Authorized: <yes — "the human's words" | no>

## Blocking questions
- none
<!-- blocks approval until the line ends with "— resolved: <answer, source>"; while pending, add "— asked: <where, when>" -->

---

## Terms
<!-- Each term the request turns on, or none: ✅ one meaning, ⚠️ assumed, ❓ two readings (each with a real value; its Clarity area stays open). -->
- ✅ <term> = <plain meaning>, e.g. <real value>
<!-- Each term the request turns on, or none: ✅ one meaning, ⚠️ assumed, ❓ two readings, each with a real value. -->
- ✅ **<term>** = <plain meaning>, e.g. <real value>
- Source: <glossary line | requester's words | file:line> · Code: <identifier>

## Clarity
<!-- State: clear, assumed or open. sdlc.sh scores clear 1, assumed 1/2, open 0 and refuses approval below 90%: one assumed area passes; two, or one open, do not. How settled: the evidence that leaves one reading, why it is assumed, or what is missing. -->
<!-- State: clear, assumed or open; under 90% blocks approval. How settled: one sentence and its Facts IDs, why it is assumed, or what is missing. -->
| Area | State | How settled |
|---|---|---|
| Outcome | <state> | <who gets what, and why> |
| Outcome | <state> | <who gets what, and why — F1> |
| Scope | <state> | <what is in and out, and who drew the line> |
| AS-IS | <state> | <today's behavior as observed; a bug as reproduced> |
| TO-BE | <state> | <the exact behavior: rules, values, messages, edge cases> |
| Success | <state> | <how each O is observed> |
| Constraints | <state> | <policies, compatibility, data, owners — or none found, where you looked> |

## Risks in this intent
- <an ambiguity, a claim the code or data contradicts (with evidence), a policy the request would change and its owner, work that needs the human's authority — or none>

## Blocking questions
- none
<!-- a question here blocks approval until its line ends with "— resolved: <answer, source>"; while the requester has it, add "— asked: <where, when>" -->

## Delivery
- Target: <local | pr | deploy>
- Note: <optional: branch, environment or anything else about the delivery>
- Authorized: <yes — "the human's words authorizing push, merge or deploy" | no>
## Facts
- F1: **<lead-in>** <fact> — <source>
11 changes: 11 additions & 0 deletions tests/selftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,17 @@ printf 'human_gates: # deliberately empty\n' > .sdlc/config.md
feature ungated
expect 0 approve intent ungated --agent reviewed
ok 'present empty human_gates allows agent approval'
printf 'human_gates: intent\n' > .sdlc/config.md
feature answered
printf '## Blocking questions\n- Q1. scope? — resolved: "TC-25 later, the rest go ahead" (requester, chat)\n' >> "$F/intent.md"
expect 1 approve intent answered --human 'TC-25 later, the rest go ahead'; contains 'answers a blocking question'
expect 0 approve intent answered --human 'yes, this is what I want'
feature answered2
printf '## Blocking questions\n- Q1. scope? -- Resolved: "**나머진** 진행" (requester)\n<!-- the line ends with "— resolved: <answer, source>" -->\n' >> "$F/intent.md"
expect 1 approve intent answered2 --human '나머진 진행'; contains 'answers a blocking question'
expect 0 approve intent answered2 --human 'the line ends with'
ok 'an answer to a blocking question is not --human approval of the intent'
printf 'human_gates: # deliberately empty\n' > .sdlc/config.md
mkdir -p nested/deep
cd nested/deep
expect 0 status ungated; contains 'ungated'
Expand Down
Loading