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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions bin/lib/resume-template.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion claude-hooks/bin/close-task.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion project-bin/close-task.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 <app.mpr> # capture/promote never validate anchors; exit 1 =
# a dead anchor. Fix or `brain drop` before commit.
Expand Down
36 changes: 32 additions & 4 deletions project-bin/exec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ fi
# b) a "## Module brief - <Module>" 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
Expand Down Expand Up @@ -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 ""
Expand Down
8 changes: 4 additions & 4 deletions skills/change-intake-detail.md
Original file line number Diff line number Diff line change
@@ -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`).

---

Expand Down Expand Up @@ -100,7 +100,7 @@ A row the user rejects is kept, with Status `rejected — <reason>`. 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 <Module> 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 <script>`. The tag lands in the BUILD-LOG detail cell, and that BUILD-LOG row is the evidence for Step 5.
4. Set the status to `applied` and fill Closed-by with the commit that holds the artifact edit.

Expand Down
40 changes: 20 additions & 20 deletions skills/change-intake.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
# Change Intake — Log Feedback Before You Build It

**Applies to:** any project with `PROJECT.md`, Stage 2 onward.
**Purpose:** log a mid-project change before any edit, then edit the highest artifact it alters; MDL last. Why, example, apply/prove, gate check: `change-intake-detail.md`.

**Trigger:** the user, or a relayed stakeholder, asks for anything to differ from artifacts or build. Not a gate answer or review finding.

Wrong: "Sure — adding it now" → MDL, no row (12 of 12 field changes).
**Applies to:** `PROJECT.md` projects, Stage 2 onward.
**Purpose:** log a change before any edit, then edit the highest artifact it alters; MDL last. `change-intake-detail.md`.
**Trigger:** the user or a relayed stakeholder wants anything to differ (not a gate answer or finding).

Wrong: "Sure, adding it now" → MDL, no row.
## Row first, in `PROJECT.md` `## Change log`

Columns: CR-NN (never reused), Date, Source, Verbatim ask (`\|` for a pipe), Class, Size, Entry artifact, Status, Closed-by. One ask, one row. Status: open → refined → agreed → applied → proven, or `rejected — reason` (kept).
Columns: CR-NN (never reused), Date, Source, Verbatim ask (`\|` for a pipe), Class, Size, Entry artifact, Status, Closed-by. One ask, one row; status: open → refined → agreed → applied → proven, or `rejected — reason`.

## Class → entry artifact (never the MDL)

