Skip to content

docfilters CLI v1.0.0: three ports, the shared contract, and the CI that keeps them together - #49

Merged
slawomir-winiarski merged 18 commits into
masterfrom
docfilters-cli
Nov 6, 2026
Merged

slawomir-winiarski merged 18 commits into
masterfrom
docfilters-cli

Conversation

@slawomir-winiarski

@slawomir-winiarski slawomir-winiarski commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

docfilters CLI v1.0.0: three ports, the shared contract, and the CI that keeps them together

master already has the C# reference. This adds the other three
implementations, the machinery that proves all four behave identically, and
the release pipeline that ships them.

466 new files, 15 changed.

The three ports

The same seven-verb command line in C++17, Python and Java, mirroring the
reference folder for folder and name for name so any two can be diffed by eye.

Source Tests Build
cpp17/ 16,845 lines 11,393 CMake, CLI11 at the version samples/cpp17 already fetches
python/ 13,000 lines 9,621 nothing to build; standard library only
java/ 14,389 lines 12,389 Gradle wrapper, the vendored binding jar, a JDK-only test runner

No new dependencies in any of them. Java has no argument-parsing library at
all, because samples/java has none, so it binds argv with the same
hand-written argument table the other three wrap their parser behind.

The contract

tests/conformance/ is the authority, 2,273 lines in four line-oriented
fixtures that every suite reads and compares byte for byte:

Fixture Authority for
arguments.txt which arguments exist on which command, short form and arity
formats.txt every --to token and what it implies
out-names.txt --out-name rendering, and templates that must be rejected
plan-corpus.txt argv to resolved plan, and every error, message and exit code

They are written by hand from what the tool is meant to do, never dumped from
csharp/. A dumped file could only prove the four agree with each other, and
would never catch an argument nobody implemented.

Below the plan, where the fixtures stop, two more checks:
tests/e2e/end2end.py drives any of the four against the real engine through
one --cli flag, and tests/parity/ compares the four to each other rather
than each to a description.

CI

cli.yml grows from one job to fifteen: a change detector, four per-language
matrix jobs, cross-implementation parity, release validation, four packaging
jobs, the publish step, and a CI gate that always reports.

Two new workflows enforce the premise that the four move together:

  • ports-landed.yml blocks a pull request touching the reference until
    the three translations have merged into it, so the contract change and all
    four implementations reach master in a single merge. Override label:
    ports-unaffected.
  • ai-sync.yml does the moving. On the ai-sync label it runs Claude
    Code against .claude/commands/propagate-delta.md and opens one port pull
    request per language, each targeting the source branch. Inference goes
    through Bedrock in our own account over OIDC, so the only stored secret is a
    role ARN, scoped to a gated environment. Fork pull requests are refused with
    no override.

These answer different questions from cli.yml's existing contract-order
job, which is why they carry separate override labels.

Reference fixes

Found while porting, fixed in csharp/ first and then translated, each with a
fixture or unit test behind it:

  • JsonWriter escapes unpaired surrogates. Written literally, they have no
    UTF-8 encoding, and each implementation's encoder invented its own answer:
    U+FFFD in C#, ? in Java, a fatal UnicodeEncodeError in Python. Reachable
    from an NTFS filename or malformed document metadata. Five new tests.
  • help --json uses JsonWriter, not JsonSerializer. It was the one
    JSON surface in the tool emitted on a single line.
  • version prints the informational version. GetName().Version is always
    four components, so 1.0.0 came back as 1.0.0.0, a shape no port can
    reproduce. <Version> now comes only from VERSION.txt via
    Directory.Build.props.
  • E_OUTPUT_EXISTS message: "pass --overwrite" to "use --overwrite", with
    the fixture.
  • plan-corpus.txt now lists all five error codes that cannot be provoked
    from argv and a filesystem, and says where each one is exercised instead.
    Two of them, E_INTERRUPTED and E_UNSAFE_OUTPUT, were undocumented.

Outside the sample

  • bindings/c/CMakeLists.txt: the Windows import library that ships with
    existing binaries now wins over the committed copy, and the release URL is
    resolved only when actually downloading, so a configure against local
    binaries needs neither the network nor the GitHub API.
  • bindings/python: MakeDocPack and its two entry points, which the Python
    port needs for --to docpack.
  • bindings/java/lib/ISYS11df.jar: updated build.
  • .gitignore: .claude/ was ignored wholesale, which would have hidden the
    checked-in agent commands; now only worktrees and *.local.json.

Documentation

CLAUDE.md and AGENTS.md hold the rules, languages.yml holds where each
implementation lives and how it is built, and design/porting/ holds the
method plus one traps file per language, including one for C# itself read the
other way round: what the reference's constructs mean, for the ports to
reproduce.

Reviewing

Execution/ in any implementation is the sample, and the rest is the plumbing
any real tool needs. For the ports, the useful read is one port's diff beside
the reference file it mirrors, which is what the layout is for.

Automated translation tests

See the PR containing
automated translation from manual C# change into C++, python and java.

@slawomir-winiarski
slawomir-winiarski requested a review from a team November 3, 2026 11:24
slawomir-winiarski and others added 10 commits November 3, 2026 14:35
Translated from the change on #50 by the /propagate-delta command.
Review it as a translation: see DocFiltersCli/.claude/commands/review-port.md.
Translated from the change on #50 by the /propagate-delta command.
Review it as a translation: see DocFiltersCli/.claude/commands/review-port.md.
Translated from the change on #50 by the /propagate-delta command.
Review it as a translation: see DocFiltersCli/.claude/commands/review-port.md.
@slawomir-winiarski

Copy link
Copy Markdown
Contributor Author

Link the test PR

@slawomir-winiarski slawomir-winiarski added the ports-unaffected This CLI's reference change has no counterpart in the ports label Nov 4, 2026
@slawomir-winiarski
slawomir-winiarski merged commit e21e53a into master Nov 6, 2026
26 checks passed
@slawomir-winiarski
slawomir-winiarski deleted the docfilters-cli branch November 6, 2026 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ports-unaffected This CLI's reference change has no counterpart in the ports

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants