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 @@ -168,7 +168,7 @@ agent APPROVED: intent of claims-status (.sdlc/work/claims-status/intent.md)

**기록을 어디에 둘지는 사용자가 정합니다.** 기본값은 프로젝트 작업 사본 안이고, `init.sh . --area ~/knowledge`를 쓰면 사용자가 고른 폴더 아래 `<area>/<단위이름>-<체크아웃 식별자>/`에 저장하고 `.sdlc`를 그곳으로 연결합니다. 체크아웃마다 저장소가 하나씩이므로 워크트리 두 개가 승인 상태를 공유하는 일이 없습니다. 영역이 프로젝트 안에 있거나 프로젝트가 영역 안에 있을 때, 다른 체크아웃이 이미 그 저장소를 소유할 때, 실제 `.sdlc` 디렉터리가 이미 있을 때(자동으로 옮기지 않습니다), 링크를 만들 수 없을 때는 아무것도 쓰지 않고 분명히 실패합니다. 이 소유권은 init 시점뿐 아니라 실행 시점에도 다시 확인합니다. `<store>/PROJECT`에 적힌 체크아웃이 지금 실행 중인 체크아웃과 다르면 `check-gate.sh`, `approve.sh`, `close.sh`, `status.sh`, `tools/auto.sh`, `tools/verify.sh`, `tools/handoff.sh`가 판정을 내리거나 상태를 쓰기 전에 거부하므로, 심볼릭 링크를 그대로 복사한 작업 사본(`cp -R`, rsync, 대부분의 백업 복원)이 다른 체크아웃의 게이트를 열거나 그 피처를 닫을 수 없습니다. 읽기는 이 제약을 받지 않아 `tools/kb.sh show|search|list`는 그대로 쓸 수 있고, 소유권을 자동으로 옮기거나 다시 묶는 일은 없습니다. 어느 쪽을 고르든 **저장소 백업은 사용자의 몫입니다.** git이 더 이상 대신해 주지 않습니다.

**기록을 다시 읽는 도구는 `tools/kb.sh`입니다.** `index`는 목차 페이지를 다시 만듭니다(`init.sh`와 `close.sh`가 자동으로 실행합니다). 페이지는 상태·날짜·태그를 담은 개요 표(최신순), 아직 close가 병합하지 않은 harvest 목록, 피처별 절 순서입니다. `show <slug>`는 피처 하나를 요약본으로 보여줍니다. 목표, `summary.md`(문제·원인·변경·결과·교훈을 담는, 계속 갱신하도록 만든 유일한 기록), 배포 상태, 병합되지 않은 harvest 후보, 교훈 제목이 먼저 나오고 파일 경로는 마지막입니다. `search "<문자열>"`은 열린 피처와 닫힌 피처, 지속 메모리를 대상으로 출력량을 제한한 문자열 검색을 하고, `harvest [--stale <일수>]`는 harvest.md가 아직 memory/에 들어가지 않은 열린 피처를 유휴 기간과 함께 나열합니다(유휴 상태가 오래된 피처는 close 없이 병합할 수 있습니다 — AGENTS.md 규칙 4). `--area <폴더>`를 붙이면 그 폴더 안의 모든 저장소를 대상으로 같은 일을 하며, 원래 체크아웃이 사라진 피처도 읽을 수 있습니다. `index --obsidian`(또는 저장소 config.md의 `index_style: obsidian`)은 Obsidian 볼트용 frontmatter와 인라인 `#태그`를 덧붙입니다. 생성 시각은 절대 쓰지 않으므로 내용이 같으면 diff도 생기지 않습니다. 종료 코드는 `0` 찾음, `1` 없음, `2` 사용법 오류 또는 거부입니다.
**기록을 다시 읽는 도구는 `tools/kb.sh`입니다.** `index`는 목차 페이지를 다시 만듭니다(`init.sh`와 `close.sh`가 자동으로 실행합니다). 페이지는 상태·날짜·태그를 담은 개요 표(최신순), 아직 close가 병합하지 않은 harvest 목록, 피처별 절 순서입니다. `show <slug>`는 피처 하나를 요약본으로 보여줍니다. 목표, `summary.md`(문제·원인·변경·결과·교훈을 담는, 계속 갱신하도록 만든 유일한 기록), 배포 상태, 병합되지 않은 harvest 후보, 교훈 제목이 먼저 나오고 파일 경로는 마지막입니다. `search "<문자열>"`은 열린 피처와 닫힌 피처, 지속 메모리를 대상으로 출력량을 제한한 문자열 검색을 하고, `harvest [--stale <일수>]`는 harvest.md가 아직 memory/에 들어가지 않은 열린 피처를 유휴 기간과 함께 나열합니다(유휴 상태가 오래된 피처는 close 없이 병합할 수 있습니다 — AGENTS.md 규칙 4). `--area <폴더>`를 붙이면 그 폴더 안의 모든 저장소를 대상으로 같은 일을 하며, 원래 체크아웃이 사라진 피처도 읽을 수 있습니다. 저장소 config.md에 `index_style: obsidian`을 적으면 Obsidian 볼트용 frontmatter와 인라인 `#태그`를 덧붙입니다. 생성 시각은 절대 쓰지 않으므로 내용이 같으면 diff도 생기지 않습니다. 종료 코드는 `0` 찾음, `1` 없음, `2` 사용법 오류 또는 거부입니다.

