Skip to content

Re-audit fixes: clean error on schema-drift + CHANGELOG gaps#20

Merged
maruwork merged 27 commits into
mainfrom
claude/serene-davinci-fd7b67
Jun 20, 2026
Merged

Re-audit fixes: clean error on schema-drift + CHANGELOG gaps#20
maruwork merged 27 commits into
mainfrom
claude/serene-davinci-fd7b67

Conversation

@maruwork

Copy link
Copy Markdown
Owner

Re-audit of all files. One real bug fixed: CLI returned a raw KeyError traceback (exit 1) on a schema-drifted/hand-edited parent artifact; now a JSON error envelope (exit 11). Plus CHANGELOG completeness (render-html, test count). Other audit findings were by-design or CLI-unreachable.

🤖 Generated with Claude Code

maruwork and others added 27 commits June 19, 2026 18:27
- C1: add adop_html.py + HTML template to adop.json manifest; adop_sync
  now copies runtime_files + template_files so a synced runtime starts.
- C2: add `reject` command (proposed/blocked/hold -> reject); summary
  resolves a standalone reject-note to terminal `reject`.
- C3: write-trial guard uses WRITE_TRIAL_TYPES membership so task-scoped
  and phase-scoped also require an isolated write sandbox.
- U1: dashboard falls back to the trial-packet no_impact_envelope.
- U2/U3: read block_reason and intake_reason field names.
- U4: modal opens with focus on close button and traps Tab.
- C4/U5: drop wrong --deprecation-reason hints, fix sample watch id,
  reconcile reject lifecycle docs in README and design notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dings

- R1: adop_sync rejects manifest paths with '..' / absolute / drive-relative
  roots so apply/push cannot write outside --target.
- R2: dashboard surfaces watch-note interest_reason.
- R3: pre-trial reject shows reject_reason and no longer claims a trial ran.
- R4: scan skips files larger than 5 MB (OOM guard).
- R5: adop_sync exits cleanly on malformed adop.json / sync-registry.json.
- Orphaned-lock recovery: reclaim a write lock only when older than 30s so a
  crashed writer cannot block a fixed-id artifact forever.
- Docs: SECURITY.md Trust Model section (operator-trusted inputs, opt-in
  artifact-root boundary, trusted sync source); document `adop_sync apply`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…olicy

- Dashboard: surface retirement_reason / migration target+plan / archive
  end_date+successor / hold reason for deprecated/migrating/archived/hold lanes
  (were showing the stale promote judgment or generic text).
- Concurrency: _acquire_lock treats Windows PermissionError like FileExistsError
  so write_next_sequential_artifact retries instead of crashing under contention;
  finally-cleanup no longer raises on a contended unlink.
- Scaling: build_summary / _resolve_scene_states resolve judgments from one
  in-memory load instead of re-reading the artifact root per trial (was ~O(n^2)).
- HTML UX: Historical filter auto-opens the history section.
- Lint: ignore E501 (long help/template strings) while keeping line-length=100
  for ruff format; apply safe ruff autofixes (import order, unused imports);
  add pytest-cov coverage config; document network-FS durability limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Schema durability: split SCHEMA_VERSION (write) from MIN_READABLE_SCHEMA_VERSION
  (read floor); accept the inclusive range; a too-new version reports "written by
  a newer adop; upgrade" instead of generic invalid. Additive-only compat contract
  documented (no in-place migration; append-only records survive upgrades).
- Cross-project value: new read-only `adop aggregate --root A --root B [--json]`
  portfolio view (scene/tool/state per root); design note updated to match.
- Coverage: add pytest-cov gate (fail_under=80, a real floor — dual try/except
  import blocks and __main__ are structurally uncoverable); add adop_sync CLI
  tests (74%->94%). Measured total ~84%.
- Docs: README note on correcting mistakes under append-only (supersede, never edit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI on the branch was red. Root cause was pre-existing lint/format debt
(carried since main fb6f167), surfaced by `pre-commit run --all-files`:

- ruff format: reflow all modules/tests to the configured width.
- ruff check: fix the residual E741/E731/E702/F401/F841 (ambiguous `l`,
  lambda->def, semicolon splits, unused import/vars).

Tests (196), phase gate (6/6), and adop lint remain green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The pre-commit `mypy` hook failed fatally with "No parent module -- cannot
perform relative import" on the `try: from . import ...` dual-import idiom:
in this flat py-modules layout the relative branch never runs (script,
pip-install, and tests all import these as top-level), so it was dead code
that only broke mypy. Collapse each module to the absolute imports that
actually execute. This also let mypy finally check adop_summary, surfacing a
real latent bug (variable `key` reused as both tuple and str) — now fixed by
renaming the str key to `note_key`.

All CI-equivalent gates pass locally: ruff check, ruff format --check,
mypy (23 files), pytest (196), phase gate (6/6), adop lint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Long lane portfolios make the rendered dashboard scroll well past a screen;
without a back-to-top control it is awkward to return to the summary/toolbar.
Add a fixed, accessible "↑ Top" button that fades in past 400px of scroll and
smooth-scrolls to the top. Lives in the canonical template so every
`render-html` output gets it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ray files)

First-view tidy-up without breaking tooling:
- Move SECURITY/CONTRIBUTING/CODE_OF_CONDUCT/SUPPORT into .github/ (GitHub still
  recognises them there for the community profile); update the SUPPORT URL in the
  issue-template config and the README references.
- Fold pytest.ini into pyproject [tool.pytest.ini_options]; remove pytest.ini.
- Stop tracking the generated .coverage file and gitignore coverage outputs.

Tests (197), ruff, ruff format, mypy all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…al-gap

