Skip to content

Commit 8bbaf42

Browse files
iOSDevSKclaude
andcommitted
gamma.2: changes after delivery never point at starting over; changes.json counts sinceZip (from 5fdfbbc)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xf7wTz9XZFZ1GCWzDzHaWW
1 parent 1d1d630 commit 8bbaf42

6 files changed

Lines changed: 46 additions & 24 deletions

File tree

‎skills/html2wp/SKILL.md‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -568,19 +568,19 @@ rows repaired. No password, token or private path.
568568
**Once the project is delivered** (`{workspace}/result.json` says `status:
569569
"delivered"`), every change the owner asks for — "make that heading italic",
570570
"swap the footer text", "tighten the hero's spacing" — is made in the
571-
installed theme and applied to the running preview. It is never a new build:
572-
no prerender, no Astro build, no service conversion, no stage, no
573-
`progress.sh mode`. `progress.sh` refuses a stage start and a `mode` without
574-
`--new` on a delivered project (exit 3) — that refusal is the rule. A rebuild
575-
from the source is the owner's **Rebuild** button, never a chat change.
571+
installed theme and applied to the running preview. The owner may knowingly
572+
make the site differ from the original; that is their site now. It is never a
573+
new build: no prerender, no Astro build, no service conversion, no stage, no
574+
`progress.sh mode`. `progress.sh` refuses a stage start and a `mode` on a
575+
delivered project (exit 3) — that refusal is the rule.
576576

577577
1. **Edit only the theme's own files**, under `{workspace}/theme/<slug>/`:
578578
`templates/*.html` and `parts/*.html` (the layout, the header and footer),
579579
the site's CSS, scripts and images in its assets, and
580580
`clara-content/sources/<key>.html` (a page's content as the importer stores
581581
it). Never `source/`, `static-src/`, `astro-project/` or the manifest —
582-
those are the build's, and the next Rebuild regenerates the theme from
583-
them. Keep the change to what was asked.
582+
those are the conversion's inputs, and a change there reaches nothing the
583+
owner sees. Keep the change to what was asked.
584584
2. **Apply it:**
585585

