Skip to content

Write the ideas file with "w" and an explicit utf-8 encoding - #19

Merged
dmccoystephenson merged 1 commit into
mainfrom
feature/write-mode-and-encoding
Sep 10, 2026
Merged

dmccoystephenson merged 1 commit into
mainfrom
feature/write-mode-and-encoding

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • writeToFile() now opens the output file with "w" instead of "a". The timestamp has one-second resolution, so append mode had exactly two effects: two runs completing within the same second were silently merged into one file, and every other run appended to a file that had just been created empty.
  • encoding="utf-8" is now passed explicitly, so a keyword or idea containing non-ASCII text is written the same way regardless of the platform default encoding.
  • Two regression tests were added to tests/test_ideaCollisionGenerator.py: one freezes the timestamp and writes twice to prove the second run replaces the first, and one records the arguments writeToFile() passes to open() and reads the written bytes back as UTF-8.

The written line format (str(pair) + ": " + idea) and the ideas/ideas-<timestamp>.txt filename are unchanged, so ideas/example.txt still records the output contract.

Scope notes

The third part of #11 — the CWD-relative output path — was resolved by documentation rather than by a code change. README.md already states that the program must be run from the repository root and that starting it from src/ creates a second src/ideas/ directory (lines 25–27), which is one of the two resolutions the issue offers. Making the path source-relative was rejected as the larger change: it would alter where every existing test and the CI end-to-end step write, for no benefit to a program that is documented as being run from the root.

Issue #14 (.vscode/launch.json using the deprecated "python" debugger type) was originally batched into this PR, but editing .vscode/launch.json was refused by the harness running this session as a protected path. That issue is therefore left open and untouched.

The remaining open issues were deferred for the reasons below, so that a later cycle is not left guessing why they were passed over:

Test plan

All commands were run from the repository root on Python 3.8.10 during this session.

  • python3 -m py_compile src/collide.py src/ideaCollisionGenerator.py → compile OK
  • python3 -m unittest discover -s tests → Ran 18 tests in 0.007s / OK (16 before this branch, 18 after)
  • Regression evidence for the "a" → "w" fix, by stashing only the production change:
$ git stash push -- src/ideaCollisionGenerator.py
$ python3 -m unittest discover -s tests
FAIL: testSameTimestampReplacesRatherThanAppends (test_ideaCollisionGenerator.TestWriteToFile)
AssertionError: Lists differ: ["['a', 'b']: first\n", "['c', 'd']: second\n"] != ["['c', 'd']: second\n"]
Ran 18 tests in 0.007s
FAILED (failures=2)

$ git stash pop
$ python3 -m unittest discover -s tests
Ran 18 tests in 0.007s
OK

Both new tests fail with the one-line fix reverted and pass with it applied.

  • Full run with all fifteen lines redirected from a fixture, one of the ideas containing non-ASCII text:
$ python3 src/collide.py < collide-write-mode-stdin.txt
Enter 1st keyword: Enter 2nd keyword: ... Enter 10th keyword: Enter an idea based off of these keywords: ['social mobility', 'complex relationships']
Enter an idea: Enter an idea based off of these keywords: ['fishing', 'exploration']
Enter an idea: Enter an idea based off of these keywords: ['community', 'branching choices']
Enter an idea: Enter an idea based off of these keywords: ['mining', 'antagonist']
Enter an idea: Enter an idea based off of these keywords: ['interaction', 'world modification']
Enter an idea:

$ cat ideas/ideas-2026-09-10_01.12.35.txt
['social mobility', 'complex relationships']: An innkeeper who buys fish from the docks.
['fishing', 'exploration']: A mine whose tunnels persist between sessions.
['community', 'branching choices']: A café that changes who talks to whom.
['mining', 'antagonist']: A rival who only appears when the town prospers.
['interaction', 'world modification']: A map that fills in as it is walked.

Five lines for five pairs, the format matching ideas/example.txt, and the accented text round-tripping. The fixture and the generated files were removed afterwards; git status was confirmed clean apart from the two intended modifications.

Closes #11

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review