- P1: `adop init --overlay a/b/c.md` crashed with a raw FileNotFoundError when
  the parent dir was absent. Create overlay_path.parent before writing/copying.
- P3: `adop summary` appended a comparison-time structural_gap ("workflow lacks
  a bounded evaluation lane") for every scene that ever had a comparison, even
  once it reached in-trial — self-contradictory. Gate the gap to pre-trial
  states (watch/proposed/blocked/trial-ready).

Tests added for both; ruff/format/mypy/pytest(199)/adop lint green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tus)

The repo's first view was dominated by its own dogfooding/coupling-demo setup,
not the tool. Remove that apparatus so the published repo is just the tool:

- Delete .adop/ (157 self-adoption records) and gitignore it (runtime output,
  not source; consumer projects create their own).
- Delete coupling-demo dev surfaces: eslint.config.js, package.json,
  .prettierrc.json, .prettierignore, .markdownlint-cli2.jsonc, .trivyignore,
  renovate.json, Dockerfile.tooling-example, .vscode/.
- Delete the tool-surface-examples workflow and scripts/repo-smoke.sh.
- Drop the now-dangling npm dependabot ecosystem and the check-renovate
  pre-commit hook.
- Reset adop-overlay.md to an honest canonical-repo overlay (no fake scenes,
  no references to deleted files).

Tool behaviour, scan surface rules, tests, and CLI are unchanged.
ruff / ruff format / mypy / pytest (199) / CLI smoke all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restores a small "ADOP used on a real lane" illustration that the dogfooding
cleanup removed — without re-cluttering the root:

- examples/walkthrough/.adop/ holds one lint-ci lane (ruff) driven intake ->
  compare -> trial -> result+judgment -> hold, plus one coupling snapshot
  (7 lint-clean artifacts).
- examples/README.md explains the lane and the view commands.
- .gitignore now ignores only the ROOT /.adop/ so this tracked example survives
  while the canonical repo still keeps no records of its own.

examples/ is one root entry and is outside ruff/mypy/pytest/packaging scope, so
it does not affect the toolchain. ruff/format/mypy/pytest(199) green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fb6f167

The Windows "Consumer smoke" step embedded a PowerShell here-string (@'...'@)
with its body flush-left at column 0 inside a `run: |` block. That under-indented
content terminated the YAML block scalar early, making ci.yml unparseable — so
GitHub failed the workflow at startup (0-second, 0-job "failure") on EVERY push,
including pushes to main, from fb6f167 onward. (Linux heredocs are fine: YAML
strips the uniform block indent so bash receives them flush-left.)

Indent the here-string body/terminator to the block indent. YAML now parses
(check-jsonschema: ok) and, after the uniform indent is stripped, PowerShell
still receives a valid flush-left here-string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The first real hosted CI run (after the ci.yml YAML fix) showed this test
failing only on windows-latest under `pytest -n auto`: 12 threads competing
with other xdist workers on a 2-core runner starved one worker until it
exhausted the 64-attempt mint retry budget ("already in progress: wt-006").
No duplicate ids occurred — it is retry-exhaustion under pathological
contention, not a product bug. 5 workers keeps the lock/retry + Windows
PermissionError path covered while staying well within the retry budget.

Full suite green under `pytest -n auto` (199).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Defense in depth so a credential file cannot land in the repo:
- .gitignore: ignore .env / *.env / secrets/ / keys/ / *.pem / *.key /
  *.p12 / *.pfx / id_rsa / id_ed25519 / *credentials*.json so they are never
  staged by accident.
- pre-commit: add detect-private-key and check-added-large-files, plus a local
  `forbid-secret-files` hook (language: fail) that blocks the commit if any
  .env / key / secrets-dir file is staged (e.g. via `git add -f`). It skips
  cleanly when no such file is present, so normal commits are unaffected.

`pre-commit run --all-files` stays green; verified the hook blocks a staged
.env and .pem.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5 workers still exhausted the 64-attempt budget on a CPU-starved
windows-latest xdist runner (wt-004): a descheduled thread loses the mint
race many times before peers commit. The test asserts the real invariant
(no duplicate ids; PermissionError retried, not raised) — not the retry-budget
size — so pass max_attempts=100_000 to remove the starvation-induced flake.

Verified: concurrency test stable across repeated runs; full suite green
under `pytest -n auto` (199).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dashboard's "Latest update" column sorted a scene's artifacts by the
numeric part of their id ACROSS types, so an intake (ci-002) outranked a later
trial packet (tr-001) and the column showed a stale, cryptic "Intake note
ci-002". Pick the latest by (created_at, lifecycle stage) and render it as
"<date> · <friendly label>", which also makes the "Latest update" sort
chronological. Adds a test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The trial-packet (and later notes) record decision_owner, but the dashboard
only kept it in the raw JSON — a governance board should name who owns each
decision. Surface it as a "Decision owner" row in the detail summary (taken
from the most-advanced artifact that records one; hidden when none). Adds a test.

ruff / ruff format / mypy / pytest (201) green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… schema-drift

A re-audit found that close-trial / start-trial read parent-artifact fields by
direct subscription (e.g. packet["landing_target"], comparison["adoption_unit"]).
Because the store tolerates older/forward records and keeps hand-edited files, a
parent missing such a field made main() leak a bare KeyError traceback with exit
1 — breaking the JSON-native error-envelope contract. main() now maps KeyError
(and any unexpected exception) to a clean JSON error envelope with exit 11.

Also fills two CHANGELOG gaps the audit found: the `render-html` command was
never listed as added since 0.1.1, and the test count is updated (now 202).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@maruwork maruwork merged commit f5d119d into main Jun 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant