diff --git a/README.ko.md b/README.ko.md index bba292b..b19ae59 100644 --- a/README.ko.md +++ b/README.ko.md @@ -85,7 +85,7 @@ $EDITOR .sdlc/config.md # build, test, lint, run, e2e, logs/ 모든 PASS·FAIL 뒤의 원본 출력, scripts/는 시나리오별 재실행 스크립트 ``` -끝난 기능은 `.sdlc/archive/`로 옮겨집니다. 검증된 사실은 `knowledge` 폴더로 +끝난 기능은 `.sdlc/archive/`로 옮겨집니다. 검증된 사실은 `memory` 폴더로 옮기고, 다음 기능의 scout가 그 폴더부터 읽습니다. ## 설정 @@ -94,9 +94,9 @@ $EDITOR .sdlc/config.md # build, test, lint, run, e2e, `lint`, `run`, `e2e` 명령, 화면이나 API를 다룰 `qa` 도구, 선택 실행기 어댑터 `qa_runner`와 그 로그인 명령 `qa_session`, `human_gates`(기본값 `intent`, 모든 plan을 직접 승인하려면 `plan` 추가, 위험 플래그가 있는 plan은 -설정과 상관없이 사람이 승인), `knowledge` 폴더, `repos`(리뷰가 묶을 저장소. +설정과 상관없이 사람이 승인), `memory` 폴더, `repos`(리뷰가 묶을 저장소. 프로젝트 루트 아래 저장소가 많으면 나열하거나 리뷰마다 `sdlc.sh review pass --repos "<경로> ..." "<메모>"`로 지정하고, 비우면 루트 아래 전부)입니다. `.sdlc/`는 -`.git/info/exclude`로 git에서 빠집니다. 팀이 배운 것을 공유하려면 `knowledge:`를 +`.git/info/exclude`로 git에서 빠집니다. 팀이 배운 것을 공유하려면 `memory:`를 git이 추적하는 폴더로 지정하세요. `adapters/e2e/`가 그런 실행기 하나입니다. `qa_runner: e2e`이면 QA는 브라우저·모바일 @@ -138,7 +138,8 @@ v2는 새 계약입니다. 기록은 있던 자리에 그대로 남습니다. - `lazymode`는 계속 읽힙니다. 0–1 → `human_gates: intent plan`, 2–3 → `intent`, 4 → 없음. 직접 정하려면 `human_gates:`를 쓰세요. `intent`, `plan`이 아닌 값은 둘 다로 처리합니다. -- `knowledge:`를 정하기 전까지 `.sdlc/memory/`를 knowledge 폴더로 읽습니다. +- v0처럼 `.sdlc/memory/`가 기본 memory 폴더입니다. 예전 `knowledge:` 줄도 폴더를 + 지정하고, 지정한 폴더가 아닌 기본 폴더가 있으면 `sdlc.sh`가 알립니다. - `init.sh` → `sdlc.sh init`, `gates/status.sh --json`과 `tools/auto.sh next` → `sdlc.sh status --json`과 `sdlc.sh next`(종료 코드 동일). spec.md, summary.md, delivery.md, qa-guide.md, map.md, harvest, 검증 영수증, handoff, tripwire, diff --git a/README.md b/README.md index 248ab73..3d64c43 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ always says what comes next. ``` Closed features move to `.sdlc/archive/`. Verified facts move to the -`knowledge` folder, which the next feature's scouts read first. +`memory` folder, which the next feature's scouts read first. ## Config @@ -95,11 +95,11 @@ Closed features move to `.sdlc/archive/`. Verified facts move to the `lint`, `run` and `e2e` commands, the `qa` tool that drives screens or APIs, an optional `qa_runner` adapter with its `qa_session` sign-in command, `human_gates` (default `intent`; add `plan` to approve every plan yourself — a -plan with a risk flag always asks), the `knowledge` folder, and `repos` (the +plan with a risk flag always asks), the `memory` folder, and `repos` (the repositories a review binds — list them when the project root holds many, or name them per review with `sdlc.sh review pass --repos " ..." ""`; empty means every one under it). `.sdlc/` stays -out of git through `.git/info/exclude`; point `knowledge:` at a tracked folder +out of git through `.git/info/exclude`; point `memory:` at a tracked folder to share what the team learns. `adapters/e2e/` is one such runner: with `qa_runner: e2e`, QA writes each @@ -144,7 +144,8 @@ v2 is a new contract. Your records stay where they are. - `lazymode` keeps working: 0–1 → `human_gates: intent plan`, 2–3 → `intent`, 4 → none. Write `human_gates:` to choose explicitly; a word other than `intent` or `plan` means both. -- `.sdlc/memory/` is read as the knowledge folder until you set `knowledge:`. +- `.sdlc/memory/` is the default memory folder, as in v0; a `knowledge:` line still + names the folder, and `sdlc.sh` reports a default folder that is not it. - `init.sh` → `sdlc.sh init`; `gates/status.sh --json` and `tools/auto.sh next` → `sdlc.sh status --json` and `sdlc.sh next` (same exit codes). spec.md, summary.md, delivery.md, qa-guide.md, map.md, harvest, verification diff --git a/SKILL.md b/SKILL.md index 24d9b13..c60b344 100644 --- a/SKILL.md +++ b/SKILL.md @@ -23,7 +23,7 @@ finds `.git`. ## Route a request - A change or fix, and `.sdlc/` exists → `sdlc.sh new `, then phase 1. -- No `.sdlc/` yet → `sdlc.sh init` (`--knowledge ` keeps knowledge +- No `.sdlc/` yet → `sdlc.sh init` (`--memory ` keeps memory elsewhere); fill `.sdlc/config.md` from the repo (scripts, Makefile, CI) and run each command once; ask only what the repo cannot show. Then phase 1. @@ -78,15 +78,15 @@ finds `.git`. `e2e`, `qa` (the tool that drives screens or APIs), `qa_runner` (an optional adapter in `adapters/`, such as `e2e`) and `qa_session` (its sign-in command), `human_gates` (default -`intent`), `knowledge` (the durable knowledge folder) and `repos` (the +`intent`), `memory` (older configs: `knowledge:`) and `repos` (the repositories a review binds; empty means every one under the project root; `review --repos` names them for one review). Records are working knowledge, not source: `init` keeps `.sdlc/` out of git through `.git/info/exclude`, changing no tracked file. Closed features move -to `.sdlc/archive//`. Scouts read `knowledge` first; Evidence adds what -was verified. Empty `knowledge:` is `.sdlc/memory` if present, else -`.sdlc/knowledge`, both local; name a tracked folder to share. +to `.sdlc/archive//`. Scouts read `memory` first; Evidence adds what +was verified. The default `.sdlc/memory` stays on this machine; point +`memory:` at a tracked folder to share it. ## Automation diff --git a/VERSION b/VERSION index 860487c..834f262 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.1 +2.8.0 diff --git a/docs/index.html b/docs/index.html index ee94410..ced6fbe 100644 --- a/docs/index.html +++ b/docs/index.html @@ -379,7 +379,7 @@

