@zalom/plastic 2.0.0-alpha.11 → 2.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,7 +31,9 @@ deliberately; the auto pipeline never dispatches them.
31
31
  `plastic-intent-executing`'s `plan-reviewer-prompt.md`; fold every finding into the spec, the
32
32
  matrix, and the tests.
33
33
  4. **Dispatch one executor, tests first** - the executor commits the matrix's tests red, then
34
- builds, then drives the full suite green; you verify the checklist against the diff.
34
+ builds, then drives the full suite green; you verify tick-versus-diff at the
35
+ post-execution review and again before the merge. A mismatch is a review finding, not a
36
+ cleanup you perform silently.
35
37
  5. **Review by risk** - dispatch the post-execution reviewer only when the auto skill's risk
36
38
  rule fires; otherwise the green suite is the review.
37
39
  6. **Close** - `outcome.md`, then `plastic-intent-ending`, which releases the worktree, clears
@@ -20,7 +20,11 @@ valid lifecycle artifacts. Honor it as your live state; do not re-derive or cont
20
20
  any other change.
21
21
  2. **Implement the actions** - make the code changes for each action in order, inside the
22
22
  worktree the preamble names.
23
- 3. **Track progress** - check off `checklist.md` items as they complete.
23
+ 3. **Tick with the commit** - commit after each logical unit of work, and in the same step
24
+ tick the checklist item that unit lands: mark its box `[x]` and move the line to
25
+ `## Completed`, then append the savepoint `Commit` line
26
+ (`scripts/savepoint-note <intent_dir> --kind Commit --text "<sha> <what it proves>"`). A
27
+ commit without its tick is incomplete.
24
28
  4. **Record insights** - capture durable discoveries and report them in the `insights:` field;
25
29
  persist each to `## Insights` via the `insight-append` helper
26
30
  (`scripts/insight-append <intent_dir> <text> --stage Exec --author "plastic-executor (autonomous)"`),
@@ -33,7 +37,8 @@ valid lifecycle artifacts. Honor it as your live state; do not re-derive or cont
33
37
  real `ACTION_N.md` with its failure-mode matrix, pasted in by the lead. Execute the action
34
38
  files in order.
35
39
  2. Write the matrix's tests; commit red.
36
- 3. Work one action at a time, preferring safe, non-destructive routes.
40
+ 3. Work one action at a time, ticking its checklist item in the same commit that lands it
41
+ (box marked, line moved); prefer safe, non-destructive routes.
37
42
  4. Run the full suite, iterate to zero failures and zero errors; commit green.
38
43
  5. Produce (output handoff): the code changes, a checked-off `checklist.md`, and `## Insights`.
39
44
  6. Report (see `## Completion Report`); the lead applies the risk rule and may dispatch a
@@ -46,7 +51,8 @@ preamble's `REPORT_CONTRACT` and `skills/auto/references/agent-report-contract.m
46
51
  finish silently. Carry the common envelope (role, intent id, stage, status, artifacts written,
47
52
  verification, checklist deltas, deviations, blockers, insights) plus the executor payload:
48
53
 
49
- - Actions implemented this turn, mapped to checklist items checked off (checked / total)
54
+ - Actions implemented this turn, mapped to checklist items checked off (checked / total);
55
+ checked / total must equal the items whose commits exist
50
56
  - A summary of the code changed (files and the shape of the change)
51
57
  - Test result: the red commit's failing count, then the full-suite command and its pass / fail
52
58
  counts
package/hooks/statusline CHANGED
@@ -2,8 +2,8 @@
2
2
  # plastic-hook-version: 4.0.0
3
3
  # StatusLine hook - Plastic owns the full statusline (no chaining).
4
4
  # Renders: {model} - ctx {pct}% ({used}/{size}) - 5h {p}% - 7d {p}% - ${cost}
5
- # - claudish {n} rw / {tok} - Plastic {version}[ -> {next}] - {path}
6
- # Every segment but claudish and the Plastic version comes from the stdin payload.
5
+ # - Plastic {version}[ -> {next}] - {path}
6
+ # Every segment but the Plastic version comes from the stdin payload.
7
7
  # Pure bash (macOS 3.2 compatible). No ruby, no jq. Reads stdin once; small file reads
8
8
  # only. The stdin JSON is walked in a single awk pass (below); nothing else in this
