Skip to content

Commit 2f44751

Browse files
authored
Merge pull request #36 from pgsty/codex/review-followups
fix: close post-1.0 review defects
2 parents 5658363 + 6a7c95d commit 2f44751

95 files changed

Lines changed: 1143 additions & 968 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,12 @@ jobs:
310310
run: python3 -m unittest discover -s tests/migrations -t .
311311

312312
- name: Check output structure and security
313-
run: python3 bin/check-output.py
313+
run: |
314+
python3 tests/test_checker_inputs.py
315+
python3 bin/check-output.py --public tests/site/public
314316
315317
- name: Check the naming namespace
316-
run: python3 bin/check-namespace.py
318+
run: python3 bin/check-namespace.py --public tests/site/public
317319

318320
- name: Check the parameter contract and legacy-key errors
319321
run: python3 bin/check-params.py

AGENTS.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ accepted decisions, dated research, and active proposals.
2929
- Put accepted rationale under `content/docs/design/decisions/` in the site
3030
repository and dated, non-normative evidence under `research/`. Preserve
3131
retired drafts in Git history and `CHANGELOG.md`, not a local planning tree.
32-
- Run the smallest owning checker here first. Then validate the real site from
33-
`../oink.pgsty.com` with the sibling theme replacement:
32+
- Run the smallest owning checker here first. Then validate the real site
33+
through its sibling-checkout Make targets:
3434

3535
```sh
36-
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> /Users/vonng/pgsty/oink' npm test
37-
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> /Users/vonng/pgsty/oink' npm run test:browser
38-
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> /Users/vonng/pgsty/oink' hugo server -DFE
36+
make -C ../oink.pgsty.com check
37+
make -C ../oink.pgsty.com browser
38+
make -C ../oink.pgsty.com dev
3939
```
4040