For the person who sets it up

  • $EDITOR .sdlc/config.md -

    The only project-specific file: the build, test, lint, run and e2e commands, the qa tool, human_gates (default intent; add plan to approve every plan) and the knowledge folder.

    +

    The only project-specific file: the build, test, lint, run and e2e commands, the qa tool, human_gates (default intent; add plan to approve every plan) and the memory folder.

  • @@ -470,7 +470,7 @@

    For the person who sets it up

    "inst-lede": '여기부터는 명령어를 씁니다. 세 단계면 프로젝트에 설치되며, 자세한 내용은 GitHub의 README에 있습니다. 그다음에는 평소 말로 변경을 요청하면 됩니다.', "st1-n": '컴퓨터마다 한 번. 두 번째 줄은 Claude Code 기준이며, 쓰는 도구의 스킬 폴더에 연결하세요. 스킬 폴더가 없으면 AGENTS.md, CLAUDE.md, GEMINI.md 중 해당 파일에 For SDLC work, read ~/sdlc-kit/SKILL.md and follow it.을 넣으세요.', "st2-n": '프로젝트마다, 또는 모노레포의 배포 단위마다 한 번. .sdlc/ 폴더를 만들며 git은 이 폴더를 무시합니다. Windows에서는 sdlc.sh를 Git Bash나 WSL에서 실행하세요.', - "st3-n": '프로젝트마다 다른 값은 이 파일 한 곳에만 둡니다. build, test, lint, run, e2e 명령, qa 도구, human_gates(기본값 intent, 모든 계획을 직접 승인하려면 plan 추가), knowledge 폴더입니다.', + "st3-n": '프로젝트마다 다른 값은 이 파일 한 곳에만 둡니다. build, test, lint, run, e2e 명령, qa 도구, human_gates(기본값 intent, 모든 계획을 직접 승인하려면 plan 추가), memory 폴더입니다.', "foot-p": 'sdlc-kit은 Anthropic이 공개한 안내서를 바탕으로 만든 독립 프로젝트로, Anthropic과는 관계가 없습니다.', "foot-gh": 'GitHub에서 보기', "foot-pb": 'Anthropic의 안내서' diff --git a/log/changelog-v2.7.1.md b/log/changelog-v2.7.1.md deleted file mode 100644 index 3a79229..0000000 --- a/log/changelog-v2.7.1.md +++ /dev/null @@ -1,21 +0,0 @@ -# v2.7.1 — an upgraded store keeps its knowledge - -Two small fixes found while applying v2.7.0. Template and prompt only; no -script change. - -## Changes - -- **The template leaves `knowledge:` empty.** It used to say - `knowledge: .sdlc/knowledge`, and `sdlc.sh` falls back to `.sdlc/memory` - only when the value is empty. So a v0 store whose config.md was rewritten - from the template stopped reading its knowledge. In one store this hid 36 - knowledge pages from scouts for a week, and Evidence wrote six new pages - next to them instead of updating them. The template now leaves the value - empty and states the default in its comment, and SKILL.md states the - same default instead of naming only `.sdlc/knowledge`. `init` still creates - `.sdlc/knowledge` for a new store, and `--knowledge` still writes the path - given. A store that already names a folder keeps it, so check that it is - the folder holding your knowledge. -- **A bug fix keeps its failing run.** Build's bug-fix rule now keeps the - regression test's failing output in `logs/`, where v2.7.0's reviewer - looks for it. A blocking review finding fixed as a bug fix inherits this. diff --git a/log/changelog-v2.8.0.md b/log/changelog-v2.8.0.md new file mode 100644 index 0000000..dbcf64f --- /dev/null +++ b/log/changelog-v2.8.0.md @@ -0,0 +1,52 @@ +# v2.8.0 — one memory folder + +The durable folder scouts read first and Evidence writes to has one name +again: `memory`, the folder v0 stores already have. A default folder that is +not the configured one is now reported instead of silently ignored. Also, a +bug fix keeps its failing run in `logs/`. (v2.7.1 went to `dev` only and is +folded in here.) + +## Why + +- **Two names hid a store's knowledge.** v2 called the folder `knowledge` + and defaulted to `.sdlc/knowledge`, reading `.sdlc/memory` only when + config.md had no value. One v0 store had its config.md rewritten from the + template, which wrote `knowledge: .sdlc/knowledge`. Its 36 pages in + `.sdlc/memory` stopped reaching scouts for a week, nothing said so, and + Evidence wrote six new pages into `.sdlc/knowledge` next to them instead of + updating them. +- Run on that store's layout, v2.7.0 prints nothing; this release prints + `sdlc: .sdlc/memory is not the memory folder (.sdlc/knowledge): move its + pages there or name it in config.md memory:`. + +## Changes + +- **`memory` everywhere.** SKILL.md, the phases, the scout role, the + template, the READMEs and the site say `memory`. config.md's key is + `memory:`, its template value `.sdlc/memory`, and `init --memory ` + replaces `init --knowledge `. An empty or missing value means + `.sdlc/memory` for every store, new or from v0. +- **Older configs keep working.** A `knowledge:` line is still read when + there is no `memory:` line, and SKILL.md tells agents so, so a store that + names `.sdlc/knowledge` keeps using it. +- **A stray default folder is reported.** On every command, `sdlc.sh` prints + one line for `.sdlc/memory` or `.sdlc/knowledge` when it exists and is not + the configured folder. The selftest drives this (126 checks): init's + default, an older `knowledge:` line, and a stray folder of either name. +- **A bug fix keeps its failing run.** Build's bug-fix rule keeps the + regression test's failing output in `logs/`, where the v2.7.0 reviewer + looks for it. A blocking review finding fixed as a bug fix inherits this. + +## Compatibility + +- `init --knowledge` is refused with the usage line; use `--memory`. +- A store with no memory line and a `.sdlc/knowledge` folder now uses + `.sdlc/memory` and is told about `.sdlc/knowledge` on every command: move + its pages to `.sdlc/memory`, or add `memory: .sdlc/knowledge`. +- To move a store that names `.sdlc/knowledge` to the one name: move the + folder's pages to `.sdlc/memory` and change the line to + `memory: .sdlc/memory`. + +## Known limits + +- The word budget is unchanged: the plan load stays at 2500 of 2500. diff --git a/phases/1-intent.md b/phases/1-intent.md index ede9e01..8765daa 100644 --- a/phases/1-intent.md +++ b/phases/1-intent.md @@ -7,7 +7,7 @@ plans how to build it. 1. `sdlc.sh new `. Prefix the slug with the ticket key or the date (`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 `knowledge` pages with the same words. + 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 diff --git a/phases/2-plan.md b/phases/2-plan.md index dd64551..df81205 100644 --- a/phases/2-plan.md +++ b/phases/2-plan.md @@ -6,7 +6,7 @@ enough that side effects are found here, before any code. Scouts write ## 1. Scout -`sdlc.sh check intent ` must print GATE OPEN. Read the `knowledge` +`sdlc.sh check intent ` must print GATE OPEN. Read the `memory` folder and phase 1's `refs/` and `logs/` first: a fact they already give with a source is not scouted again. Dispatch one scout (`roles/scout.md`) per open question, in parallel, each diff --git a/phases/4-evidence.md b/phases/4-evidence.md index e4195c4..4c83c59 100644 --- a/phases/4-evidence.md +++ b/phases/4-evidence.md @@ -51,7 +51,7 @@ that checked it and its deciding output line. A changed policy is updated where it is written in the same delivery, or its follow-up and owner go in Delivery's `Policy:` line. Then: - move verified facts from the refs (and `refs/learnings.md`, if any) into - `knowledge`: one place per fact, updating a page rather than adding a + the `memory` folder: one place per fact, updating a page rather than adding a duplicate; a fact that rests on code ends with its file (path from the project root) and the delivered commit of that file's repository, after re-checking the fact there if the file changed since it was checked: diff --git a/roles/scout.md b/roles/scout.md index 3c756fa..b95fbbc 100644 --- a/roles/scout.md +++ b/roles/scout.md @@ -3,10 +3,10 @@ Answer one question about the code and its data with evidence, in one file under `refs/`. Change nothing else. -Inputs: the question, intent.md, the project root, the `knowledge` folder, +Inputs: the question, intent.md, the project root, the `memory` folder, and config.md. -- Read `knowledge` first; cite what it already answers. Re-check, or mark +- Read `memory` first; cite what it already answers. Re-check, or mark assumed, a cited code fact with no commit or whose file changed since (its repository's `git log`). - Trace from the entry the question names to the end: every caller in every diff --git a/sdlc.sh b/sdlc.sh index 0e7b656..6e1de67 100755 --- a/sdlc.sh +++ b/sdlc.sh @@ -6,7 +6,7 @@ err() { printf 'sdlc: %s\n' "$*" >&2; } die() { err "$1"; exit "${2:-1}"; } usage() { cat <<'USAGE' -Usage: sdlc.sh init [--knowledge path] | new slug | status [slug] [--json] +Usage: sdlc.sh init [--memory path] | new slug | status [slug] [--json] sdlc.sh next slug | check intent|plan slug sdlc.sh approve intent|plan slug --human words|--agent review sdlc.sh review slug pass|fail [--uncommitted] [--repos "path ..."] note @@ -47,11 +47,10 @@ config() { # An unknown word fails closed, as an unknown lazymode does. gates=$(printf '%s' "$gates" | awk '{gsub(/,/," ");for(i=1;i<=NF;i++){v=tolower($i);if(v!="intent"&&v!="plan"){bad=$i;break}if(!seen[v]++)out=out (out==""?"":" ") v}}END{if(bad!="")print "!" bad;else print out}') case "$gates" in '!'*) err "unknown human_gates value '${gates#!}' (write intent, plan, or leave it empty): the human approves intent and plan"; gates='intent plan';; esac - knowledge=$(value "$STORE/config.md" knowledge) + memory=$(value "$STORE/config.md" memory); [ -n "$memory" ] || memory=$(value "$STORE/config.md" knowledge) bound=$(value "$STORE/config.md" repos); boundby='config repos:' - if [ -z "$knowledge" ]; then - if [ -d "$STORE/memory" ]; then knowledge=.sdlc/memory; else knowledge=.sdlc/knowledge; fi - fi + [ -n "$memory" ] || memory=.sdlc/memory + for d in memory knowledge; do [ ! -d "$STORE/$d" ] || [ ".sdlc/$d" = "$memory" ] || [ "$STORE/$d" -ef "$ROOT/$memory" ] || [ "$STORE/$d" -ef "$memory" ] || err ".sdlc/$d is not the memory folder ($memory): move its pages there or name it in config.md memory:"; done } human_gate() { case " $gates " in *" $1 "*) return 0;; *) return 1;; esac; } find_store() { @@ -422,14 +421,14 @@ case "$command" in help) [ "$#" -eq 0 ] || die 'unexpected arguments' 2; usage; exit;; --version) [ "$#" -eq 0 ] || die 'unexpected arguments' 2; cat "$KIT/VERSION"; exit;; init) - custom=; if [ "$#" -gt 0 ]; then [ "$#" -eq 2 ] && [ "$1" = --knowledge ] && [ -n "$2" ] || die 'init [--knowledge path]' 2; custom=$2; fi + custom=; if [ "$#" -gt 0 ]; then [ "$#" -eq 2 ] && [ "$1" = --memory ] && [ -n "$2" ] || die 'init [--memory path]' 2; custom=$2; fi if [ -f .sdlc/config.md ]; then printf 'Existing .sdlc/config.md left as is.\n'; exit; fi mkdir -p .sdlc/work .sdlc/archive tmp=$(mktemp .sdlc/config.md.XXXXXX) - awk -v kit="$KIT" -v k="$custom" '/^kit:/{print "kit: " kit;next}/^knowledge:/{if(k!=""){print "knowledge: " k;next}}{print}' "$KIT/templates/config.md" > "$tmp" + awk -v kit="$KIT" -v k="$custom" '/^kit:/{print "kit: " kit;next}/^memory:/{if(k!=""){print "memory: " k;next}}{print}' "$KIT/templates/config.md" > "$tmp" mv "$tmp" .sdlc/config.md ROOT=$PWD; STORE=$PWD/.sdlc; config - case "$knowledge" in /*|[A-Za-z]:*) :;; *) mkdir -p "$knowledge";; esac + case "$memory" in /*|[A-Za-z]:*) :;; *) mkdir -p "$memory";; esac # Records stay out of git through the clone-local exclude list, so init never changes a tracked file. if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && ! git check-ignore -q .sdlc 2>/dev/null; then exclude=$(git rev-parse --git-path info/exclude); mkdir -p "$(dirname "$exclude")" diff --git a/templates/config.md b/templates/config.md index 5692e5a..9402f44 100644 --- a/templates/config.md +++ b/templates/config.md @@ -16,7 +16,7 @@ qa_runner: qa_session: # gates a human approves: intent, plan. Plan also needs the human when PLAN.md raises a risk flag. human_gates: intent -# durable knowledge: scouts read it first, Evidence adds verified facts to it; empty = .sdlc/memory if it exists (a store from v0), else .sdlc/knowledge -knowledge: +# durable memory: scouts read it first, Evidence adds verified facts to it (older configs call it knowledge:) +memory: .sdlc/memory # repositories a review PASS binds: top folders, separated by spaces or commas, of repositories under the project root (relative, no spaces in a path), "." for the repository holding the root; empty = every repository under the root; review --repos replaces it for one review repos: diff --git a/tests/selftest.sh b/tests/selftest.sh index 51592fe..08c60f2 100755 --- a/tests/selftest.sh +++ b/tests/selftest.sh @@ -1035,6 +1035,28 @@ if [ "$#" -gt 0 ]; then fi run_regressions all +# One memory folder: init names .sdlc/memory, an older knowledge: line still +# names the folder, and a default folder that is not the configured one is +# reported instead of silently ignored (a rewritten config once hid a v0 +# store's .sdlc/memory this way). +repo memory-folder +expect 0 init +grep -q '^memory: \.sdlc/memory$' .sdlc/config.md || fail 'init writes memory: .sdlc/memory' +[ -d .sdlc/memory ] || fail 'init creates .sdlc/memory' +expect 0 status +case "$OUTPUT" in *'is not the memory folder'*) fail "no stray folder, yet: $OUTPUT";; esac +mkdir .sdlc/knowledge +expect 0 status +contains '.sdlc/knowledge is not the memory folder (.sdlc/memory)' +awk '/^memory:/ {print "knowledge: .sdlc/knowledge"; next} {print}' .sdlc/config.md > "$TMP/memory-config" && mv "$TMP/memory-config" .sdlc/config.md +rmdir .sdlc/memory +expect 0 status +case "$OUTPUT" in *'is not the memory folder'*) fail "older knowledge: line not read: $OUTPUT";; esac +mkdir .sdlc/memory +expect 0 status +contains '.sdlc/memory is not the memory folder (.sdlc/knowledge)' +ok 'memory folder: init default, older knowledge: line, stray default folder reported' + cd "$KIT" words=$(cat SKILL.md phases/*.md roles/*.md templates/*.md | wc -w | tr -d ' ') [ "$words" -le 6000 ] || fail "prompt total $words words > 6000" @@ -1070,13 +1092,13 @@ expect 2 unknown ok 'help, version, missing store and usage errors' repo 'project with spaces' assert_file .sdlc/config.md -[ -d .sdlc/work ] && [ -d .sdlc/archive ] && [ -d .sdlc/knowledge ] || fail 'init directories' +[ -d .sdlc/work ] && [ -d .sdlc/archive ] && [ -d .sdlc/memory ] || fail 'init directories' exclude=$(git rev-parse --git-path info/exclude) contains "Ignored .sdlc/ in $exclude (this clone only; no tracked file changed)." [ ! -e .gitignore ] || fail 'init created .gitignore' case "$(cat .sdlc/config.md)" in *"kit: $KIT"*) ;; *) fail 'init kit path';; esac cp .sdlc/config.md "$TMP/config.before" -expect 0 init --knowledge ignored +expect 0 init --memory ignored cmp .sdlc/config.md "$TMP/config.before" || fail 'second init overwrote config' [ "$(awk '$0=="/.sdlc" {n++} END {print n+0}' "$exclude")" -eq 1 ] || fail 'two inits must append one exclude entry' [ ! -e .gitignore ] && [ -z "$(git status --porcelain)" ] || fail 'second init changed the working tree'