- **requirement** → BRD.
- **decision** → new `CONFIRMED` row `supersedes D-NN`; old row's Status `SUPERSEDED by CR-NN`, text untouched.
- **structure** → `architecture/` or module brief.
- **decision** → new `CONFIRMED` row `supersedes D-NN`; old row `SUPERSEDED by CR-NN`, text untouched.
- **structure** → `architecture/` or brief.
- **visual** → wireframe while unbuilt; once built, before/after pair in `design/ui-reviews/`.
- **impl**, **bug** → regression journey (a bug's fails first).
- **impl**, **bug** → regression journey (failing first for a bug).

## Brief and waiting changes

BRD stays the source. A CR touching a module's build steps, screens, access or rules gets a `CR-07-1` row in that brief's Build steps and a build-plan row `CR-NN` (S: module's phase; M/L: change round). Agreed, unbuilt CRs are *waiting*: say so at session start and module close.

## Size → ask (ask, then stop)

- **S** (one artifact per layer, one module, contradicts nothing `CONFIRMED`), user-typed: no question; receipt `Logged CR-07: visual, S → wireframe`, `agreed`, lead edits.
- **M** (several artifacts or modules): restate (`refined`); one batched question at the module gate, one ba-agent pass.
- **L** (new feature or module, or contradicts `CONFIRMED`): ask now (two options, a recommendation); re-enter the owning stage.
- **Relayed**: `open` until the next gate asks. **Ambiguous**: one "A or B?".
- **S** (one artifact per layer, one module, no `CONFIRMED` clash), user-typed: no question, receipt `Logged CR-07: S → wireframe`, `agreed`, lead edits.
- **M** (several artifacts or modules): restate (`refined`); one batched gate question, one ba-agent pass.
- **L** (new feature or module, or contradicts `CONFIRMED`): ask now (two options, one recommended); re-enter that stage.
- **Relayed**: `open` until a gate asks. **Ambiguous**: one "A or B?".

## Owners (single source)
## Owners

Lead: all CR/decision rows, S edits; helpers never write `PROJECT.md`. ba-agent: BRDs, briefs. architect-agent: wireframes, `design/`, `architecture/`. When the lead edits inline, no `UNSYNCED` marker is needed; use one only for a deferred flush.
Lead: CR/decision rows, S edits; helpers never write `PROJECT.md`. ba-agent: BRDs, briefs. architect-agent: wireframes, `design/`, `architecture/`.

## Done when

- N asks → N rows.
- N asks → N rows; decisions superseded, not edited.
- Every CR script ran via `./bin/exec.sh --cr CR-NN`.
- At a module gate, `Change log:` reads `PASS`, or each listed row is named with a reason.
- Zero "no Closed-by evidence" rows.
- Decisions superseded, not edited.
- At a module gate, `Change log:` reads `PASS` or each listed row is named; none lack Closed-by evidence.
10 changes: 5 additions & 5 deletions skills/close-the-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ topics at once.
| What you have | Goes to | Test that it belongs there |
|---|---|---|
| A script was executed against the `.mpr` | `docs/BUILD-LOG.md` | Append-only. One entry per exec: what it did · exec result · mxbuild result · manual SP work after |
| A build-plan step is now done, blocked, or descoped | `architecture/build-plan.md` (+ the module's own plan) | Someone reading only the plan would otherwise redo it or skip it |
| A build-plan step is now done, blocked, or descoped | `architecture/build-plan.md` (+ the module's own plan) | A plan-only reader would redo or skip it |
| A gate answer — stage, entry mode, `CONFIRMED`/`ASSUMED`, waiver, opt-in | `PROJECT.md` | `gate-check.sh` and `checkpoint.sh` read it |
| Feedback or a change the user asked for — typed, relayed from a stakeholder, or said over a screenshot | `PROJECT.md` → `## Change log`, before any edit (`change-intake.md`) | `gate-check.sh` counts the rows not yet applied or proven |
| A decision a session learned — a pattern chosen and why, a disposition, an open question outside a gate, a slice requirement | `mxcli brain capture … -a @Module.Element`, then `promote` | Without the *why*, a future session could reasonably decide the opposite. Anchors into the model; `brain check` fails when they stop resolving, which `PROJECT.md` rows never do |
Expand Down Expand Up @@ -150,10 +150,10 @@ So keep two:
| `docs/progress/checkpoints.md` | Append-only, unbounded | Auditing why a past claim was made. **Never to resume** |
| `docs/progress/RESUME.md` | **Overwritten**, hard cap ~60 lines | Always, and first, after `/clear` |

`RESUME.md` holds four things and nothing else: where we are (3–4 lines), the next actions in
order, do-not-lose, and a **pointer table** — "read this larger file only if you are doing that
specific thing." Pointers, not content. The whole value is that it replaces a 31k read with a
1.5k one.
`RESUME.md` holds five things and nothing else: where we are (3–4 lines), the next actions in
order, `Waiting changes:` (agreed, unbuilt CR ids), do-not-lose, and a
**pointer table** — "read this larger file only if you are doing that specific thing." Pointers, not content. It replaces a 31k read
with a 1.5k one.

Wire it by putting the instruction in the project's `CLAUDE.md`, which is auto-loaded: *resuming?
read `RESUME.md` and nothing else yet.* A resume convention that lives anywhere else is a
Expand Down
Loading
Loading