Skip to content

Releases: cskwork/sdlc-kit

v0.17.0: verification gates ship — baseline, coverage, flaky proof

Choose a tag to compare

@cskwork cskwork released this 27 Sep 02:45
34fb2ff

v0.17.0 — verification that gates ship: baseline, coverage, per-feature recipes

Until now only the host driver enforced the verification receipt. A human
could approve ship and close shipped over a failed, stale or missing
receipt, even under profile: strict; a failure the base already had read
like a regression; nothing proved a new test could fail; a recipe that skipped
a requirement still said VERIFY ok; and two open features collided in the
one project recipe. This release closes those holes. A project without
.sdlc/verify.md behaves as before. The reference is docs/automation.md §4;
the recipe syntax is templates/verify.md.

Changes

  • The receipt gates ship. approve.sh ship (every mode), close.sh shipped and check-gate.sh ship read one verdict table
    (gates/_auto.sh sdlc_verify_gate), shared with auto.sh, handoff.sh and
    status.sh. ok passes; unconfigured passes with a note; blocked needs
    --accept-gap "<the human's words>" (never --lazy); every other state is
    refused with its fix. Accepted words are recorded in the ship approval and,
    on a shipped close, in CLOSED.
  • No git repository is now blocked: the receipt binds no source.
  • Baseline. New tools/verify.sh baseline <slug> [--base <ref>] runs the
    project recipe's build/unit/lint checks at the base commit in a disposable
    worktree and writes verify-baseline.md. run labels a failure the base
    shared, with the same exit status, pre-existing. New baseline_setup:.
  • Must fail on base. A check may end in | must-fail-on-base; with the
    new test_paths:, the baseline runs the new test against the old code.
    Passing there, or not running (124/126/127), is the new state vacuous.
  • Coverage. Every requirement id (spec.md R, compact intent.md O) needs a
    check named for it or a gap: <id> | <reason> line, else the new state
    uncovered. Strict needs a runtime/e2e check and the happy, boundary
    and negative variants. New tools/verify.sh coverage <slug>.
  • Per-feature recipe. Optional .sdlc/work/<slug>/verify.md
    (templates/verify-feature.md), check:/gap: lines only; the receipt binds
    its digest.
  • Flaky. A failing runtime/e2e check runs once more; passing then is the
    new state flaky, refused like fail.
  • Lens verdicts. Ship needs a VERDICT: line under each verifier lens in
    evidence.md, or AGENTS.md rule 5's gap line.
  • Safety. forbidden_hosts: refuses a recipe that names a listed host.
    tools/_run.py redacts credentials in every log before it is hashed
    (best-effort).
  • Parsing. A UTF-8 BOM no longer hides profile: strict; value lines take
    a trailing # comment; an indented or tabbed directive is refused, not
    skipped; doctor_attempt_timeout is validated.
  • auto.sh --json: new blockers verify.uncovered / verify.vacuous;
    flaky is verify.fail; a verification that fails after the ship approval
    blocks the delivery stage.
  • verify.sh resolves a pyenv/asdf python shim once per run.
  • approve.sh ship writes the source snapshot before the record.
  • kb.sh never reads verify-baseline.md.

Agent guidance

  • roles/verifier.md: baseline, run and coverage with a recipe;
    pre-existing vs regression; every changed file traced; the scenario floor
    (happy, boundary, negative, + regression, + authz) with expectations first,
    per role and platform, source rung stated; writes read back; new tests
    must fail on base; red flags.
  • roles/adversary.md: seven blocking clean-code findings and a per-file
    table.
  • skills/4-build, 5-ship, AGENTS.md rule 6, templates: the checks are
    written with the code; ship needs ok or the human's --accept-gap;
    evidence.md carries variants, the receipt line and each lens VERDICT:.

Compatibility

  • No recipe: no change beyond one note line.
  • With a recipe: a ship approval or shipped close over a non-ok receipt
    is now refused. A recipe with no git repository needs --accept-gap.
  • Old recipes may read uncovered until checks are named for requirements
    (R1, R1.happy) or gap lines are added; strict recipes also need the
    variants.
  • evidence.md needs the lens VERDICT: lines, recipe or not.
  • Old receipts stay valid; the schema stays sdlc-kit/verify-receipt@1.

Validation

  • bash gates/selftest.sh → SELFTEST PASS. New sections cover the
    ship gate, --accept-gap, baseline and pre-existing, must-fail and
    vacuous, coverage and the strict variants, per-feature recipes, flaky,
    forbidden hosts, redaction, the parser rules, worktree cleanup, no-git,
    and a receipt going stale when the source changes (a gap before v0.17.0).
  • Faster: the selftest runs its independent sections in parallel (~31 s →
    ~9 s), and sdlc_sha256_stdin (prefers sha256sum) and sdlc_field
    (no awk per field) roughly halve the processes verify.sh run/check
    start.
  • bash -n on every changed script; no CRLF.
  • By hand: auto.sh --json blockers, --base handling, TERM/HUP during a
    baseline, and the worktree-removal fallback.

v0.16.0: area pages — figures, menu-path file names, reader-first tables

Choose a tag to compare

@cskwork cskwork released this 23 Sep 06:09
269e1f2

v0.16.0 — area pages: figures, menu-path file names, reader-first layout