586586
```
@@ -606,10 +606,10 @@ from the source is the owner's **Rebuild** button, never a chat change.
606606
importer never overwrites an owner's edit — so a change to that page's source
607607
does not reach it. Say so; the owner makes it in Visual Edit Lite.
608608

609-
**A request the live theme cannot take** — a new page or route, behaviour that
610-
lives in the source app's code, content only the source's data has — needs a
611-
Rebuild from the source. Say exactly that, and that a Rebuild regenerates the
612-
theme and drops the changes made after delivery. Do not rebuild yourself.
609+
**What the theme files cannot change**, say plainly — which part of the
610+
request, and why (for example: the posts' own text lives in WordPress, where
611+
the owner edits it in Visual Edit Lite) — and make every part they can. Never
612+
offer starting over or another build as a way to apply a change.
613613

614614
## Stage -3 — can this machine run it at all?
615615

@@ -2833,9 +2833,9 @@ hunting for a deleted `static-src`.
28332833

28342834
## Post-handover repairs — run only what the fix touches
28352835

2836-
*A change the owner asks for after delivery is "Changes after delivery"
2837-
(above), in the live theme. What follows is for a repair only a rebuild can
2838-
make, and only when the owner asked for the Rebuild.*
2836+
*After delivery, every change the owner asks for is made in the live theme
2837+
("Changes after delivery", above). What follows belongs to a run that has not
2838+
been delivered, or to a new run the owner started over from the original.*
28392839

28402840
**The theme is yours to change. The editor plugin is not. Ever.**
28412841

‎skills/html2wp/assets/scripts/apply-change.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ def main(argv=None):
165165
log["changes"].append(entry)
166166
last = ts.zip_tree(zipped) if zipped else {}
167167
log["changedSinceZip"] = bool(ts.differ(last, current))
168+
# How many applied changes the last ZIP does not have yet.
169+
since = (log.get("lastZip") or {}).get("at") or ""
170+
log["sinceZip"] = sum(1 for c in log["changes"] if c.get("applied") and c.get("at", "") > since)
168171
ts.write_json(ws / "changes.json", log)
169172
print(json.dumps({k: entry[k] for k in ("id", "applied", "files", "pages", "screenshots", "seconds") if k in entry}))
170173
return 0 if entry["applied"] else 1

‎skills/html2wp/assets/scripts/lib/theme_state.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,5 @@ def make_zip(theme, out, manifest_path):
111111
def load_changes(ws):
112112
doc = read(Path(ws) / "changes.json")
113113
if not isinstance(doc, dict) or doc.get("schema") != CHANGES_SCHEMA:
114-
doc = {"schema": CHANGES_SCHEMA, "changedSinceZip": False, "lastZip": None, "changes": []}
114+
doc = {"schema": CHANGES_SCHEMA, "changedSinceZip": False, "sinceZip": 0, "lastZip": None, "changes": []}
115115
return doc

‎skills/html2wp/assets/scripts/package-theme.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def main(argv=None):
5858
file=sys.stderr)
5959
return 1
6060
if previous and ts.zip_tree(previous) == ts.zip_tree(candidate):
61-
log["changedSinceZip"] = False
61+
log.update(changedSinceZip=False, sinceZip=0)
6262
ts.write_json(ws / "changes.json", log)
6363
theme_row = result.get("theme") or {}
6464
print(json.dumps({"file": theme_row.get("file"), "sha256": theme_row.get("sha256"),
@@ -75,7 +75,8 @@ def main(argv=None):
7575
result.update(theme=theme_row, revision=revision, packagedAt=ts.now(),
7676
checkedRevision=int(result.get("checkedRevision") or 1),
7777
changesSinceChecks=len([c for c in log["changes"] if c.get("applied")]))
78-
log.update(changedSinceZip=False, lastZip={"file": name, "revision": revision, "at": result["packagedAt"]})
78+
log.update(changedSinceZip=False, sinceZip=0,
79+
lastZip={"file": name, "revision": revision, "at": result["packagedAt"]})
7980
ts.write_json(ws / "changes.json", log)
8081
# The workspace copy first, the output's last: its presence is what a UI reads.
8182
ts.write_json(ws / "result.json", result)

‎skills/html2wp/assets/scripts/progress.sh‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ run_mode() {
158158

159159
# A delivered project ({workspace}/result.json status "delivered"): after
160160
# delivery a change is made in the live theme (apply-change.py), never as a
161-
# new build, so no stage starts and `mode` needs --new — the app's Rebuild.
161+
# new build, so no stage starts. Only a new run the owner starts over from the
162+
# original (the app's goal says `mode <m> --new`) passes.
162163
delivered_here() {
163164
local ws
164165
ws="$(timing_workspace)"
@@ -173,9 +174,8 @@ PY
173174
}
174175

175176
refuse_delivered() {
176-
printf '\n This project was delivered: a change is made in the live theme — edit its files, then\n' >&2
177-
printf ' apply-change.py (SKILL.md, "Changes after delivery"); no stage starts. A rebuild from the\n' >&2
178-
printf ' source is the owner'"'"'s Rebuild, never a chat change: progress.sh mode <flash|full> --new.\n' >&2
177+
printf '\n This project was delivered: every change is made in the live theme — edit its files,\n' >&2
178+
printf ' then apply-change.py (SKILL.md, "Changes after delivery"). No stage starts.\n' >&2
179179
exit 3
180180
}
181181

@@ -315,8 +315,9 @@ PY
315315
PF="${H2WP_PROGRESS_FILE:-$WS_NOW/progress.json}"
316316
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
317317
[ -f "$PF" ] && mv "$PF" "${PF%.json}-$STAMP.json"
318-
# A rebuild over a delivered project: its result and its change log are
319-
# the last run's — kept beside, never read as this run's.
318+
# A new run over a delivered project (the owner started over from the
319+
# original): its result and its change log are the last run's — kept
320+
# beside, never read as this run's.
320321
for f in result.json changes.json .theme-applied.json; do
321322
[ -f "$WS_NOW/$f" ] && mv "$WS_NOW/$f" "$WS_NOW/${f%.json}-$STAMP.json"
322323
done

‎skills/html2wp/assets/scripts/test-change-mode.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,22 @@ def test_a_change_is_made_in_the_theme_and_logged_without_a_stage(self):
8787
log = json.loads((self.ws / 'changes.json').read_text())
8888
self.assertEqual(log['schema'], 'h2wp-changes/1')
8989
self.assertTrue(log['changedSinceZip'])
90+
self.assertEqual(log['sinceZip'], 1)
9091
entry = log['changes'][-1]
9192
self.assertEqual((entry['what'], entry['files'], entry['applied']),
9293
('make the heading italic', ['clara-content/sources/front-page.html'], True))
9394
self.assertEqual((self.ws / 'progress.json').read_text(), before, 'no stage started')
9495
self.assertEqual(self.apply().returncode, 3, 'applied: nothing new to apply')
9596

9697
def test_the_guard_a_delivered_project_starts_no_stage(self):
98+
before = (self.ws / 'progress.json').read_text()
9799
for args in (('start', '-1'), ('start', '0'), ('mode', 'flash'), ('mode', 'full')):
98100
done = run('bash', HERE / 'progress.sh', *args, env=self.env)
99101
self.assertEqual(done.returncode, 3, args)
100102
self.assertIn('apply-change.py', done.stderr)
103+
# The owner's rule: a change never points at starting over.
104+
self.assertNotRegex(done.stderr.lower(), r'rebuild|--new|start over')
105+
self.assertEqual((self.ws / 'progress.json').read_text(), before, 'a refusal writes nothing: still delivered')
101106
rebuild = run('bash', HERE / 'progress.sh', 'mode', 'flash', '--new', env=self.env)
102107
self.assertEqual(rebuild.returncode, 0, rebuild.stderr)
103108
self.assertFalse((self.ws / 'result.json').exists())
@@ -122,10 +127,22 @@ def test_get_zip_packages_the_live_theme_as_the_next_revision(self):
122127
result = json.loads((folder / 'result.json').read_text())
123128
self.assertEqual((result['theme']['file'], result['revision'], result['checkedRevision'], result['status']),
124129
('studio-1.0.0-r2.zip', 2, 1, 'delivered'))
125-
self.assertFalse(json.loads((self.ws / 'changes.json').read_text())['changedSinceZip'])
130+
self.assertEqual({k: json.loads((self.ws / 'changes.json').read_text())[k] for k in ('changedSinceZip', 'sinceZip')},
131+
{'changedSinceZip': False, 'sinceZip': 0})
126132
again = package()
127133
self.assertEqual((json.loads(again.stdout)['reused'], json.loads(again.stdout)['file']), (True, 'studio-1.0.0-r2.zip'))
128134

135+
def test_the_change_brief_never_offers_starting_over(self):
136+
# After delivery every change goes into the live theme. What the theme
137+
# cannot change is said plainly — never "Rebuild", never a new run.
138+
skill = (HERE.parent.parent / 'SKILL.md').read_text()
139+
start = skill.index('## Changes after delivery')
140+
section = skill[start:skill.index('\n## ', start + 3)]
141+
self.assertIn('apply-change.py', section)
142+
self.assertNotRegex(section.lower(), r'rebuild|--new|start over|mode <')
143+
for script in ('apply-change.py', 'package-theme.py'):
144+
self.assertNotRegex((HERE / script).read_text().lower(), r'rebuild|start over', script)
145+
129146
def test_not_a_delivered_project(self):
130147
(self.ws / 'result.json').write_text(json.dumps({'status': 'stopped'}))
131148
self.assertEqual(self.apply().returncode, 2)

0 commit comments

Comments
 (0)