diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d7e1ea6..8bbbe4d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased +- process(change-intake): **a change request now updates the module brief and stays visible until it is built.** The rule "most BRD edits need no brief edit" let a CR on a closed module leave the brief's Build steps stale, so `context-pack.sh` had no row for it and nothing counted it. Now the BRD stays the source of truth, the brief gets a CR-tagged Build steps row for any CR touching build steps, screens, access or rules, every CR that adds work gets a build-plan row tagged `CR-NN` (S: the module's phase, M/L: a change round), and agreed unbuilt CRs are "waiting" and are surfaced at session start and module close. RESUME.md gains a `Waiting changes:` line. `exec.sh --cr CR-NN` WARNs (never blocks) when the brief of a module the script writes to does not mention the id, for the folder brief and the merged build-plan form. Skill word counts at or below master (baseline 75,204 to 75,203, budget 80,000). A full unplanned-changes view and change rounds come in a follow-up. Test: `tests/wave2/test-exec-cr-brief-warn.sh` (19 checks, 6 fail on master's exec.sh) — MendixMau - new(security): **`project-bin/check-staged-secrets.sh` — compare the project's real secret values (`.env`, `.mxcli/run-local.json` bootConfig) against the staged blobs (or `--tree`, every tracked file at HEAD) and print only the file and the key name, never a value (TK-025).** Plus a section in `learned-constants-and-secrets.md`: the secret-bearing files and commands an agent never prints (`run-local.json`, the `run --local` command line, run state, `describe constant`, `.env`, rendered files that interpolate a secret), read-by-testing habits, and "a leaked value is rotated, not deleted". Field evidence: a five-app lab run with six leak events into transcripts and a demo password in git (mxcli v0.26.0). Opt-in, not wired into the generated pre-commit hook. Test with made-up values: `tests/wave2/test-check-staged-secrets.sh` (15 checks). Not adopted: the report's `exec-secret.sh` is a project-local script that does not ship here, and masking `--constant` values in `app-restart.sh` is TK-003. Values under 8 characters, well-known defaults such as `mendix` (the mxcli database password, present in thousands of tracked files) and values equal to `.env.example` are not sources, and `.docker/.env` is read. It runs one `git grep -F -f` per value, not a process per file: 230 staged files in a 6,000-file synthetic repo took 1.0 s (the whole tree 2.6 s). — five-app lab report, TK-025 - fix(wire-agents): **a sync re-wire no longer wipes `.claude/settings.json`.** `mxcli init` overwrites that file; wire-agents now snapshots it and merges after init: the project's entries win and keep their order (permission profile, user entries, the `mxcli exec` deny rule, hooks), and entries that only mxcli's new file has are appended. Merged rather than added to PRESERVE because PRESERVE restores old bytes and would also drop anything a newer mxcli adds; an unparsable snapshot is restored byte for byte. Test: `tests/wave2/test-wire-agents-settings.sh` (4 of 6 checks fail on master). — review of #414 (#419) - fix(sync): **the exec-approval item-3 rewrite now works on BSD awk (stock macOS) and `sync-project.sh` no longer prints "Rewrote" for a rewrite that failed.** `_ea_rewrite_item3` passed the multi-line replacement through `awk -v`, which BSD awk rejects ("newline in string", exit 2); the file stayed unchanged and sync still reported the rewrite on every run. The replacement is now streamed from a temp file (as the Q9 repair does), the helper returns 2 when awk fails, and the caller prints a `WARN:` instead of "Rewrote" and does not count a change. `test-exec-approval.sh` gains 3 assertions (failing-awk shim: no "Rewrote", a WARN, file unchanged). No other multi-line `awk -v` call site exists (the rest are single-line values) ([#426](https://github.com/MendixMau/mxcli-project-toolkit/issues/426)) — MendixMau diff --git a/bin/lib/resume-template.sh b/bin/lib/resume-template.sh index 75c15bb8..26500053 100644 --- a/bin/lib/resume-template.sh +++ b/bin/lib/resume-template.sh @@ -64,6 +64,11 @@ resume doc is worse than none. Step 1 below is how to find out. 3. Overwrite this file before the session ends (\`bin/close-task.sh\` prints the checklist), so the next one does not have to repeat step 1. +## Waiting changes + +None recorded. A closing session lists the agreed change requests that are not built yet, as +\`Waiting changes: CR-07, CR-09\` (\`none\` when there are none), so the overwrite cannot drop them. + ## Do not lose Nothing recorded yet. This section is where a closing session puts the thing that would be diff --git a/claude-hooks/bin/close-task.sh b/claude-hooks/bin/close-task.sh index 79671b2a..667400c7 100755 --- a/claude-hooks/bin/close-task.sh +++ b/claude-hooks/bin/close-task.sh @@ -115,7 +115,8 @@ echo "### Then" echo " $_ck --write # and fill the Narrative (append-only history)" cat <<'TABLE' OVERWRITE docs/progress/RESUME.md # <= 60 lines: where we are, next actions, - # do-not-lose, and which file to read for what. + # Waiting changes: (agreed, unbuilt CR ids), do-not-lose, and + # which file to read for what. # This is the ONLY file read after /clear. git commit close-task.sh --done # empties the pending-writes file; PreCompact unblocks diff --git a/project-bin/close-task.sh b/project-bin/close-task.sh index 0afb1a8b..480a97c6 100755 --- a/project-bin/close-task.sh +++ b/project-bin/close-task.sh @@ -164,7 +164,8 @@ else fi cat <<'TABLE' OVERWRITE docs/progress/RESUME.md # <= 60 lines: where we are, next actions, - # do-not-lose, and which file to read for what. + # Waiting changes: (agreed, unbuilt CR ids), do-not-lose, and + # which file to read for what. # This is the ONLY file read after /clear. mxcli brain check -p # capture/promote never validate anchors; exit 1 = # a dead anchor. Fix or `brain drop` before commit. diff --git a/project-bin/exec.sh b/project-bin/exec.sh index 1236b755..352f44ea 100755 --- a/project-bin/exec.sh +++ b/project-bin/exec.sh @@ -814,7 +814,7 @@ fi # b) a "## Module brief - " heading in architecture/build-plan.md # # Platform and marketplace modules are skipped - this project does not author their briefs. -brief_missing="" +brief_missing=""; cr_unbriefed="" if [ "$PATCH_MODE" != "1" ] && [ -f "$SCRIPT" ] && [ -f "$PROJECT_ROOT/architecture/build-plan.md" ]; then # Comments FIRST, before any pattern match. A `-- ... CREATE MODULE errors if ...` line in a # real workshop script otherwise yielded a module named "errors" and would have demanded a @@ -859,12 +859,40 @@ if [ "$PATCH_MODE" != "1" ] && [ -f "$SCRIPT" ] && [ -f "$PROJECT_ROOT/architect case "$m" in System|Administration|Atlas_Core|Atlas_Web_Content|Atlas_UI_Resources|MxModelReflection |CommunityCommons|Encryption|NanoflowCommons|WebActions|DeepLink|MxTest) continue ;; esac - [ -f "$PROJECT_ROOT/architecture/modules/$m/module-brief.md" ] && continue - grep -qiE "^#+[[:space:]]*Module brief[[:space:]]*(—|-|:)[[:space:]]*$m[[:space:]]*$" \ - "$PROJECT_ROOT/architecture/build-plan.md" 2>/dev/null && continue + if [ -f "$PROJECT_ROOT/architecture/modules/$m/module-brief.md" ]; then + # CR advisory: a CR that touches this module should have a CR-tagged Build steps row. + if [ -n "$CR_ID" ] && ! grep -qE "$CR_ID([^0-9]|\$)" "$PROJECT_ROOT/architecture/modules/$m/module-brief.md" 2>/dev/null; then + cr_unbriefed="$cr_unbriefed $m" + fi + continue + fi + if grep -qiE "^#+[[:space:]]*Module brief[[:space:]]*(—|-|:)[[:space:]]*$m[[:space:]]*$" \ + "$PROJECT_ROOT/architecture/build-plan.md" 2>/dev/null; then + if [ -n "$CR_ID" ]; then + # Merged form: look only inside that module's own section (to the next "## " heading). + # LC_ALL=C: macOS awk exits 2 ("towc: multibyte conversion failure") on an em dash heading + # under a UTF-8 locale. Exit 1 = id not found (warn); any other code is a real awk error and + # its stderr stays visible. Every occurrence on a line is checked, not just the first. + _awk_rc=0 + LC_ALL=C awk -v m="$m" -v cr="$CR_ID" ' + /^#+[ \t]*[Mm]odule [Bb]rief/ { inb = 0; t = $0; sub(/^#+[ \t]*[Mm]odule [Bb]rief/, "", t); while (t != "" && substr(t, 1, 1) !~ /[A-Za-z0-9_]/) t = substr(t, 2); sub(/[ \t]+$/, "", t); if (tolower(t) == tolower(m)) inb = 1; next } + /^## / { inb = 0 } + inb { r = $0; while ((i = index(r, cr)) > 0) { c = substr(r, i + length(cr), 1); if (c !~ /[0-9]/) { found = 1; break }; r = substr(r, i + length(cr)) } } + END { exit found ? 0 : 1 }' "$PROJECT_ROOT/architecture/build-plan.md" || _awk_rc=$? + [ "$_awk_rc" -eq 0 ] || cr_unbriefed="$cr_unbriefed $m" + fi + continue + fi brief_missing="$brief_missing $m" done fi +if [ -n "$cr_unbriefed" ]; then + for m in $cr_unbriefed; do + echo "⚠ WARN: the brief of $m does not mention $CR_ID. Add a $CR_ID tagged Build steps row to it" + echo " (skills/change-intake-detail.md Step 4), so the change is specified and stays visible. Proceeding." + done + echo "" +fi if [ -n "$brief_missing" ]; then echo "⚠ No module brief for:$brief_missing — proceeding, but nothing has specified this module." echo "" diff --git a/skills/change-intake-detail.md b/skills/change-intake-detail.md index fca1c690..80beed98 100644 --- a/skills/change-intake-detail.md +++ b/skills/change-intake-detail.md @@ -1,10 +1,10 @@ # Change Intake — Detail: Why, Worked Example, Apply, Prove, Gate Check **Applies to:** any mxcli project with a `PROJECT.md` (migration, requirements-driven, greenfield, change an existing app), from Stage 2 onward. -**Purpose:** the on-demand half of `change-intake.md`. That file is the baseline core every lead and ba-agent session reads (trigger, row format, class → entry artifact, size → ask, owners, Done when). Read this one when you apply or prove a change request, when a class, size or status is unclear, or when the `Change log:` gate line needs explaining. -**Owns:** nothing on its own: `change-intake.md` owns the `## Change log` table in `PROJECT.md` and the classes, sizes and statuses. `conversion-runbook.md` §3b (drift-sync) is the BRD-flush step of this loop; `iterative-build-loop.md`, `close-the-loop.md` and `agent-roles.md` point at `change-intake.md`. +**Purpose:** the on-demand half of `change-intake.md`. That file is the baseline core every lead and ba-agent session reads. Read this one to apply or prove a change request, when a class, size or status is unclear, or to explain the `Change log:` gate line. +**Owns:** nothing: `change-intake.md` owns the `## Change log` table and the classes, sizes and statuses. `conversion-runbook.md` §3b (drift-sync) is this loop's BRD-flush step. -**Trigger:** any message from the user that asks for something to be different from what the artifacts say or what was built, typed, pasted, relayed ("the finance lead wants..."), or said while looking at a screenshot or the running app. Not a trigger: answering a question you asked at a gate (that is `interview-protocol.md`), or a finding from a review pass (that is `finding-disposition.md`). +**Trigger:** any message asking for something different from the artifacts or the build, typed, pasted, relayed or said over a screenshot. Not a trigger: a gate answer (`interview-protocol.md`) or a review finding (`finding-disposition.md`). --- @@ -100,7 +100,7 @@ A row the user rejects is kept, with Status `rejected — `. A rejected ## Step 4 — Apply: top down, as far down as needed 1. Edit the entry artifact (Step 2). -2. Cascade down only to layers that *restate* what changed. Module briefs point to the BRD and do not copy it (`module-brief.md`), so most BRD edits need no brief edit. If the change adds or retires a build step, add or edit that row in the build plan. +2. The BRD stays the source of truth. The module brief's `### Build steps` table is updated, with a CR-tagged row such as `CR-07-1`, for every CR that touches that module's build steps, screens, access or rules, so `context-pack.sh CR-07-1` finds it. Every CR that adds work also gets a build-plan row tagged `CR-NN`: **S** one row in the module's own phase, **M** and **L** wait for a change round (a follow-up adds the full "unplanned changes" view and change rounds). An agreed CR that is not built yet is *waiting*. Surface it at session start and at module close; never let it drop out of sight. 3. Then write the MDL and run it with `./bin/exec.sh --cr CR-NN