v0.15.0 gave a product area one home for its business rules (P-numbers). A
menu that shows counts, rates, scores or charts had nowhere to say what a
number on screen actually means — "what is counted, out of what, real-time
or batch" lived in someone's head or nowhere. This release adds an optional
Numbers section to the area page, numbered N1… with the same permanence and
filing rules as a business rule. It also names each area page by its menu
path, so the file list reads like the product's menus, and lays the page out
reader first: plain language on top for a non-developer, every source,
verification, and code location folded into an evidence block at the
bottom, with rules, figures, history and evidence as markdown tables. Gates,
approvals, and the --json schema are unchanged; kb.sh's rule count still
counts only live rules (a | P<n> | row, or an older - P<n>: line).

Changes

  • templates/area.md gains an optional Numbers (통계 산정) section
    between Business rules and How it works: one row per on-screen figure,
    named as the screen labels it, recording what it counts, out of what,
    whether it is real-time or batch (job, refresh, data-as-of), its source,
    and who set it. Delete the section for an area with no figures. N-numbers
    follow the P-number rules: permanent, a changed figure is edited in place,
    a retired one stays struck through.
  • The loop treats a figure like a business rule wherever omitting it
    would leave it unprotected:
    • templates/spec.md's "Business rules touched" section gains a sibling
      line for numbers touched (kept/changed/new), so the Side effects
      verifier re-checks a figure it did not set out to change, the same way
      it already re-checks a P-number.
    • Ship's retrospective (skills/5-ship/SKILL.md) writes a figure a
      feature sets, changes, or retires as an area candidate, same as a
      business rule.
    • templates/harvest.md's Area candidates format gains the N<n>
      line shape the close merge numbers, next to the existing P<n> one.
    • AGENTS.md rule 4's "what goes where" table gains a row: a figure's
      calculation method files to the area page's Numbers, written by the
      close merge on a shipped close only — same as a business rule.
  • An area page is named by its menu path. The file name is the Menu
    line with each > written -, and any character a Windows or macOS
    file name may not hold (/ \ : * ? " < > |) replaced with -: Menu
    교사 > 학생 > 학급 분석 → memory/areas/교사 - 학생 - 학급 분석.md. The
    "ASCII kebab-case file name" instruction is gone from templates/area.md,
    AGENTS.md rule 4, the READMEs, and init.sh's DOMAIN.md header; the
    close merge (AGENTS.md rule 4, skills/5-ship) is told the rule when it
    creates a page.
  • tools/kb.sh finds an area page by its file name, by its Menu line,
    or by the file name a Menu gives (kb_area_file); a name holding / or
    \, or starting with ., is still never looked up as a file. The
    contents page's Product areas links percent-encode space % ( ) [
    ] # < > (kb_area_href) so a link to 교사 - 학생 - 학급 분석.md
    opens in GitHub and Obsidian; Hangul bytes stay as written. kb_body
    prints a <summary>…</summary> line as a section label, so show prints
    the page top first and the evidence block last, under
    근거 · 코드 위치 (개발자용): — the part the line bound cuts first. The
    Rules column no longer counts an unfilled template row | P1 | <…> |
    (or line - P1: <…>), and show drops such a line the way it already
    dropped - Where: <…>.
  • Reader-first templates/area.md. Menu and Aliases on top; Business
    rules, Numbers, How it works, and History in plain sentences with no
    source and no code; then a <details> block — "근거 · 코드 위치
    (개발자용)" — holding the Where line and one evidence row per rule or
    figure: | P1 | source | set by | verified |. Evidence rows sit below
    the block's opening line, where the rule count stops, so they are never
    counted as rules. A retired rule stays struck through on top
    (| ~~P3~~ | ~~rule~~ |) and its evidence row's Source cell carries
    retired YYYY-MM-DD by <slug>: <why>. The section
    headings are unchanged. templates/harvest.md's area candidates split
    the plain sentence from its evidence the same way, and ship's
    retrospective says which part goes where.
  • Obsidian form of the evidence block. Obsidian does not render markdown
    inside <details>, so a store with index_style: obsidian writes it as
    a folded callout (> [!info]- 근거 · 코드 위치 (개발자용), every line
    prefixed > ); kb.sh reads both forms (Where found, callout lines
    printed last without > , never counted as rules).
  • Tables for rules, figures, history and evidence. Each is one
    markdown table row (| P1 | <rule> |, | N1 | label | counted | out of | timing |, | YYYY-MM-DD | slug | what changed |, and in the evidence
    block | P1 | source | set by | verified | under the - Where: line);
    Menu, Aliases and How it works stay list lines. A retired rule is
    | ~~P3~~ | ~~rule~~ |, its reason in its evidence row's Source cell.
    Header words are free (| # | 정책 | works); a literal | in a cell is
    \|. kb.sh keys on row shapes only: the Rules count reads | P<n> |
    rows above the evidence block (the <details> or > [!…] line), the
    last change reads History's first | YYYY-MM-DD | row, and show
    prints tables as written — callout rows without > — dropping only a
    template placeholder row (every cell after the first <…>) and the
    header of a table left with no rows. The close merge (AGENTS.md rule 4,
    ship, harvest.md) writes rows; harvest candidates stay line-shaped.
  • README.md / README.ko.md: the "Knowledge is filed by product area"
    paragraph and the memory-tree comment now mention the optional Numbers
    section, the menu-path file name, and the folded evidence block.

Compatibility

Existing ASCII-named area pages keep working. kb.sh show "<menu path>" still finds a page by its Menu line whatever the file is called,
the contents page still lists and links it, and features still file under
it by their Area: line. Only a newly created page takes the menu-path
name. To rename an old page, move it to the name its Menu line gives (a
plain mv when the store is not in git), then regenerate the contents page:

git mv ".sdlc/memory/areas/class-analysis.md" ".sdlc/memory/areas/교사 - 학생 - 학급 분석.md"
tools/kb.sh index

A feature whose Area: line names the old file name
instead of the Menu should be changed to the Menu words. Nothing is renamed
for you.

List-form pages keep working. A page written with - P<n>: rule
lines and - YYYY-MM-DD <slug> — … History lines (v0.15.0 and the first
v0.16.0 commits) is still counted and dated, and a page may mix both forms;
convert it to rows whenever the close merge next edits it.

Existing pages with inline sources keep working. A rule written the old
way — - P1: <rule> — source: … · set by … — still counts and still
prints; the close merge moves its source to the evidence block the next
time it edits that rule, or you can move it by hand.

An area page with no Numbers section is unaffected: the section is
optional and kb.sh's Rules column stays a P-number count only (a store
with only P-numbers renders identically to before). No script parses the
N<n>: line; filing it is an instruction to the agent, exactly like a
P<n>: line.