9
9
  # script forks a second process per field, which is what keeps a render cheap.
@@ -17,7 +17,6 @@ C_OK='' # default - meter below 70%
17
17
  C_WARN=$'\033[33m' # yellow - meter at 70% and above
18
18
  C_CRIT=$'\033[38;2;231;76;60m' # red - meter at 90% and above
19
19
  C_COST=$'\033[37m' # white - session cost
20
- C_CLAUDISH=$'\033[38;2;249;140;30m' # orange - claudish rewrites
21
20
  C_VER=$'\033[37m' # white - "Plastic {version}" and update arrow
22
21
  C_NEXT=$'\033[33m' # yellow - next version
23
22
  C_PATH=$'\033[38;2;128;134;144m' # gray - path (matches dim UI text)
@@ -309,40 +308,18 @@ if [ -n "$COST_RAW" ]; then
309
308
  [ -n "$COST_FMT" ] && [ "$COST_FMT" != "0.00" ] && COST_SEG="${C_COST}\$${COST_FMT}${RST}"
310
309
  fi
311
310
 
312
- # --- claudish rewrites for THIS session (fork ledger, optional) ---
313
- # Tab separated, one call per line; column 11 is the session id. Rows written
314
- # before that column existed have 9 or 10 fields and never match, so an old
315
- # ledger renders no segment instead of a wrong one.
316
- CL_SEG=""
317
- CL_LOG="${CLAUDISH_LOCAL_DIR:-$HOME/.claude/claudish-local}/usage.log"
318
- if [ -n "$SID" ] && [ -f "$CL_LOG" ]; then
319
- CL=$(awk -F'\t' -v sid="$SID" '
320
- NF >= 11 && $11 == sid { n++; t += $5 + $6 }
321
- END {
322
- if (n > 0) {
323
- if (t >= 1000) {
324
- s = sprintf("%.1f", t / 1000); sub(/\.0$/, "", s); printf "%d %sk", n, s
325
- } else {
326
- printf "%d %d", n, t
327
- }
328
- }
329
- }' "$CL_LOG" 2>/dev/null)
330
- [ -n "$CL" ] && CL_SEG="${C_CLAUDISH}claudish ${CL%% *} rw / ${CL#* }${RST}"
331
- fi
332
-
333
311
  # --- Assemble segments in order, joined by " . " ---
334
312
  PARTS=""
335
313
  add() { [ -z "$1" ] && return; if [ -z "$PARTS" ]; then PARTS="$1"; else PARTS="$PARTS$SEP$1"; fi; }
336
314
 
337
- # Order: identity first (model), then this session's spend (context, meters, cost,
338
- # rewrites), then the volatile-length values last (version, path) so the line does
315
+ # Order: identity first (model), then this session's spend (context, meters, cost),
316
+ # then the volatile-length values last (version, path) so the line does
339
317
  # not visually jump as they change.
340
318
  [ -n "$MODEL" ] && add "${C_MODEL}${MODEL}${RST}"
341
319
  add "$CTX_SEG"
342
320
  add "$M5"
343
321
  add "$M7"
344
322
  add "$COST_SEG"
345
- add "$CL_SEG"
346
323
  if [ -n "$VERSION" ]; then
347
324
  vseg="${C_VER}Plastic ${VERSION}${RST}"
348
325
  [ -n "$NEXT" ] && vseg="${vseg}${C_VER} "$'\342\206\222'" ${RST}${C_NEXT}${NEXT}${RST}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalom/plastic",
3
- "version": "2.0.0-alpha.11",
3
+ "version": "2.0.0-alpha.12",
4
4
  "description": "Intent-driven idea development system for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/scripts/doctor.rb CHANGED
@@ -30,6 +30,7 @@ require_relative "lib/power_tools"
30
30
  require_relative "lib/preflight"
31
31
  require_relative "lib/ruby_probe"
32
32
  require_relative "lib/doctor_session_ledger"
33
+ require_relative "lib/intent_screen"
33
34
 
34
35
  # Diagnostic engine, instantiable with an injected store/agent map so tests can
35
36
  # run it hermetically (no eval, no global-constant rewriting).
@@ -1161,9 +1162,11 @@ end
1161
1162
  #
1162
1163
  # Doctor's fourth check scope, invoked by `--intent <id>`. Never a store-wide sweep:
1163
1164
  # resolves exactly one intent directory (mirrors scripts/end-intent's resolve_intent_dir /
1164
- # scripts/project-links's --intent disambiguation) and returns five checks, four
1165
- # FAIL-severity, one WARN-severity (intent_savepoint_truthful stays WARN per intent 134's
1166
- # binding advisory-only ruling; see spec.md D2/D8 - do NOT escalate it to FAIL).
1165
+ # scripts/project-links's --intent disambiguation) and returns six checks, four
1166
+ # FAIL-severity, two WARN-severity: intent_savepoint_truthful stays WARN per intent 134's
1167
+ # binding advisory-only ruling (see spec.md D2/D8 - do NOT escalate it to FAIL), and
1168
+ # intent_ticks_lag is WARN-only per intent 329's ruling that a lagging tick warns rather
1169
+ # than blocks.
1167
1170
  def check_intent_end(id, store: nil, disposition: nil)
1168
1171
  intent_dir, scope = resolve_single_intent_dir(id, store: store)
1169
1172
  unless intent_dir
@@ -1185,6 +1188,7 @@ end
1185
1188
  intent_structure_check(intent_dir),
1186
1189
  intent_lifecycle_artifacts_check(intent_dir, disposition),
1187
1190
  intent_checklist_complete_check(intent_dir),
1191
+ intent_ticks_lag_check(intent_dir),
1188
1192
  intent_links_projection_check_for(id, scope),
1189
1193
  intent_savepoint_truthful_check(intent_dir, index_path: index_path),
1190
1194
  ]