Scored adversarially against the diff and against commands actually run during this session, not against reasoning.

  • Scope: PASS — two files changed, src/ideaCollisionGenerator.py (one line) and tests/test_ideaCollisionGenerator.py (two added cases). No formatting churn, no renames, no unrelated edits.
  • Tests-new: PASS — no new public method was added; both behaviours changed by the one-line fix are covered by a new case.
  • Tests-fix: PASS — established empirically, not by reasoning. With git stash push -- src/ideaCollisionGenerator.py applied, python3 -m unittest discover -s tests reported FAILED (failures=2) naming testSameTimestampReplacesRatherThanAppends and testOutputFileIsOpenedAsUtf8; after git stash pop the same command reported Ran 18 tests / OK. The full output is quoted in the PR body.
  • Sibling structure: PASS — both cases were added inside the existing TestWriteToFile class, using the same tempfile + os.chdir fixture, the same mock.patch idiom, and the same camelCase test naming as their neighbours.
  • Sibling renames: no signal — nothing was renamed.
  • Docs: PASS — every row of the documentation sources-of-truth table was re-checked against the diff. README.md makes no claim about append versus replace or about encoding, so nothing there is falsified; ideas/example.txt still matches the emitted line format, which is untouched; .github/workflows/tests.yml's end-to-end step asserts five written lines, which still holds and was confirmed green on both matrix versions; .gitignore still covers the generated file; LICENSE and COPYRIGHT.md are untouched.
  • Issue resolution: PASS, with a caveat worth the owner's attention — parts 2 and 3 of writeToFile(): append mode, CWD-relative output path, and unspecified encoding #11 are fixed in code. Part 1, the CWD-relative output path, is resolved by documentation that already existed in README.md before this PR rather than by anything in this diff. The issue explicitly offers that as one of two acceptable resolutions, so Closes #11 is claimed; if a source-relative path was the intended outcome instead, this PR does not deliver it and the issue should be reopened.
  • Manual validation: PASS — py_compile clean, 18 tests passing locally on Python 3.8.10, one full fifteen-line redirected run producing five correctly formatted lines, and CI green on 3.8 and 3.13.
  • Ran, not read: PASS — every behavioural claim in the PR body is backed by output produced this session. No prompt text or output line was quoted from reading the source.
  • Stdin always redirected: PASS — the only invocation of src/collide.py anywhere in this cycle read a fifteen-line fixture written with an editor tool; nothing in the diff, the PR body, or the tests calls it bare, and no test reaches an unpatched input().
  • camelCase preserved: PASS — git diff origin/main...HEAD -- src/ renames nothing; the two new test methods follow the existing testX camelCase convention.
  • Stdlib only: PASS — no import was added at all; the tests reuse os, tempfile, and unittest.mock, all already imported in that file.
  • Python 3.8-safe: PASS — no match, no X | Y union, no dict | merge. Confirmed by the green 3.8 matrix job rather than by inspection alone.
  • Output contract preserved: PASS — the written line is still str(pair) + ": " + idea and the filename is still ideas/ideas-<timestamp>.txt; the end-to-end run's output matches ideas/example.txt's format.
  • Prompt strings intact: PASS — none of the four prompt or print strings appears in the diff.
  • Entry point still runnable: PASS — src/collide.py was not modified, and it was nevertheless run end to end on the patched tree.
  • License claims consistent: no signal — no file stating a licence, an owner, or a repository URL was touched.
  • No artifacts committed: PASS — git status --porcelain before committing showed only the two intended modifications; the fixture and the two generated ideas/ideas-*.txt files were removed, and staging was done by name rather than with git add -A.
  • No inflation: PASS — no packaging layout, no dependency, no new top-level directory, 47 added lines of which 46 are tests.

Judgement calls flagged rather than fixed

  1. testOutputFileIsOpenedAsUtf8 asserts the literal argument shape of the open() call (("w",) and {"encoding": "utf-8"}), which makes it brittle: a functionally identical refactor that passed the mode as a keyword would break it. That trade-off was taken deliberately, because the byte-level assertion in the same test cannot distinguish an explicit encoding="utf-8" from a platform default that happens to be UTF-8 — which is the case on every machine this suite currently runs on, including both CI matrix versions — and the mechanism open() consults for that default differs between 3.8 and 3.13, so patching it would be version-dependent. Observing the call is the version-stable option.
  2. testSameTimestampReplacesRatherThanAppends replaces the whole datetime module in the module namespace with a Mock. If writeToFile() later grows another use of datetime, that use will silently receive a Mock instead of failing loudly. The substitution is confined to a single with block, which was judged sufficient containment for a six-line method.
  3. src/ideaCollisionGenerator.py still has no trailing newline, as the diff footer shows. It is pre-existing and was left alone rather than pulled into an unrelated PR.
  4. Issue .vscode/launch.json uses the deprecated "python" debugger type #14 was originally batched here and has been dropped: editing .vscode/launch.json was refused by the harness running this session as a protected path. The issue remains open and untouched, and the branch was renamed to match the narrowed scope.

This review comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener). It is a self-review by the same session that wrote the change, not an independent review.


drafted by Claude on behalf of Daniel Stephenson

@dmccoystephenson
dmccoystephenson merged commit fc60cb3 into main Sep 10, 2026
2 checks passed
@dmccoystephenson
dmccoystephenson deleted the feature/write-mode-and-encoding branch September 10, 2026 07:14
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.

writeToFile(): append mode, CWD-relative output path, and unspecified encoding

1 participant