4141
- Use the documentation site for rendered EN/ZH, desktop/mobile, light/dark,

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
All notable changes to OINK are documented here. The project follows
44
[Semantic Versioning](https://semver.org/) for published tags.
55

6+
## [Unreleased]
7+
8+
### Fixed
9+
10+
- `params.ui.scroll_spy` and the page-level `scroll_spy` override no longer
11+
emit an unused patch runtime. OINK's normal shell runtime already tracks the
12+
active outline heading. Both keys remain accepted as quiet compatibility
13+
no-ops throughout 1.x and may be removed only in a future breaking release.
14+
- Plain page Print and overlapping Book Print aggregates no longer race on the
15+
page's `tdBookAggregate` Store flag. One per-page cached coordinator now
16+
renders and exposes the distinct plain and Book variants in a fixed order.
17+
618
## [1.0.0] - 2026-08-29
719

820
OINK 1.0.0 declares the current component, configuration, content, output, and

CLAUDE.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,26 @@ responsive, and visual validation against the sibling documentation site.
4949
```sh
5050
python3 bin/check-shell.py # shell, blog, page-end contracts
5151
python3 bin/check-components.py # component and render-hook contracts
52-
python3 bin/check-output.py # rendered structure/runtime/security
52+
python3 bin/check-output.py # fresh strict fixture + output checks
53+
python3 bin/check-namespace.py # fresh strict fixture + namespace checks
5354
python3 bin/check-goldens.py # HTML/print/Markdown/RSS/LLMS goldens
5455
python3 bin/check-params.py # config shapes and warning fallbacks
5556
python3 bin/check-i18n.py # locale schema parity
5657
node --test 'tests/js/**/*.test.js' # browser-runtime units
5758
hugo --source tests/site --printPathWarnings --panicOnWarning
5859
```
5960

60-
From `../oink.pgsty.com`, validate and preview the real bilingual site with the
61-
current theme checkout:
61+
Both rendered-output checkers build into a temporary directory when invoked as
62+
shown. Pass `--public tests/site/public` only to reuse an intentionally fresh
63+
fixture that was built separately; this mode does not rebuild that fixture.
64+
65+
Validate and preview the real bilingual site with the current sibling theme
66+
checkout:
6267

6368
```sh
64-
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> /Users/vonng/pgsty/oink' npm test
65-
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> /Users/vonng/pgsty/oink' npm run test:browser
66-
HUGO_MODULE_REPLACEMENTS='github.com/pgsty/oink -> /Users/vonng/pgsty/oink' hugo server -DFE
69+
make -C ../oink.pgsty.com check
70+
make -C ../oink.pgsty.com browser
71+
make -C ../oink.pgsty.com dev
6772
```
6873

6974
Migration is dry-run unless `--write` is explicit:

assets/js/docs-shell.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@
443443
/*
444444
* Below xl the TOC rail is hidden, which used to take the table of contents
445445
* and the taxonomy clouds with it. Rather than render a second copy —
446-
* duplicate ids would break the scrollspy and the disclosure wiring — the
446+
* duplicate ids would break the outline tracker and the disclosure wiring — the
447447
* single block is moved into a slot in the sidebar drawer and moved back on
448448
* the way up.
449449
*

assets/js/keyboard-nav.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,7 @@
582582
return true;
583583
}
584584
}
585-
var fallback = doc.querySelector('[data-td-theme-toggle]') ||
586-
doc.querySelector('[data-td-theme-toggle]');
585+
var fallback = doc.querySelector('[data-td-theme-toggle]');
587586
if (!fallback || typeof fallback.click !== 'function') return false;
588587
fallback.click();
589588
return true;

assets/js/scrollspy-patch.js

Lines changed: 0 additions & 103 deletions
This file was deleted.

assets/js/sidebar-nav.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Hydrate the active path of a cached sidebar without fetching page fragments.
1+
// Hydrate the active path of a visible cached sidebar without fetching fragments.
22
(function () {
33
'use strict';
44

@@ -13,7 +13,7 @@
1313

1414
function init() {
1515
var menu = document.getElementById('td-sidebar-menu');
16-
if (!menu || !menu.classList.contains('d-none')) return;
16+
if (!menu || !menu.hasAttribute('data-td-sidebar-hydrate-active')) return;
1717

1818
var canonical = document.querySelector('link[rel="canonical"]');
1919
var current = pathOf(canonical ? canonical.href : window.location.href);
@@ -46,6 +46,7 @@
4646
item = item.parentElement && item.parentElement.closest('li')
4747
) {
4848
item.classList.add('td-active-path');
49+
item.classList.remove('td-shell-tree__item--hidden');
4950
var toggle = item.querySelector(
5051
':scope > .td-shell-tree__row [data-td-shell-tree-toggle]',
5152
);
@@ -61,7 +62,10 @@
6162
}
6263
}
6364

64-
menu.classList.remove('d-none');
65+
// Resolve the marker-scoped transition suppression before restoring the
66+
// ordinary disclosure motion used after hydration.
67+
menu.getBoundingClientRect();
68+
menu.removeAttribute('data-td-sidebar-hydrate-active');
6569
}
6670

6771
if (document.readyState === 'loading') {

assets/scss/td/shell/_sidebar.scss

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,16 @@
413413
visibility 0s;
414414
}
415415

416+
// A cached tree discovers its active branch after the first paint. Apply that
417+
// initial state without fading readable text through a sub-AA opacity; once
418+
// hydration removes the marker, reader-triggered disclosure transitions keep
419+
// their normal motion.
420+
[data-td-sidebar-hydrate-active] .td-shell-tree__children,
421+
[data-td-sidebar-hydrate-active] .td-shell-tree__row,
422+
[data-td-sidebar-hydrate-active] .td-shell-tree__link {
423+
transition: none;
424+
}
425+
416426
.td-shell-tree__children-inner {
417427
overflow: hidden;
418428
min-height: 0;

bin/check-actions.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from pathlib import Path
1717
from typing import Any
1818

19-
from test_site import TEST_SITE, fixture_config
19+
from test_site import TEST_SITE, fixture_config, run_hugo_process
2020

2121

2222
ROOT = Path(__file__).resolve().parents[1]
@@ -201,7 +201,7 @@ def validate_external_mount_actions() -> None:
201201

202202
with tempfile.TemporaryDirectory(prefix="oink-external-actions-") as temp:
203203
public = Path(temp) / "public"
204-
result = subprocess.run(
204+
result = run_hugo_process(
205205
[
206206
"hugo",
207207
"--source",
@@ -382,7 +382,15 @@ def validate_manifest(
382382
require(commands["print_now"]["title"] == "立即打印", "out-of-order ZH merge failed")
383383

384384

385-
def run_invalid_build(helper: Any, workspace: Path, name: str, command_yaml: str, expected: str) -> None:
385+
def run_invalid_build(
386+
helper: Any,
387+
workspace: Path,
388+
name: str,
389+
command_yaml: str,
390+
expected: str,
391+
*,
392+
strict_canary: bool,
393+
) -> None:
386394
site = workspace / f"invalid-{name}"
387395
output = workspace / f"invalid-public-{name}"
388396
shutil.copytree(helper.SITE_FIXTURE_PATH, site)
@@ -396,7 +404,7 @@ def run_invalid_build(helper: Any, workspace: Path, name: str, command_yaml: str
396404
)
397405
config = config.replace(" weight: 1\n zh:\n", " weight: 1\n" + addition + " zh:\n")
398406
(site / "hugo.yaml").write_text(config, encoding="utf-8")
399-
result = subprocess.run(
407+
result = run_hugo_process(
400408
[
401409
"hugo",
402410
"--source",
@@ -422,6 +430,10 @@ def run_invalid_build(helper: Any, workspace: Path, name: str, command_yaml: str
422430
require(expected in result.stdout, f"invalid {name} build missed {expected!r}:\n{result.stdout}")
423431
require(result.returncode == 0,
424432
f"invalid {name} command stopped the build instead of warning:\n{result.stdout}")
433+
require(
434+
(output / "en/docs/guides/tutorial/index.html").is_file(),
435+
f"invalid {name} command emitted no safe page output",
436+
)
425437
if "unsafe URL" in expected:
426438
# The dropped URL must not reach any rendered page -- that is the
427439
# whole point of refusing it.
@@ -431,22 +443,23 @@ def run_invalid_build(helper: Any, workspace: Path, name: str, command_yaml: str
431443
if path.is_file() and "javascript:alert" in path.read_text(errors="ignore")
432444
]
433445
require(not leaked, f"invalid {name} command leaked its unsafe URL into {leaked[:3]}")
434-
strict = subprocess.run(
435-
[
436-
"hugo", "--source", str(site), "--themesDir", str(ROOT.parent),
437-
"--destination", str(output) + "-strict",
438-
"--cacheDir", str(workspace / "cache-invalid-strict"),
439-
"--panicOnWarning",
440-
],
441-
cwd=site,
442-
env={**os.environ, "HUGO_ENVIRONMENT": "development"},
443-
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False,
444-
)
445-
require(strict.returncode != 0, f"invalid {name} command survived --panicOnWarning")
446+
if strict_canary:
447+
strict = run_hugo_process(
448+
[
449+
"hugo", "--source", str(site), "--themesDir", str(ROOT.parent),
450+
"--destination", str(output) + "-strict",
451+
"--cacheDir", str(workspace / "cache-invalid-strict"),
452+
"--panicOnWarning",
453+
],
454+
cwd=site,
455+
env={**os.environ, "HUGO_ENVIRONMENT": "development"},
456+
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=False,
457+
)
458+
require(strict.returncode != 0, f"invalid {name} command survived --panicOnWarning")
446459

447460

448461
def strict_fails(site: Path, workspace: Path, name: str) -> bool:
449-
strict = subprocess.run(
462+
strict = run_hugo_process(
450463
["hugo", "--source", str(site), "--themesDir", str(ROOT.parent),
451464
"--destination", str(workspace / f"strict-{name}"),
452465
"--cacheDir", str(workspace / f"cache-strict-{name}"), "--panicOnWarning"],
@@ -818,8 +831,25 @@ def main() -> int:
818831
'field "keywords" must be an array of strings',
819832
),
820833
}
834+
strict_canaries = {
835+
"unknown", # built-in action lookup
836+
"both", # action/URL exclusivity
837+
"callback", # unsupported fields
838+
"javascript", # URL policy
839+
"duplicate", # command identity
840+
"reserved", # built-in identity
841+
"numeric-title", # scalar field types
842+
"scalar-keywords", # list field types
843+
}
821844
for name, (yaml, expected) in invalid_cases.items():
822-
run_invalid_build(helper, workspace, name, yaml, expected)
845+
run_invalid_build(
846+
helper,
847+
workspace,
848+
name,
849+
yaml,
850+
expected,
851+
strict_canary=name in strict_canaries,
852+
)
823853

824854
validate_custom_url_policy(helper, workspace)
825855

0 commit comments

Comments
 (0)