@@ -1263,6 +1267,45 @@ end
1263
1267
  end
1264
1268
  end
1265
1269
 
1270
+ # Intent 329: the checklist is the only input to the state screen's Progress bar, so an
1271
+ # intent whose commit ledger has entries and whose checklist has no ticked item is
1272
+ # reporting 0 progress on work that already landed. Advisory only (WARN), like
1273
+ # intent_savepoint_truthful: a lagging tick is a lead's review finding, never a machine
1274
+ # refusal. The commit count comes from this intent's own savepoint.md Commit ledger
1275
+ # (intent 317 D17), never from git: git's detected base is the repo default branch, which
1276
+ # is not the base a 2.0 intent branch forks from.
1277
+ def intent_ticks_lag_check(intent_dir)
1278
+ items = IntentScreen.checklist_items(intent_dir)
1279
+ if items.empty?
1280
+ return check(category: "intent_end", name: "intent_ticks_lag", status: "pass",
1281
+ message: "n/a: checklist.md has no items to tick")
1282
+ end
1283
+
1284
+ ticked = items.count { |i| i[:done] }
1285
+ commits = savepoint_commit_count(intent_dir)
1286
+
1287
+ if commits.positive? && ticked.zero?
1288
+ check(category: "intent_end", name: "intent_ticks_lag", status: "warn",
1289
+ message: "ticks lag the branch: #{commits} commit(s) recorded, " \
1290
+ "0 of #{items.size} checklist items ticked")
1291
+ else
1292
+ check(category: "intent_end", name: "intent_ticks_lag", status: "pass",
1293
+ message: "#{ticked} of #{items.size} ticked, #{commits} commit(s) recorded")
1294
+ end
1295
+ end
1296
+
1297
+ # Count `Commit` lines in the intent's savepoint ledger, through the one regex that parses
1298
+ # a savepoint line (IntentScreen::SAVEPOINT_RE; field 2 is the kind).
1299
+ def savepoint_commit_count(intent_dir)
1300
+ path = File.join(intent_dir, "savepoint.md")
1301
+ return 0 unless File.exist?(path)
1302
+
1303
+ File.readlines(path).count do |line|
1304
+ m = line.strip.match(IntentScreen::SAVEPOINT_RE)
1305
+ m && m[2] == "Commit"
1306
+ end
1307
+ end
1308
+
1266
1309
  def intent_links_projection_check_for(id, scope)
1267
1310
  ctx = build_links_projection_context
1268
1311
  node = ctx[:intents].find { |n| n[:id] == id && n[:scope] == scope }
@@ -15,7 +15,8 @@
15
15
  # other flags exist.
16
16
  #