Validation

  • bash gates/selftest.sh → SELFTEST PASS. Its "knowledge by product
    area" test now also files a feature under a Hangul menu page named
    교사 - 학생 - 학급 분석.md, laid out reader first with evidence rows and
    a retired rule: the contents-page row links the percent-encoded path with
    a Rules count of 2 (evidence and retired rows not counted); show by
    Menu and by file name prints the rules before the evidence block; a
    lookup holding / is refused; a second page in the Obsidian callout form
    is read the same way. Both pages are in table form with Korean header
    words: evidence rows, a retired row and a placeholder row are not
    counted, the last change comes from the History table, and show
    prints the tables with the evidence last. The list-form roster page
    still counts. With the old kb.sh, the test fails.

v0.15.0: knowledge filed by product area, with its business rules

Choose a tag to compare

@cskwork cskwork released this 22 Sep 22:32
b3903f4

v0.15.0 — knowledge filed by product area, with its business rules

Records were filed by feature and date. Nobody could ask "what are the rules
for the submit screen?" without knowing which slug changed it. In real stores
DOMAIN.md had turned into long evidence sentences with file paths and
hashes, not something a person reads. summary.md had five one-line fields
and no store had used it. This release files knowledge the way people
navigate a product, by area (for a web app, by menu), and gives business
rules (정책) one home. Gates, approvals, the --json schema, and lazymode are
unchanged.