공개 sdlc-kit 저장소는 프레임워크만 담습니다. 기록은 작성된 자리, 즉 프로젝트 작업 사본이나 사용자가 고른 영역에 남아 그대로 읽힙니다.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Per feature, inside the **target project**:

**Where the records live is your choice.** By default they sit in the project's working copy. `init.sh . --area ~/knowledge` puts them in a folder you choose instead — `<area>/<unit>-<checkout-id>/`, with `.sdlc` linked to it, one store per checkout so two worktrees never share approvals. The area is refused if it sits inside the project (or the project inside it), if another checkout already owns that store, if a real `.sdlc` directory is already there (nothing is ever relocated for you), or if the link cannot be made. That ownership is re-checked at RUNTIME, not only at init: `check-gate.sh`, `approve.sh`, `close.sh`, `status.sh`, `tools/auto.sh`, `tools/verify.sh` and `tools/handoff.sh` refuse before any verdict or write when `<store>/PROJECT` names a different checkout, so a copied working copy (`cp -R`, rsync and most restores keep the symlink) can neither open another checkout's gate nor close its features. Reading is never bound that way: `tools/kb.sh show|search|list` still works, and nothing is ever re-bound or moved for you. Whichever you choose, **the store is yours to back up** — git no longer does it for you.

**Reading the records back** is `tools/kb.sh`: `index` regenerates the contents page (`init.sh` and `close.sh` do it for you) — an overview table by state, date and tags, newest first, the harvests no close has merged yet, then one section per feature; `show <slug>` prints one feature as a digest — goal, its `summary.md` (Problem/Cause/Change/Result/Lesson, the one record meant to be kept current), delivery, unmerged harvest candidates, lesson titles, then the paths; `search "<text>"` does a bounded literal search over open and closed features plus durable memory; `harvest [--stale <days>]` lists open features whose harvest.md is not in memory yet, with idle time (a stale one may be merged without closing — AGENTS.md rule 4); and `--area <folder>` does any of these across every store in that folder — including features whose checkout no longer exists. `index --obsidian` (or `index_style: obsidian` in the store's config.md) adds frontmatter and inline `#tags` for a vault; no timestamp is ever written, so an unchanged page produces no diff. Exit codes: `0` found, `1` nothing found, `2` usage error or refusal.
**Reading the records back** is `tools/kb.sh`: `index` regenerates the contents page (`init.sh` and `close.sh` do it for you) — an overview table by state, date and tags, newest first, the harvests no close has merged yet, then one section per feature; `show <slug>` prints one feature as a digest — goal, its `summary.md` (Problem/Cause/Change/Result/Lesson, the one record meant to be kept current), delivery, unmerged harvest candidates, lesson titles, then the paths; `search "<text>"` does a bounded literal search over open and closed features plus durable memory; `harvest [--stale <days>]` lists open features whose harvest.md is not in memory yet, with idle time (a stale one may be merged without closing — AGENTS.md rule 4); and `--area <folder>` does any of these across every store in that folder — including features whose checkout no longer exists. `index_style: obsidian` in the store's config.md adds frontmatter and inline `#tags` for a vault; no timestamp is ever written, so an unchanged page produces no diff. Exit codes: `0` found, `1` nothing found, `2` usage error or refusal.

The public sdlc-kit repository stays framework-only. The records live and stay readable where they were written — in the project's working copy, or in the area you chose.

Expand Down
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ they do it under this contract (gates, artifacts, memory), not beside it.
| "continue <slug>" / "what's next" | Run `gates/status.sh <slug>` from the project root. Its `next →` line names the stage skill or gate command. |
| "where are we" / "sdlc status" | `gates/status.sh` (open features; `--all` adds the newest 20 archived) + `gates/stats.sh` (open + recent closed). Full-archive sweeps: `ls`/`grep .sdlc/archive/`, never the whole listing into context. |
| "has this been done before" / "why is X like this" / debugging an old area | `tools/kb.sh search "<text>"` then `tools/kb.sh show <slug>` — a digest (goal, summary.md, delivery, unmerged harvest candidates, lesson titles, then the paths) over open and closed features plus durable memory, bounded output (`--area <folder>` covers every store, even one whose checkout is gone). Exit 0 found · 1 nothing · 2 usage/refusal. |
| "what did we learn that is not in memory yet" / many open features, few closes | `tools/kb.sh harvest [--stale <days>]` — open features whose harvest.md no close has merged, with idle time; a STALE one may be merged without closing (AGENTS.md rule 4). The contents page (`tools/kb.sh index`; `--obsidian` or `index_style: obsidian` in config.md for a vault) shows the same candidates and an overview table by state, date and tags. |
| "what did we learn that is not in memory yet" / many open features, few closes | `tools/kb.sh harvest [--stale <days>]` — open features whose harvest.md no close has merged, with idle time; a STALE one may be merged without closing (AGENTS.md rule 4). The contents page (`tools/kb.sh index`; `index_style: obsidian` in config.md for a vault) shows the same candidates and an overview table by state, date and tags. |
| a host/scheduler drives the loop, or you need machine state | `gates/status.sh --json` (= `tools/auto.sh status --json`) and `tools/auto.sh next <slug>` (exit 0 ready · 10 needs-human · 20 blocked · 30 complete). Verification receipts: `tools/verify.sh`; review handoff: `tools/handoff.sh`. Contract: `docs/automation.md`. These report and record — they run no stage. |
| gate request answered "approve" in chat | `gates/approve.sh <stage> <artifact> --delegated` per AGENTS.md rule 3. |
| incident / bug / alert on a shipped feature | Read `skills/6-maintain/SKILL.md`. |
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.13.0
v0.13.1
17 changes: 9 additions & 8 deletions gates/knowledge-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -649,18 +649,19 @@ assert_exit "H23 harvest exits 1 for a store with nothing unmerged" 1 \
sh -c "mkdir -p '$FIX/hempty/work/x' && echo '# Intent: x' > '$FIX/hempty/work/x/intent.md' && bash '$KIT/tools/kb.sh' harvest --store '$FIX/hempty'"
assert_fail_msg "H24 harvest takes no positional argument" "takes no argument" bash "$KIT/tools/kb.sh" harvest h-new
assert_fail_msg "H25 --stale wants a number" "number of days" bash "$KIT/tools/kb.sh" harvest --stale soon
# obsidian — frontmatter and inline tags on the generated page only
assert_ok "H26 index --obsidian writes the page" kb index --obsidian
# obsidian — a store setting (config.md), so init.sh and close.sh keep the style;
# it changes the generated page only
printf 'index_style: obsidian # tools/kb.sh index\n' >> .sdlc/config.md
assert_ok_msg "H26 index_style: obsidian in config.md selects the style" "(obsidian)" kb index
assert_ok_msg "H27 the page opens with frontmatter" "---" head -n 1 "$PAGE"
assert_ok_msg "H28 the frontmatter tags the page" "tags: [sdlc-kit, knowledge]" head -n 5 "$PAGE"
assert_ok_msg "H29 feature tags become inline #tags" "#roster #export" cat "$PAGE"
assert_ok_msg "H30 links stay relative markdown" "](work/h-new/intent.md)" cat "$PAGE"
assert_ok "H31 a frontmatter page is still recognized as generated (plain regenerate)" kb index
assert_exit "H32 a plain regenerate drops the frontmatter again" 1 sh -c "head -n 1 '$PAGE' | grep -q '^---'"
printf 'index_style: obsidian # tools/kb.sh index\n' >> .sdlc/config.md
assert_ok "H33 index_style in config.md selects the style without a flag" kb index
assert_ok_msg "H34 the configured style produced frontmatter" "sdlc_store:" head -n 5 "$PAGE"
assert_fail_msg "H35 --obsidian is refused for anything but index" "applies to index only" bash "$KIT/tools/kb.sh" show h-new --obsidian
assert_ok "H31 a frontmatter page is still recognized as generated" kb index
sed -i.bak '/^index_style:/d' .sdlc/config.md && rm -f .sdlc/config.md.bak
assert_ok_msg "H32 removing the setting regenerates a plain page" "(plain)" kb index
assert_exit "H33 the plain page has no frontmatter" 1 sh -c "head -n 1 '$PAGE' | grep -q '^---'"
assert_fail_msg "H34 there is no style flag — one setting, one place" "unknown option" bash "$KIT/tools/kb.sh" index --obsidian
assert_ok_msg "H36 records under work/ were not written by any of this" "not delivered" cat .sdlc/work/h-new/summary.md
assert_nofile ".sdlc/work/h-new/README.md" "H37 no page was written inside a feature directory"
cd "$FIX" || exit 2
Expand Down
43 changes: 43 additions & 0 deletions log/changelog-v0.13.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# v0.13.1 — kb.sh without the duplication v0.13.0 shipped with

A refactor of `tools/kb.sh` with one intentional behaviour change. Output of
`show`, `index`, `list`, `search` and `harvest` is unchanged byte for byte
except where noted; the gates are untouched.

## Changes

- **One definition of "unmerged harvest".** `index` and `harvest` each had their
own loop deciding which open features still hold candidates; both now read
`kb_unmerged`. `index`'s two passes over the features (overview table, then
one section each) share `kb_meta` for state, date, tags and title instead of
extracting them twice.
- **`kb_get`** replaces the nine `kb_real "$(kb_field …)"` idioms; `kb_closed`
replaces the three awk one-liners over `CLOSED`.
- **Dropped: the `## Summary`-inside-intent.md fallback.** It supported a format
no store ever wrote — designed and abandoned inside the v0.13.0 work — and its
helper `kb_summary_where` with it. `summary.md` is the digest.
- **Dropped: `index --obsidian`.** The page style is a property of the store, and
`init.sh` and `close.sh` regenerate the page without flags, so
`index_style: obsidian` in the store's `config.md` was already the setting
that mattered. One setting, one place; the flag is now an unknown option.
- **Fixed on the way:** `show` printed `Tags:` only when the intent carried them,
although the tags live in `summary.md` — the meta line now uses the same
`kb_tags` the page uses. A feature that carries a `CLOSED` record but was never
archived (an interrupted close) is now treated as closed everywhere on the
page — sorted by `closed_at` and labelled `closed <date>` — where before only
`archive/` entries were.
- Internal record lines use `\037` as separator, not a tab: a tab is IFS
whitespace, so `read` collapses adjacent tabs and an empty field (no tags, no
date) shifted the fields after it.
- Header comment shortened; `tools/kb.sh` 605 → 538 lines.

Checked against a real store (84 open, 2 archived, Korean records): the page
and the `show` digests are byte-identical to v0.13.0 except for the two lines
the fixes above describe; `harvest` output is identical.

## Validation

- `bash gates/knowledge-test.sh` → `KNOWLEDGE-TEST PASS`, 144 assertions
(section H rewritten for the config-only style; the flag refusal is asserted)
- `bash gates/selftest.sh` → `SELFTEST PASS`
- `bash gates/e2e.sh` → `E2E PASS`, 152 · `bash gates/autotest.sh` → `AUTOTEST PASS`, 195
Loading
Loading