17
17
  # Checks, all run every invocation (the fourth only when --suite is given):
18
- # 1. doctor - Doctor#run_intent_check(id, store: scope), fail-open on a crash
18
+ # 1. doctor - Doctor#run_intent_check(id, store: scope), fail-open on a crash; the doctor
19
+ # scan includes the intent_ticks_lag warning (intent 329)
19
20
  # 2. em-dash - added lines only of `git diff <base>...HEAD`, never the whole tree
20
21
  # 3. diffstat - `git diff --stat <base>...HEAD`, never a failure on its own
21
22
  # 4. suite - the supplied --suite command, run with RUBYOPT cleared
@@ -217,7 +217,7 @@ Then Exec.
217
217
  then builds, then drives the full suite green.
218
218
  2. Read its return by code: DONE or DONE_WITH_CONCERNS proceeds; NEEDS_CONTEXT re-dispatches
219
219
  with the missing context; BLOCKED stops under the error procedure.
220
- 3. Tick the checklist as items land (the executor does this; verify it).
220
+ 3. Tick the checklist as items land (the executor does this); verify tick-versus-diff against the diff. A mismatch is a review finding, not a lead cleanup.
221
221
 
222
222
  ## Review by risk (boot 3, only when a rule fires)
223
223
 
@@ -254,7 +254,7 @@ every choice is non-destructive and the team has full autonomy.
254
254
 
255
255
  Read `../plastic-conventions/references/completion-and-done.md` for what "intent done" means.
256
256
 
257
- 1. Verify every checklist item is checked and the suite is green once on the branch.
257
+ 1. This is the merge gate: verify every checklist item is checked, verify tick-versus-diff against the diff, and confirm the suite is green once on the branch.
258
258
  2. Write `outcome.md` from `~/.plastic/templates/outcome.md` with `disposition: delivered`,
259
259
  `## Delivered` as the labeled table whose row labels match the action-file headings (317a).
260
260
  3. Release, if configured: match the working directory against `~/.plastic/projects.yml`, read
@@ -108,11 +108,15 @@ Capture observations in `## Insights`. When ALL checklist items are checked:
108
108
 
109
109
  ## Tick-as-you-land
110
110
 
111
- As each task lands, in the same edit: move its checklist item from `## In
112
- Progress` to `## Completed` in `checklist.md`, and add one `## Session Log`
113
- row (Date, Items Completed, Notes). Do not batch several tasks' worth of
114
- checklist updates into one later edit; tick the moment the task is verified,
115
- before moving to the next task.
111
+ A tick is two edits, made together: mark the item's box `[x]`, and move its
112
+ checklist item from `## In Progress` to `## Completed` in `checklist.md`;
113
+ then add one `## Session Log` row (Date, Items Completed, Notes). The box is
114
+ the half the state screen's Progress bar reads: `IntentScreen::ITEM_RE` and
115
+ `progress_fields` count `[x]`, not which section the line sits in, so a line
116
+ moved to `## Completed` with its box left unmarked still reads as zero
117
+ progress. Do not batch several tasks' worth of checklist updates into one
118
+ later edit; tick the moment the task is verified, before moving to the next
119
+ task.
116
120
 
117
121
  ## Verify before every owner review
118
122
 
@@ -20,12 +20,17 @@ You are implementing a specific task from a plan. You have been given the full t
20
20
  2. Implement exactly what the task specifies — nothing more, nothing less.
21
21
  3. Write tests first when the task includes test steps (TDD).
22
22
  4. Follow the file paths specified in the task exactly.
23
- 5. Commit after each logical unit of work.
23
+ 5. Commit after each logical unit of work, and in the same step tick the checklist item that
24
+ unit lands: mark its box `[x]` and move the line to `## Completed`, then append the
25
+ savepoint `Commit` line
26
+ (`scripts/savepoint-note <intent_dir> --kind Commit --text "<sha> <what it proves>"`). A
27
+ commit without its tick is incomplete.
24
28
  6. When done, self-review against this checklist:
25
29
  - [ ] All steps in the task are completed
26
30
  - [ ] Tests pass
27
31
  - [ ] Code is clean and follows project conventions
28
32
  - [ ] No unrelated changes
33
+ - [ ] Every landed unit's checklist item is ticked
29
34
 
30
35
  ## Report Format
31
36