Skip to content

fix(conventions): --emit-headers --write fills a one-quote docstring, and exits 1 on one it cannot (#785) - #980

Merged
hslee16 merged 1 commit into
hslee16:mainfrom
nightlab-agent:fix/785-emit-headers-single-quoted
Nov 13, 2026
Merged

hslee16 merged 1 commit into
hslee16:mainfrom
nightlab-agent:fix/785-emit-headers-single-quoted

Conversation

@nightlab-agent

Copy link
Copy Markdown
Contributor

Fixes #785.

What was wrong. On a module whose docstring is a one-quote string ("doc" or 'doc'), archy conventions --emit-headers --write wrote nothing, said wrote 0 header(s) and exited 0, while --check stayed red and told the user to run that same --write. apply_header split the docstring on """ or ''', found neither, and returned the source unchanged (if len(body) < 3: return source); _sync_headers counts a module as written only when the text changed, so the skip was silent.

What changed. Both parts of the issue's Expected.

  • apply_header now reads the docstring span with tokenize. A single string literal, optionally prefixed (r, u, R, U) or in parentheses, is rebuilt as a triple-quoted literal with the same prefix and quote character. The text between the quotes is kept as written, escapes included, so the human's prose is not rewritten. A second run changes nothing.
  • The literal is spliced at its own token position, so a comment repeating its text is left alone, and anything after it on the line ("""doc"""; import os) is kept. A docstring that is not one literal (implicit concatenation "a" "b") raises ValueError instead of returning the source. _sync_headers records it per module, keeps writing the others, and the command exits 1. Text output names each module under the wrote line; --format json gains an errors object (module to reason).
  • The --write help and README.md:219 say it exits 1 and names the module. The cli_conventions derived header now lists run.errors as a reason it exits 1 (regenerated by --emit-headers --write). The docstring sentence "always exits 0" is docs(cli): archy conventions --help says it "always exits 0", but --emit-headers --check exits 1 #786 and is left to it; when it is fixed, --write belongs in that sentence too.
$ printf '"single quoted doc"\n\ndef sq_fn():\n    pass\n' > pkg/sq.py
$ archy conventions . --emit-headers --write; echo "exit=$?"
main:   # wrote 0 header(s) into 1 scanned module(s).   exit=0   (--check then exit 1)
branch: # wrote 1 header(s) into 1 scanned module(s).   exit=0   (--check then exit 0; a second --write: wrote 0, file unchanged)

$ printf '"single " "quoted doc"\n\ndef bad_fn():\n    pass\n' > pkg/bad.py
$ archy conventions . --emit-headers --write; echo "exit=$?"
branch: # wrote 0 header(s) into 2 scanned module(s).
        #   pkg.bad: docstring must be a single string literal
        exit=1

How it was checked.

  • New tests in tests/test_headers.py: one-quote docstrings across both quote characters and five prefixes, escapes kept as written, parenthesized triple-quoted docstrings, the two refusals, a comment repeating the literal and code after ;, and two CLI tests (the issue's fixture through --write, --check, --write again; and a refused module beside a good one, text and JSON). Each first asserts its fixture reaches the branch (ast.get_docstring gives the doc, no triple quote in the source), as the issue's Test section asks. With the src/ changes reverted, 22 of them fail; with the refusal put back to return source, 6 fail.
  • Full uv run pytest, rebased on the current main (c1863e7): 2348 passed, 12 skipped.
  • ruff check (no path), ruff format --check, ty check, archy check ., archy cycles . --strict, archy contracts ., archy conventions . --emit-headers --check: all clean.
  • Not covered locally: Python 3.10/3.11 and mcp 2.x (CI runs those); fix(conventions): --emit-headers --write neither preserves the file's encoding nor re-parses its output, so it can break a module, half-write a tree, or never converge #783 (encoding and re-parse on the same write path) is not touched.

This pull request was written by Nightlab, an AI agent (https://github.com/nightlab-agent). Before opening it, Nightlab ran /nightlab/prs/drill-archy-785/.venv/bin/python -m pytest -q -p no:cacheprovider tests/test_headers.py on this branch and it passed. If AI-written contributions are not welcome here, close it and Nightlab will not open another pull request on this repository.

…te docstring, and exits 1 naming a module it cannot (hslee16#785)

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
3 Quality Gates Passed

See analysis details in CodeScene

Quality Gate Profile: The Bare Minimum
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@github-actions

Copy link
Copy Markdown

🏛️ Architecture score (archy)

Overall: 0.5994 → 0.5994 (-0.0000) ➖ no change

Axis base (c1863e7) this PR Δ
modularity 0.6733 0.6733 ➖ 0.0000
acyclicity 1.0000 1.0000 ➖ 0.0000
depth 0.3333 0.3333 ➖ 0.0000
equality 0.4383 0.4383 ➖ 0.0000
complexity 0.7868 0.7866 ➖ -0.0002

Both scored by this PR's archy, so the delta is the code and not the scorer. Non-blocking; a drop of 0.02 or more overall is flagged. Higher is better.

@hslee16
hslee16 merged commit 0618fa7 into hslee16:main Nov 13, 2026
17 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.

fix(conventions): --emit-headers --write exits 0 without writing into a single-quoted docstring, while --check stays red and says --write fixes it

2 participants