Changes

  • Product area pages (templates/area.md → memory/areas/<area>.md).
    There is one page per area. For a web app an area is one menu, named by its
    menu path (학습 > 평가 > 제출); for other software it is a module, API,
    job, or CLI command. Each page has:

    • Menu:, Where:, and Aliases: lines;
    • Business rules (정책), numbered P1… and written as testable sentences
      a non-developer can read, each with its source and the feature that set
      it (P-numbers are never reused, and a retired rule stays struck through);
    • a short How it works;
    • a History line per feature that changed the area.

    init.sh seeds memory/areas/. The page replaces DOMAIN.md's old
    "split into memory/domain/<area>.md" overflow rule, so there is one
    concept instead of two.

  • What goes where, in one table (AGENTS.md rule 4). Business rules go to
    the area page, area history to the page's History, cross-area terms and
    facts to DOMAIN.md, traps to lessons, agent rules to POLICY.md (only on the
    human's word, and never a business rule), and one feature's story to
    summary.md. Every row except POLICY.md and summary.md is written only by
    the close merge, which creates a missing area page from the template.

  • The loop uses the pages:

    • Intent names the area and cites the P-numbers the request touches.
    • The spec's AS-IS names each rule it keeps, changes, or retires.
    • The Side effects verifier re-checks the rules the change did not set out
      to change.
    • Ship writes every set, changed, or retired rule and a history line as area
      candidates (a new Area candidates section in templates/harvest.md).
  • summary.md reads like a note to a colleague: a Goal sentence as its
    title, then Area, Tags, and Status (only what delivery.md confirms),
    followed by What was wrong, Before → After, How to check, and
    Remember.

  • tools/kb.sh:

    • index opens with a Product areas table (live-rule count, last
      change, features, and areas a feature names that have no page yet).
    • The overview table gains an Area column.
    • show <name> falls back to a product area — the page's file name, its
      exact menu path, or an area features name with no page yet — and prints
      it with the features that name it. An Area: line may use the menu path
      or the file name.
    • search already covered memory/, so business rules are found by their
      words.
    • An area name containing / or starting with . never resolves to a file.
    • A record heading with nothing under it is no longer printed, so an
      unfinished summary shows only what is known. Summaries print up to 20
      lines (was 12).
    • Messages: a show miss reads "no feature or product area", show with
      no argument asks for "a feature slug or a product area", an --area miss
      reads "no such knowledge folder (--area)", the usage text lists
      show <slug | product area>, the contents page's Search line names
      show <slug | area>, and the harvest and close hints name the area
      pages.
  • kb.sh runs with byte semantics (LC_ALL=C). Under a UTF-8 locale,
    macOS awk compares strings by collation, so different Hangul menu paths
    compared equal and every feature was filed under every area. Found by macOS
    CI (en_US.UTF-8).

  • Rules and history describe what shipped. They merge only on a
    shipped close; any other close drops them, and a stale merge leaves
    them in harvest.md for that close. The merge
    gives a new rule the next number its page never used. A fact about one area
    goes to that page's How it works. The spec template gains a Business
    rules touched
    section, and the spec adversary and the verifier read the
    area pages.

  • E2E lens findings from v0.14.0, fixed:

    • The compact route gains a Baseline: line (build reads it).
    • Probe 8 covers any outbound call, not only UI.
    • evidence.md and the ship authorization name the compact-route source.
    • The router states that a broken-and-undiagnosed report goes to stage 6
      ahead of the generic change row.
    • The compact criterion now explains why optional open questions still
      disqualify it: the compact route has no spec to answer them.

Compatibility

Older summaries (Problem/Cause/… bullets) still print, now up to 20 lines. A store with
no memory/areas/ and no Area: lines gets no Product areas section. The
overview table's header gains a column; gates/knowledge-test.sh H8 is
updated for that deliberate change.

Validation

  • bash gates/knowledge-test.sh → KNOWLEDGE-TEST PASS, 152, under the
    en_US.UTF-8, ko_KR.UTF-8 and C locales. H38–H45 are new and cover one
    contract each: area row, page-less area, show by menu path, show of a
    page-less area, path walk, unfilled summary, filled section, and Area by
    file name. H38, H39 and H45 fail on the pre-fix kb.sh under en_US.UTF-8.
  • Three fresh-context verifier lenses (E2E, Side effects, Intent match).
    Round 1 found the unfilled-template leak (blocking) and ~20 minor gaps. A
    round-2 re-check found every one resolved (PASS), including against the old
    kb.sh on seven real stores (only named differences, byte-identical
    regeneration, unchanged exit codes). It also found four minor new gaps
    (usage line, changelog wording, stale-merge rule loss, researcher
    destination), and those were fixed.
  • bash gates/selftest.sh → SELFTEST PASS
  • bash gates/e2e.sh → E2E PASS, 152 · bash gates/autotest.sh → AUTOTEST PASS, 195

Tests: one smoke test

The kit is mostly instructions, and four suites (about 3,800 lines, several
minutes per run) mostly re-checked wording. gates/e2e.sh,
gates/autotest.sh, gates/knowledge-test.sh, and
gates/win-restricted-run.py are removed. gates/selftest.sh is now one
smoke test (~75 lines, a few seconds) covering: scripts parse and are
LF-only, SKILL.md frontmatter, gates bind content and upstream, lazymode
limits, close proof (lesson, ship approval, confirmed delivery), and
product-area filing under a UTF-8 locale. CI runs only that: on pull
requests (branch protection requires its checks) and by hand, no longer on
every push to main or on tags.

v0.14.0: leaner contract, test-first bug fixes, generic intake

Choose a tag to compare

@cskwork cskwork released this 22 Sep 21:15
ac98656

v0.14.0 — a leaner contract, test-first bug fixes, generic intake

Real stores showed where the kit's words went. The agent-facing prose had
grown to about 128 KB. AGENTS.md restated what the scripts already enforce,
each stage skill repeated the heartbeat and memory paragraphs, and bug fixes
could "prove" themselves with reproduction steps that nothing re-runs. The
kit is used for any software (web, API, batch, CLI), but stage 6 intake still
asked only UI questions. This release cuts the duplication and makes the bug
proof durable. Gates, scripts, the --json schema, and lazymode are
unchanged; init.sh gains three optional config lines.

Changes

  • AGENTS.md keeps what an agent must decide (453 → 324 lines, −31%).
    Mechanics a script enforces and prints a reason for (digest binding,
    ship-snapshot rules, C-quoted paths, handoff refusals, store ownership) are
    now one line each. Text that docs/automation.md already holds (full-auto
    intent contract §3, verify receipt §4) is now a pointer. Rule numbers and
    section names are unchanged. The stale "rule 3a" citations in
    gates/_auto.sh and gates/status.sh now resolve: 3a labels the full-auto
    intent contract.
  • Defined once. Heartbeat (rule 9, now listing the stage names and
    build's n/m) and memory reading (rule 4) are no longer repeated in six stage
    skills; each skill carries a one-line pointer. The harvest/close-writer
    rule, the kb.sh digest description, and tripwire caveats are referenced,
    not restated. SKILL.md drops its Coexistence and Invariants summaries,
    which repeated AGENTS.md. roles/researcher.md points at probes.md
    instead of copying three probes.
  • Bug fixes start with a failing test (AGENTS.md rule 6). By default the
    reproduction is an automated test at the lowest level that reaches the
    defect. It fails on the pre-fix code for the reported reason, passes after,
    and stays in the suite. Manual steps or logs stand in only when no test can
    reach the defect, and the evidence says why. Stage 6 drafts the test outside
    the source tree, build adds it before the fix, and the verifier runs it
    against the pre-fix commit and must see it fail. templates/evidence.md
    gains a Regression test: line, and the plan's Proof and the compact
    route's Proof line name it.
  • Generic stage 6 intake. The five questions now fit a UI, an API, a job,
    or a CLI: what was done with what input, what happened versus what was
    expected, where and when, as whom, and what trace exists (request or trace
    id, log line, affected record keys). A symptom class (nothing happened /
    wrong result / looks wrong / intermittent) replaces the UI-only
    "does not react vs looks disabled" split. Probe 2 is no longer a MyBatis
    awk over mapper.xml: it diffs the filters of every query on one entity
    (SQL, ORM, API params, cache keys).
  • Projects name their own helpers. New optional researcher:,
    verifier:, adversary: keys in .sdlc/config.md: the named agent or
    skill is dispatched with the kit's role file as its contract ("Running
    beside…" rule 3). Empty or absent means the old behavior. Existing configs
    are not rewritten.

Agent-facing prose (SKILL.md, AGENTS.md, stage skills, roles): 104,136 →
~89,700 bytes.

Validation

  • bash gates/knowledge-test.sh → KNOWLEDGE-TEST PASS, 144
  • bash gates/selftest.sh → SELFTEST PASS
  • bash gates/e2e.sh → E2E PASS, 152 · bash gates/autotest.sh → AUTOTEST PASS, 195
  • Three fresh-context verifier lenses over the diff: E2E (init.sh in a
    disposable repo, plus a backend-bug compact walk and a UI full-route walk),
    Side effects (removed instructions and cross-references), and Intent match
    (against the approved recommendation). Their minor findings in the changed
    text were fixed before release.

v0.12.0: external knowledge areas and retrieval

Choose a tag to compare

@cskwork cskwork released this 20 Sep 15:26
cfb4c2e

v0.12.0 — records out of git, an external knowledge area, and retrieval

Records are the project's knowledge, not its source. init.sh now ignores the
whole of .sdlc/ with one anchored rule, an optional external area keeps a
checkout's store outside the checkout entirely, and tools/kb.sh is the way to
read any of it back. Gate authority, tamper detection, source binding, the
--json automation schema and lazymode are unchanged in kind.

Changes

  • One ignore rule: /.sdlc. init.sh writes it, anchored to the project root, so it matches this project's records — a real directory or the symlink an external area installs — and never a .sdlc deeper in the tree (a nested shipping unit keeps its own rule, written by its own init run). The twenty narrower kit-owned lines earlier versions issued are subsumed and removed by exact match; a CRLF .gitignore is handled, and every other line, its bytes and its line ending, is written back untouched — read and written by the shell itself, never through an awk that may translate line endings on Windows. The git index is never touched: records already committed by an older kit stay committed until you untrack them yourself, and init.sh prints the exact command.
  • init.sh [dir] --area <folder> — an external knowledge area. The store is <folder>/<unit>-<checkout-id>/, .sdlc is a symlink to it, and <store>/PROJECT records the owning checkout, its unit, its id, the creation time and the kit version. One store per checkout, so two worktrees or two same-named clones never share approvals. Refused, writing nothing: an area inside the project, a project inside the area (checked before any folder is created), a store another checkout owns, a directory that is not a store, an unwritable area (refused by the writability test and, if that test cannot see the right — a Windows deny ACL — by the first real write, before anything is linked), a .sdlc link that points elsewhere, or a link the filesystem cannot create.
  • Ownership is enforced at runtime, not only at init. check-gate.sh, approve.sh, close.sh, status.sh (prose and --json), tools/auto.sh, tools/verify.sh, tools/handoff.sh, init.sh (including an ordinary re-run) and tools/kb.sh index all refuse, before any verdict and before any write, when the store they reach names a different checkout, carries no ownership record, or carries an unreadable one. This closes a real accident: cp -R, rsync, tar without --dereference and most backup restores preserve the .sdlc symlink, so a copied working copy resolved into the ORIGINAL's store and could read its approvals and archive its features. An ordinary project-local .sdlc directory has no PROJECT record, needs none, and is unaffected.
  • Nothing is migrated, re-bound, or moved for you. A refusal prints the store, the recorded owner, this checkout, how to read the records (tools/kb.sh --store/--area), how to give this checkout a store of its own, and — for a renamed or moved checkout — the one manual line to edit (project: in <store>/PROJECT). A real .sdlc directory is never relocated by --area; it keeps working exactly as it is.
  • tools/kb.sh — reading the records back. index regenerates a store's contents page (init.sh and close.sh run it; a README.md it did not generate is never overwritten), show <slug> prints one feature's goal, track, documents, delivery and lessons, search "<text>" is a bounded literal (grep -F) search across open and closed features and durable memory, and list names stores and features. --area <folder> covers every owned store in that folder, read-only and one level deep, following no symlinks — including features whose checkout no longer exists. scratch/, approvals/, progress.md, baseline.txt, checkpoint.md and verify-receipt.md are never read. Exit codes: 0 found, 1 nothing found, 2 usage error or refusal.
  • Readable store names. Only path-hostile characters are replaced in the unit name, so a Korean, Japanese or accented checkout name stays legible in the area listing and in kb.sh output (지식-프로젝트-e2117a76), instead of collapsing to project-<id>.
  • The source snapshot excludes the bare .sdlc entry as well as its descendants, in both the working-source list and the committed-tree enumeration, so an external store's symlink is never bound by a ship approval and moving the area is not drift.
  • Stage guidance and docs. skills/1-intent, skills/5-ship, skills/6-maintain, templates/evidence.md, AGENTS.md rule 7, SKILL.md, both READMEs, docs/index.html and docs/automation.md state the same storage behaviour: records are ignored by default, a clone does not carry them, the store is yours to back up, and a stage starts with a targeted kb.sh search/show instead of scanning the archive.

Usage

# default: records stay in the checkout, ignored by git
~/sdlc-kit/init.sh .

# or keep this checkout's records outside it, in a folder you choose
~/sdlc-kit/init.sh . --area ~/knowledge
#   → ~/knowledge/<unit>-<checkout-id>/ , with .sdlc linked to it

# read the records back, from the project
~/sdlc-kit/tools/kb.sh index                       # refresh this store's contents page
~/sdlc-kit/tools/kb.sh show   260920-login-fix     # one feature: goal, documents, delivery, lessons
~/sdlc-kit/tools/kb.sh search "rate limit"         # bounded literal search

# … or across every store in the area, from anywhere, read-only
~/sdlc-kit/tools/kb.sh list   --area ~/knowledge
~/sdlc-kit/tools/kb.sh show   260920-login-fix --area ~/knowledge
~/sdlc-kit/tools/kb.sh search "rate limit" --area ~/knowledge --limit 20

Upgrade notes

  • Existing projects keep working. Re-run init.sh to get the single /.sdlc rule and the cleanup of the lines it subsumes. Your existing records are not moved, rewritten or deleted, and the git index is left exactly as it is — already-committed records stay in history until you run the untracking command init.sh prints.
  • There is no automated migration, by design. --area never relocates a real .sdlc directory: to use an area for a checkout that already has local records, move them aside yourself and let init.sh create an empty store; the old records stay readable with tools/kb.sh --store <path>.
  • Approval state is per checkout; retrieval is area-wide. Gates, status, verification and handoff answer only for the checkout that owns the store. Reading (kb.sh list|show|search, with or without --area) is not bound to any checkout, which is what lets knowledge outlive the worktree that produced it.
  • Backups and versioning of the store are yours, whichever location you choose: git no longer carries the records, and this kit adds no backup mechanism.
  • Windows: --area requires real symlinks — run Git Bash with MSYS=winsymlinks:nativestrict. init.sh fails loudly if the link comes out as a copy rather than creating a forked store, and gates/knowledge-test.sh reports the external-area cases as NOT VERIFIED where the filesystem cannot link at all.
  • docs/index.html no longer describes the durable record as committable; that policy is the one this release inverts.

Validation

Local, on macOS (darwin 25.6.0, arm64, GNU bash 3.2.57, git 2.54.0, APFS
case-insensitive, UTF-8):

  • bash gates/selftest.sh → SELFTEST PASS
  • bash gates/knowledge-test.sh → KNOWLEDGE-TEST PASS, 107 assertions, 0 failures (new suite: the ignore rule, area binding and every refusal, the loop through the link, owner isolation against a cp -R copy, CRLF ignore cleanup, readable Unicode store names, retrieval after the checkout is deleted)
  • bash gates/e2e.sh → E2E PASS, 152 assertions, 0 failures
  • bash gates/autotest.sh → AUTOTEST PASS, 195 assertions, 0 failures
  • bash -n over every changed shell script: clean. git diff --check: clean.

The CI matrix runs all four suites on Ubuntu, macOS and Windows Git Bash for
pull requests, main and version tags. The published release notes link the CI
results for the shipped source; earlier runs are not evidence for later edits.

The Windows permission fixture uses a native ACL confined to its temporary
directory and proves an actual write is denied before testing init. Retained
history is searched from a valid directory after the checkout is deleted, with
its evidence bytes and the same query results checked. CRLF preservation is
checked byte by byte, independently of text-mode awk.

Release verification

  • PR #5 merged after all required Ubuntu, macOS and Windows Git Bash checks passed in run 35518755595. Each runner executed knowledge, selftest, E2E and automation suites.
  • The released commit is cfb4c2e0ca403b74f6adf621cbc48ee9cb6c5f4a. Its source tree is identical to the tested PR head 3bd9525f2e8d01b1159ded1131683e91c7c31132. Remote dev, main, and v0.12.0 were verified at that release commit.
  • Windows permission-denial coverage runs the real write probe and initializer with backup/restore privileges removed only from the test subprocess. No Windows check was waived.

v0.11.0: three-lens verification, bound origin, machine-readable fix loop

Choose a tag to compare

@cskwork cskwork released this 17 Sep 13:09
3129049

v0.11.0 — three-lens verification, bound origin, machine-readable fix loop

The build stage's verification grows from one E2E pass into three lenses that
run in parallel, each in a fresh context, under the one existing role
contract; the request's origin becomes a bound artifact; the fix-loop cap and
data-consistency checks become machine-readable. The gates and the source
binding are unchanged in kind.

Changes

  • Three verification lenses. roles/verifier.md is now the single definition of what verification checks, dispatched one lens each: E2E (the change through the real interface; the bug-fix proof chain; the verify.sh receipt), Side effects (baseline and untouched items, neighbouring flows, and data consistency — every shape the diff writes or reads followed to its other producers and consumers), and Intent match (the build read back, per intent.md O-item, against the origin of the request, listing what is covered, missing, and beyond; a detail dropped between the origin and an approved spec.md is a finding). The verifier now receives intent.md and its origin on the full route too — before, it saw only plan.md and spec.md, so the human's actual ask was never read back.
  • origin.md — the ticket / 기획서 as requested, bound by the gates (templates/origin.md). Stage 1 snapshots the origin BEFORE the intent gate; approve.sh intent binds its digest exactly like a spec or plan upstream (upstream_origin:), and every downstream gate, status.sh, check-gate.sh, tools/auto.sh and close.sh report a rewrite in the same words (origin.md changed after … — re-approve intent, then <stage>). An origin written after the approval reads as unbound and closes the gate, exactly like a late spec.md. origin is never a gate of its own: approve.sh origin is refused. Absent when the request had no origin beyond the chat — nothing is demanded then. It is part of the durable record (AGENTS.md rule 7, init.sh, README trees). One artifact map now serves every script (_common.sh sdlc_artifact_of); status.sh and _auto.sh dropped their private copies.
  • O-numbered success criteria. intent.md's Success criteria are O1..On in the origin's words (else the human's); spec R-items cite them (R1: … (O1)); an O-item with no R is a flagged concern, never a silent drop. The adversary's traceability check and the verifier's Intent match lens count coverage over them instead of rediscovering it.
  • Data touched in plan.md. The plan names every shape the changed files write or read, with its other producers and consumers and what happens to pre-existing records. The plan adversary checks the list is complete; the Side effects lens executes it and treats a missed shape as a finding.
  • Fix loop, any lens, machine-readable cap. skills/4-build routes a finding from ANY lens into the fix loop. A round is one deviations.md line — lens, accepted, declined, re-check: pending | resolved | open: <what> updated in place. The cap stays at three rounds. gates/_auto.sh sdlc_auto_fixloop_state reads those lines: round 3 still open, or any round past 3, is fixloop.exhausted — tools/auto.sh next exits 10 (needs-human) at every lazymode, both while build is open and once evidence.md exists over it (the lazy ship gate is refused), status --json carries "fix_loop", and status.sh prints FIX LOOP EXHAUSTED and overrides its next action, exactly like an open material question. A finding that implies new scope is a human decision, not a fix.
  • data check kind in .sdlc/verify.md (templates/verify.md, docs/automation.md): a read-only consistency query for the Side effects lens, receipted like every other check and never counted as runtime evidence.
  • templates/evidence.md carries the three reports under one Verification section (E2E · Side effects · Intent match · Fix loop) and names the origin and its live re-read; the old End-to-end and Regression sections fold into it.
  • DRY: the real-E2E prose that was repeated in AGENTS.md rule 6, skills/4-build, skills/5-ship and roles/verifier.md now lives in the role file; rule 6 states the contract in one paragraph and the stage skills reference it.

Upgrade notes

  • Nothing is required. Existing approvals keep working: a feature without origin.md binds nothing new, and existing evidence.md files stay valid — no script parses their section names.
  • Existing deviations.md round lines without a re-check: field read as open (in progress) and never as exhausted; only a round past 3, or a round 3 marked open, blocks.
  • Snapshot the ticket or 기획서 as origin.md BEFORE approve.sh intent; written afterwards it closes the gate as unbound until intent is re-approved — that is the binding working.
  • status --json gained fix_loop; drivers that ignore unknown fields are unaffected.

Validation

Local, on macOS (Bash 3.2) — darwin 25.6.0, arm64:

  • bash gates/selftest.sh → SELFTEST PASS (new: origin.md bound by intent and downstream gates, refused as a gate, unbound when written late).
  • bash gates/e2e.sh → E2E PASS, 146 assertions, 0 failures (new: B2b, B10i–B10k, B12b — the full route snapshots, binds, refuses a post-review origin edit, archives origin.md).
  • bash gates/autotest.sh → AUTOTEST PASS, 195 assertions, 0 failures (new: A24a–h fix-loop states through next, status.sh, status --json and the lazy ship gate; A25a–d the data kind runs, is receipted, is not runtime evidence, and fails the run when it fails).
  • bash -n over every changed shell script: clean.

v0.10.0: automated SDLC review handoffs

Choose a tag to compare

@cskwork cskwork released this 16 Sep 18:34
59eec5f

The existing six-stage SDLC can now be driven by an automation host through a verified feature-branch handoff. Human merge and deployment decisions remain separate.

  • Machine-readable status and resumable, bounded execution checkpoints.
  • Full-auto intent checks that stop on unresolved material questions.
  • Source-bound verification recipes, command and log checks, execution timeouts, and owned-process cleanup.
  • Feature-branch push and remote SHA confirmation before review-ready handoff.
  • Updated English/Korean guides and a Symphony integration example.

Existing manual workflows remain supported. Python 3 is required for the optional bounded verification runner. Hashes detect changes; they do not authenticate reviewers or prove deployment.

Validation: existing gate suite, 141 end-to-end assertions, 183 automation assertions, and local Symphony integration tests. Cross-platform CI results are checked before this release is published.

v0.9.0 — verifiable feature and bug-fix delivery

Choose a tag to compare

@cskwork cskwork released this 15 Sep 02:08
ef49755

Feature development and bug fixing

  • Ordinary development requests enter SDLC Kit, using one compact route for bounded changes and the full route for complex work.
  • Approval records bind the exact artifact, upstream decisions, and reviewed source. Stale approvals, unsupported source paths, and mismatched delivery commits are rejected.
  • QA requires scoped real E2E with the command/tool, environment, scenario, and observed result. Unit tests cannot silently substitute for E2E.
  • Requirements, final evidence, and delivery summaries remain durable. Repeated approval requests, mandatory delegation at every stage, duplicate test runs, and immediate evidence deletion are reduced.
  • Adds a reusable E2E suite and fixes Windows CI fixture setup. PR, main, and version-tag checks remain enabled.

Upgrade notes

  • Existing unbound approval records require reapproval. Track: micro remains accepted as the compact route.
  • Run init.sh and review its changes to kit-owned ignore entries; it does not modify the Git index.
  • Hashes detect changes; they do not authenticate approvals or prove remote delivery.
  • Whole-project source hashing can be slow in large repositories. Git-quoted special path names are rejected; submodule contents are outside the source snapshot.

Validation and release exception

  • Local macOS/Bash 3.2: 35 selftests and 133 E2E assertions passed, plus shell syntax and diff checks.
  • Independent review: no remaining blockers in the reviewed local implementation.
  • PR CI: Ubuntu and macOS passed. Windows selftests passed, but Windows E2E did not pass.
  • The repository owner explicitly requested skipping the remaining E2E gate and merging/releasing. Windows' combined required check was temporarily excluded only for PR #1's merge and immediately restored. All three required checks, administrator enforcement, and force-push/deletion protections are active again.
  • Windows E2E remains unresolved in this release; this release does not claim that every platform's E2E passed.

Merged through PR #1, commit ef49755886cc2319c2c1acf092d9175a69108a81.

v0.8.0 — evidence and approvals stay on disk

Choose a tag to compare

@cskwork cskwork released this 01 Sep 05:17

What git keeps is now the decision record

init.sh writes sixteen .gitignore lines instead of four. Per feature, git
keeps intent.md, plan.md, map.md, CLOSED, memory/, and config.md.
Everything else is evidence or working residue and stays in the working copy:
approvals/, spec.md, baseline.txt, deviations.md, evidence.md, and
harvest.md, alongside the existing scratch/ and progress.md. Each is
listed for work/ and archive/ alike, so a close does not resurrect them.

  • Gates are unaffected. check-gate.sh, status.sh, and stats.sh read
    from disk, so gate state, approval modes, and re-approval counts behave
    exactly as before. stats.sh makes no git calls at all — its header claimed
    otherwise, and that comment is now correct too.
  • Durability is what changes. A fresh clone carries no approvals, so
    re-cloning mid-feature means approving again. The audit trail is the on-disk
    .sdlc/approvals/ tree plus the agent rules. AGENTS.md rules 3 and 7, both
    READMEs, and the docs site no longer claim git history holds it.
  • Ship stages less. skills/5-ship drops .sdlc/approvals/ from the
    staged set; staging .sdlc/work/<slug>/ now yields only intent, plan, and
    map. Anything else showing up there means the project predates this release.

Upgrading an existing project

.gitignore never untracks. Re-run init.sh in the project — it reports
every already-tracked path and prints the removal command:

<kit>/init.sh .
git ls-files -ci --exclude-standard -- .sdlc | tr '\n' '\0' | xargs -0 git rm --cached --

Files stay on disk; commit the removal to stop carrying them. status.sh's
kit-drift note points at this, and close.sh stages the approvals deletion
while those paths are still tracked.

gates/selftest.sh gains test 28: the ignore set, the kept decision record,
idempotency, and the already-tracked warning.

v0.7.4 — progress.md heartbeat: one live line per stage

Choose a tag to compare

@cskwork cskwork released this 30 Aug 17:47

Heartbeat (AGENTS.md rule 9)

.sdlc/work/<slug>/progress.md now holds exactly one overwritten line —
<stage>[ n/m] · <what is happening> · <ISO timestamp> — so a human can see
where the loop is at any moment, in every stage, not just at gates.

  • Every stage writes it: on entry, at every sub-task change, before each
    dispatch; build counts plan.md's Order of work as build n/m and updates
    per fix-loop round.
  • Long sub-tasks refresh it at natural checkpoints (a command finished,
    a file edited) even when the text does not change — a stale heartbeat means
    a dead loop, not a slow step.
  • status.sh shows it as a now → line with its age (BSD/GNU stat);
    older than 30 min gets — STALE. Absent file: silent, fully
    backward-compatible.
  • A signal, not a record: init.sh gitignores it under work/ and
    archive/; history stays in deviations.md / evidence.md / harvest.md.
    Follow it live with watch -n5 cat .sdlc/work/<slug>/progress.md.

Also retro-tagged v0.7.3 (optional qa: tool in config, intent Refs: line).