open-code-review-toolkit 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/PKG-INFO +2 -1
  2. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/README.md +1 -0
  3. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/pyproject.toml +7 -13
  4. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/_version.py +2 -2
  5. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/cli.py +10 -2
  6. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/manifests.py +27 -15
  7. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/repo.py +4 -2
  8. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/gitlab.py +14 -10
  9. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/markers.py +6 -4
  10. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/workflow.py +5 -4
  11. open_code_review_toolkit-0.2.1/CHANGELOG.md +0 -61
  12. open_code_review_toolkit-0.2.1/CONTRIBUTING.md +0 -14
  13. open_code_review_toolkit-0.2.1/SECURITY.md +0 -13
  14. open_code_review_toolkit-0.2.1/changelog.d/README.md +0 -3
  15. open_code_review_toolkit-0.2.1/docs/codex/AGENT_EXECUTION_PITFALLS.md +0 -44
  16. open_code_review_toolkit-0.2.1/docs/codex/TASKS_BACKLOG.md +0 -336
  17. open_code_review_toolkit-0.2.1/docs/configuration.md +0 -67
  18. open_code_review_toolkit-0.2.1/docs/development.md +0 -19
  19. open_code_review_toolkit-0.2.1/docs/engineering/project_principles.md +0 -30
  20. open_code_review_toolkit-0.2.1/docs/engineering/toolkit_strategy.md +0 -145
  21. open_code_review_toolkit-0.2.1/docs/gitlab.md +0 -31
  22. open_code_review_toolkit-0.2.1/docs/operations.md +0 -83
  23. open_code_review_toolkit-0.2.1/docs/release.md +0 -49
  24. open_code_review_toolkit-0.2.1/docs/security.md +0 -29
  25. open_code_review_toolkit-0.2.1/examples/gitlab/ocr-review.gitlab-ci.yml +0 -84
  26. open_code_review_toolkit-0.2.1/examples/gitlab/rules.json +0 -33
  27. open_code_review_toolkit-0.2.1/tests/__init__.py +0 -1
  28. open_code_review_toolkit-0.2.1/tests/support.py +0 -85
  29. open_code_review_toolkit-0.2.1/tests/test_cli.py +0 -39
  30. open_code_review_toolkit-0.2.1/tests/test_common_helpers.py +0 -391
  31. open_code_review_toolkit-0.2.1/tests/test_context_helpers.py +0 -1946
  32. open_code_review_toolkit-0.2.1/tests/test_install_local_artifact.py +0 -67
  33. open_code_review_toolkit-0.2.1/tests/test_operations_docs.py +0 -58
  34. open_code_review_toolkit-0.2.1/tests/test_posting_helpers.py +0 -1757
  35. open_code_review_toolkit-0.2.1/tests/test_project_strategy.py +0 -109
  36. open_code_review_toolkit-0.2.1/tests/test_quality_script.py +0 -12
  37. open_code_review_toolkit-0.2.1/tests/test_release_authorization.py +0 -107
  38. open_code_review_toolkit-0.2.1/tests/test_release_notes.py +0 -46
  39. open_code_review_toolkit-0.2.1/tests/test_release_process_docs.py +0 -30
  40. open_code_review_toolkit-0.2.1/tests/test_runtime_helpers.py +0 -880
  41. open_code_review_toolkit-0.2.1/tests/test_testpypi_preview.py +0 -238
  42. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/.gitignore +0 -0
  43. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/LICENSE +0 -0
  44. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/__init__.py +0 -0
  45. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/common/__init__.py +0 -0
  46. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/common/language.py +0 -0
  47. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/common/markdown.py +0 -0
  48. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/common/redaction.py +0 -0
  49. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/config_writer.py +0 -0
  50. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/configure.py +0 -0
  51. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/__init__.py +0 -0
  52. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/__main__.py +0 -0
  53. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/ansible.py +0 -0
  54. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/categorize.py +0 -0
  55. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/instructions.py +0 -0
  56. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/planner.py +0 -0
  57. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/render.py +0 -0
  58. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/context/settings.py +0 -0
  59. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/mcp_config.py +0 -0
  60. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/__init__.py +0 -0
  61. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/__main__.py +0 -0
  62. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/comments.py +0 -0
  63. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/formatting.py +0 -0
  64. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/payloads.py +0 -0
  65. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/result.py +0 -0
  66. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/settings.py +0 -0
  67. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/posting/snapshot.py +0 -0
  68. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/preflight.py +0 -0
  69. {open_code_review_toolkit-0.2.1 → open_code_review_toolkit-0.3.0}/src/ocr_toolkit/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-code-review-toolkit
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Unofficial GitLab CI integration layer for Open Code Review
5
5
  Project-URL: Homepage, https://github.com/xeonvs/open-code-review-toolkit
6
6
  Project-URL: Repository, https://github.com/xeonvs/open-code-review-toolkit
@@ -244,6 +244,7 @@ ocr-ci --help
244
244
  ```
245
245
 
246
246
  The current compatibility target is OCR `1.7.17`. CI should pin the release and verify its published checksum before execution.
247
+ The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
247
248
  Review output defaults to English. Set `OCR_REVIEW_LANGUAGE=Russian` to use Russian consistently in both OCR configuration and generated review context.
248
249
 
249
250
  Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
@@ -16,6 +16,7 @@ ocr-ci --help
16
16
  ```
17
17
 
18
18
  The current compatibility target is OCR `1.7.17`. CI should pin the release and verify its published checksum before execution.
19
+ The [versioned compatibility policy](docs/compatibility.md) records tested assets and evidence and describes the conservative Dependabot-like qualification workflow for later upstream releases.
19
20
  Review output defaults to English. Set `OCR_REVIEW_LANGUAGE=Russian` to use Russian consistently in both OCR configuration and generated review context.
20
21
 
21
22
  Stable distributions are published to [PyPI](https://pypi.org/project/open-code-review-toolkit/) and mirrored as checksum-listed, provenance-attested assets in the corresponding [GitHub Release](https://github.com/xeonvs/open-code-review-toolkit/releases). Development snapshots are published only to TestPyPI.
@@ -38,6 +38,7 @@ ocr-ci = "ocr_toolkit.cli:main"
38
38
 
39
39
  [dependency-groups]
40
40
  dev = [
41
+ "bandit==1.9.4",
41
42
  "build>=1.2.2",
42
43
  "coverage[toml]>=7.6",
43
44
  "hatch-vcs>=0.4",
@@ -60,19 +61,7 @@ version-file = "src/ocr_toolkit/_version.py"
60
61
  packages = ["src/ocr_toolkit"]
61
62
 
62
63
  [tool.hatch.build.targets.sdist]
63
- include = [
64
- "/src",
65
- "/tests",
66
- "/examples",
67
- "/docs",
68
- "/changelog.d",
69
- "/README.md",
70
- "/CONTRIBUTING.md",
71
- "/SECURITY.md",
72
- "/LICENSE",
73
- "/CHANGELOG.md",
74
- "/pyproject.toml",
75
- ]
64
+ only-include = ["src", "README.md", "LICENSE", "pyproject.toml"]
76
65
 
77
66
  [tool.hatch.version.raw-options]
78
67
  fallback_version = "0.3.0.dev0"
@@ -101,6 +90,11 @@ exclude = ["src/ocr_toolkit/_version.py"]
101
90
  warn_unreachable = false
102
91
  disable_error_code = ["arg-type", "assignment", "import-not-found", "index", "name-defined", "no-any-return", "no-redef", "union-attr", "unused-ignore", "var-annotated"]
103
92
 
93
+ [tool.bandit]
94
+ exclude_dirs = ["tests"]
95
+ severity = "medium"
96
+ confidence = "medium"
97
+
104
98
  [tool.pytest.ini_options]
105
99
  addopts = "--strict-config --strict-markers"
106
100
  testpaths = ["tests"]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.2.1'
22
- __version_tuple__ = version_tuple = (0, 2, 1)
21
+ __version__ = version = '0.3.0'
22
+ __version_tuple__ = version_tuple = (0, 3, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -33,9 +33,17 @@ def build_parser() -> argparse.ArgumentParser:
33
33
 
34
34
  post_parser = subparsers.add_parser("post", help="Publish an OCR result artifact to GitLab.")
35
35
  post_parser.add_argument(
36
- "--result", default="/tmp/ocr-result.json", help="OCR JSON result path."
36
+ # The public GitLab example writes this fixed path inside an isolated CI job.
37
+ "--result",
38
+ default="/tmp/ocr-result.json", # nosec B108
39
+ help="OCR JSON result path.",
40
+ )
41
+ post_parser.add_argument(
42
+ # The file contains only the current job's OCR stderr and is never shared.
43
+ "--stderr",
44
+ default="/tmp/ocr-stderr.log", # nosec B108
45
+ help="OCR stderr log path.",
37
46
  )
38
- post_parser.add_argument("--stderr", default="/tmp/ocr-stderr.log", help="OCR stderr log path.")
39
47
  return parser
40
48
 
41
49
 
@@ -196,9 +196,9 @@ def parse_pyproject(path: Path) -> dict[str, Any]:
196
196
 
197
197
  try:
198
198
  try:
199
- import tomllib
199
+ import tomllib # type: ignore[import-untyped]
200
200
  except ModuleNotFoundError:
201
- import tomli as tomllib # type: ignore[import-not-found]
201
+ import tomli as tomllib
202
202
 
203
203
  data = tomllib.loads(text)
204
204
  except ModuleNotFoundError:
@@ -209,9 +209,12 @@ def parse_pyproject(path: Path) -> dict[str, Any]:
209
209
  if not isinstance(data, dict):
210
210
  return {"present": True, "parse_error": JSON_OBJECT_PARSE_ERROR}
211
211
 
212
- project = data.get("project") if isinstance(data.get("project"), dict) else {}
213
- tool = data.get("tool") if isinstance(data.get("tool"), dict) else {}
214
- poetry = tool.get("poetry") if isinstance(tool.get("poetry"), dict) else {}
212
+ raw_project = data.get("project")
213
+ raw_tool = data.get("tool")
214
+ project: dict[Any, Any] = raw_project if isinstance(raw_project, dict) else {}
215
+ tool: dict[Any, Any] = raw_tool if isinstance(raw_tool, dict) else {}
216
+ raw_poetry = tool.get("poetry")
217
+ poetry: dict[Any, Any] = raw_poetry if isinstance(raw_poetry, dict) else {}
215
218
 
216
219
  dependencies: list[str] = []
217
220
  if isinstance(project.get("dependencies"), list):
@@ -233,12 +236,14 @@ def parse_pyproject(path: Path) -> dict[str, Any]:
233
236
  f"group.{group_name}: {redact_url_userinfo(str(dep))}" for dep in group_deps
234
237
  )
235
238
 
236
- poetry_deps = poetry.get("dependencies") if isinstance(poetry.get("dependencies"), dict) else {}
239
+ raw_poetry_deps = poetry.get("dependencies")
240
+ poetry_deps: dict[Any, Any] = raw_poetry_deps if isinstance(raw_poetry_deps, dict) else {}
237
241
  for name, version in poetry_deps.items():
238
242
  if str(name).lower() != "python":
239
243
  dependencies.append(f"{name}: {redact_url_userinfo(str(version))}")
240
244
 
241
- poetry_groups = poetry.get("group") if isinstance(poetry.get("group"), dict) else {}
245
+ raw_poetry_groups = poetry.get("group")
246
+ poetry_groups: dict[Any, Any] = raw_poetry_groups if isinstance(raw_poetry_groups, dict) else {}
242
247
  for group_name, group_value in poetry_groups.items():
243
248
  if not isinstance(group_value, dict):
244
249
  continue
@@ -354,10 +359,14 @@ def parse_composer_json(path: Path) -> dict[str, Any]:
354
359
  if not isinstance(data, dict):
355
360
  return {"present": True, "parse_error": JSON_OBJECT_PARSE_ERROR}
356
361
 
357
- require = data.get("require") if isinstance(data.get("require"), dict) else {}
358
- require_dev = data.get("require-dev") if isinstance(data.get("require-dev"), dict) else {}
359
- config = data.get("config") if isinstance(data.get("config"), dict) else {}
360
- platform = config.get("platform") if isinstance(config.get("platform"), dict) else {}
362
+ raw_require = data.get("require")
363
+ raw_require_dev = data.get("require-dev")
364
+ raw_config = data.get("config")
365
+ require: dict[Any, Any] = raw_require if isinstance(raw_require, dict) else {}
366
+ require_dev: dict[Any, Any] = raw_require_dev if isinstance(raw_require_dev, dict) else {}
367
+ config: dict[Any, Any] = raw_config if isinstance(raw_config, dict) else {}
368
+ raw_platform = config.get("platform")
369
+ platform: dict[Any, Any] = raw_platform if isinstance(raw_platform, dict) else {}
361
370
 
362
371
  platform_items, platform_omitted = limited_manifest_items(
363
372
  [f"{name}: {version}" for name, version in platform.items()]
@@ -433,10 +442,13 @@ def parse_package_json(path: Path) -> dict[str, Any]:
433
442
  if not isinstance(data, dict):
434
443
  return {"present": True, "parse_error": JSON_OBJECT_PARSE_ERROR}
435
444
 
436
- engines = data.get("engines") if isinstance(data.get("engines"), dict) else {}
437
- dependencies = data.get("dependencies") if isinstance(data.get("dependencies"), dict) else {}
438
- dev_dependencies = (
439
- data.get("devDependencies") if isinstance(data.get("devDependencies"), dict) else {}
445
+ raw_engines = data.get("engines")
446
+ raw_dependencies = data.get("dependencies")
447
+ raw_dev_dependencies = data.get("devDependencies")
448
+ engines: dict[Any, Any] = raw_engines if isinstance(raw_engines, dict) else {}
449
+ dependencies: dict[Any, Any] = raw_dependencies if isinstance(raw_dependencies, dict) else {}
450
+ dev_dependencies: dict[Any, Any] = (
451
+ raw_dev_dependencies if isinstance(raw_dev_dependencies, dict) else {}
440
452
  )
441
453
 
442
454
  engines_items, engines_omitted = limited_manifest_items(
@@ -162,11 +162,13 @@ def resolve_output_path(value: str) -> Path | None:
162
162
  requested = Path(value)
163
163
  logical_output = requested if requested.is_absolute() else root_lexical / requested
164
164
  logical_output = logical_output.absolute()
165
- temp_lexical = Path(tempfile.gettempdir()).absolute()
165
+ # tempfile supplies the platform temp root; containment and symlink checks follow.
166
+ temp_lexical = Path(tempfile.gettempdir()).absolute() # nosec B108
166
167
  allowed_roots = [(root_lexical, root), (root, root)]
167
168
  temp_resolved = temp_lexical.resolve()
168
169
  allowed_roots.extend([(temp_lexical, temp_resolved), (temp_resolved, temp_resolved)])
169
- tmp_path = Path("/tmp")
170
+ # POSIX /tmp may be a distinct lexical alias (for example, /private/tmp).
171
+ tmp_path = Path("/tmp") # nosec B108
170
172
  if tmp_path.exists():
171
173
  tmp_lexical = tmp_path.absolute()
172
174
  tmp_resolved = tmp_path.resolve()
@@ -125,7 +125,7 @@ def _open_gitlab_request(request: urllib.request.Request) -> Any:
125
125
  def _read_limited_response(response: Any) -> bytes:
126
126
  """Read a GitLab response body and fail when it exceeds the success budget."""
127
127
 
128
- body = response.read(MAX_API_RESPONSE_BODY_BYTES)
128
+ body: bytes = response.read(MAX_API_RESPONSE_BODY_BYTES)
129
129
  if len(body) >= MAX_API_RESPONSE_BODY_BYTES and response.read(1):
130
130
  raise GitLabResponseTooLarge(f"GitLab response exceeds {MAX_API_RESPONSE_BODY_BYTES} bytes")
131
131
  return body
@@ -323,11 +323,13 @@ def fetch_current_user_id(server_url: str, api_token: str, auth_header: str) ->
323
323
  return None
324
324
 
325
325
  raw_user_id = result.get("id")
326
- try:
327
- return int(raw_user_id)
328
- except (TypeError, ValueError):
329
- print_user_id_failure_banner("GET /user response has no valid id field")
330
- return None
326
+ if isinstance(raw_user_id, (str, int, float)) and not isinstance(raw_user_id, bool):
327
+ try:
328
+ return int(raw_user_id)
329
+ except ValueError:
330
+ pass
331
+ print_user_id_failure_banner("GET /user response has no valid id field")
332
+ return None
331
333
 
332
334
 
333
335
  def load_gitlab_config() -> GitLabConfig | None:
@@ -481,10 +483,12 @@ def draft_note_id(value: Any) -> int | None:
481
483
  if isinstance(raw_id, bool):
482
484
  return None
483
485
 
484
- try:
485
- return int(raw_id)
486
- except (TypeError, ValueError):
487
- return None
486
+ if isinstance(raw_id, (str, int, float)) and not isinstance(raw_id, bool):
487
+ try:
488
+ return int(raw_id)
489
+ except ValueError:
490
+ pass
491
+ return None
488
492
 
489
493
 
490
494
  def created_draft_note(response: Any, context: str) -> DraftNoteCreation | None:
@@ -240,10 +240,12 @@ def author_id_from_note(note: dict[str, Any]) -> int | None:
240
240
  if isinstance(author, dict):
241
241
  raw_author_id = author.get("id")
242
242
 
243
- try:
244
- return int(raw_author_id)
245
- except (TypeError, ValueError):
246
- return None
243
+ if isinstance(raw_author_id, (str, int, float)) and not isinstance(raw_author_id, bool):
244
+ try:
245
+ return int(raw_author_id)
246
+ except ValueError:
247
+ pass
248
+ return None
247
249
 
248
250
 
249
251
  def is_diff_note(note: dict[str, Any]) -> bool:
@@ -131,7 +131,7 @@ def changed_new_lines(
131
131
  timeout=15,
132
132
  )
133
133
  except (OSError, subprocess.SubprocessError):
134
- lines = set()
134
+ lines: set[int] = set()
135
135
  if cache is not None:
136
136
  cache[cache_key] = lines
137
137
  return lines
@@ -217,7 +217,7 @@ def head_file_lines(
217
217
  size_result.returncode != 0
218
218
  or int(size_result.stdout.strip() or "0") > MAX_REMAP_FILE_BYTES
219
219
  ):
220
- lines = []
220
+ lines: list[tuple[int, str]] = []
221
221
  if cache is not None:
222
222
  cache[cache_key] = lines
223
223
  return lines
@@ -827,8 +827,9 @@ def main(argv: list[str] | None = None) -> int:
827
827
  """CLI entrypoint."""
828
828
 
829
829
  args = argv if argv is not None else sys.argv[1:]
830
- result_path = Path(args[0]) if len(args) >= 1 else Path("/tmp/ocr-result.json")
831
- stderr_path = Path(args[1]) if len(args) >= 2 else Path("/tmp/ocr-stderr.log")
830
+ # Defaults are per-job files in the isolated CI container; callers can override both.
831
+ result_path = Path(args[0]) if len(args) >= 1 else Path("/tmp/ocr-result.json") # nosec B108
832
+ stderr_path = Path(args[1]) if len(args) >= 2 else Path("/tmp/ocr-stderr.log") # nosec B108
832
833
 
833
834
  config = load_gitlab_config()
834
835
  if config is None:
@@ -1,61 +0,0 @@
1
- ## 0.2.1 - 2026-07-27
2
-
3
- ### Features
4
-
5
- - Target Open Code Review 1.7.17 in preflight validation and the checksum-pinned GitLab CI example. ([#12](https://github.com/xeonvs/open-code-review-toolkit/issues/12))
6
-
7
- ### Documentation
8
-
9
- - Document the durable toolkit strategy, milestone roadmap, and reconciled implementation backlog. ([#13](https://github.com/xeonvs/open-code-review-toolkit/issues/13))
10
-
11
-
12
- ## 0.2.0 - 2026-07-21
13
-
14
- ### Features
15
-
16
- - Target Open Code Review 1.7.14 in preflight validation and the checksum-pinned GitLab CI example. ([#11](https://github.com/xeonvs/open-code-review-toolkit/issues/11))
17
- - Replace the ambiguous `/ocr keep` and `/ocr skip` discussion replies with `/ocr resolve` and `/ocr suppress`, preserve human-owned deduplication, and document the complete GitLab review lifecycle for developers and CI operators. ([#8](https://github.com/xeonvs/open-code-review-toolkit/issues/8))
18
-
19
- ### Bug fixes
20
-
21
- - Allow stable release verification to coexist with previously published development builds of the same base version on TestPyPI. ([#10](https://github.com/xeonvs/open-code-review-toolkit/issues/10))
22
- - Treat ordinary merged pull requests as a successful no-op in the production release workflow while keeping release-branch authorization fail-closed. ([#7](https://github.com/xeonvs/open-code-review-toolkit/issues/7))
23
-
24
- ### Security
25
-
26
- - Mark every source-distribution smoke install as hash-required while retaining the no-dependency boundary, and document the single-maintainer security posture and Scorecard triage policy. ([#6](https://github.com/xeonvs/open-code-review-toolkit/issues/6))
27
-
28
- ### Documentation
29
-
30
- - Reduce the routine Ubuntu CI matrix to the supported Python 3.10 and 3.14 endpoints, matching the macOS matrix. ([#10](https://github.com/xeonvs/open-code-review-toolkit/issues/10))
31
- - Document accepted project decisions, their optional `ocr-accept` marker convention, and the guard that prevents a merge request from whitelisting its own findings. ([#11](https://github.com/xeonvs/open-code-review-toolkit/issues/11))
32
-
33
-
34
- ## 0.1.0 - 2026-07-20
35
-
36
- ### Features
37
-
38
- - Publish one deterministic, checksum-verified TestPyPI development build after every merge into `main`, with bounded registry downloads and idempotent reruns. ([#4](https://github.com/xeonvs/open-code-review-toolkit/pull/4))
39
- - Target Open Code Review 1.7.13 in preflight validation and the pinned GitLab CI example. ([#4](https://github.com/xeonvs/open-code-review-toolkit/pull/4))
40
- - Support and continuously test Python 3.14 while retaining Python 3.10-3.13 compatibility. ([#4](https://github.com/xeonvs/open-code-review-toolkit/pull/4))
41
- - Publish reproducible 0.1.0 distributions to TestPyPI and PyPI with exact hash verification, provenance attestations, and immutable GitHub Release assets.
42
- - Introduce the standalone `ocr-ci` toolkit with safe context generation, GitLab posting, runtime configuration, MCP configuration, and preflight checks.
43
-
44
- ### Bug fixes
45
-
46
- - Bind production release smoke tests to the exact reviewed wheel and sdist hashes, with bounded HTTPS downloads from TestPyPI and PyPI. ([#4](https://github.com/xeonvs/open-code-review-toolkit/pull/4))
47
- - Use `OCR_REVIEW_LANGUAGE` as the single safe language setting for OCR configuration and generated review context, with English as the default and Russian as an explicit option. ([#4](https://github.com/xeonvs/open-code-review-toolkit/pull/4))
48
- - Preserve bounded context and version discovery with a 7,950-character ceiling, improve provider billing classification, and prevent cross-file remapping of findings that already name a path. ([#3](https://github.com/xeonvs/open-code-review-toolkit/pull/3))
49
-
50
- ### Security
51
-
52
- - Require secure credential endpoints, block unsafe GitLab redirects, redact secret-shaped environment values, and reduce GitHub Actions credential persistence and permissions. ([#3](https://github.com/xeonvs/open-code-review-toolkit/pull/3))
53
-
54
- ### Documentation
55
-
56
- - Document the checksum-verified TestPyPI prerelease path used before the public stable release. ([#2](https://github.com/xeonvs/open-code-review-toolkit/pull/2))
57
-
58
-
59
- # Changelog
60
-
61
- Changes for each release are assembled from `changelog.d/` by Towncrier.
@@ -1,14 +0,0 @@
1
- # Contributing
2
-
3
- Thank you for improving Open Code Review Toolkit. Keep changes focused, add regression tests for behavior changes, and use synthetic examples only.
4
-
5
- 1. Create a branch from `main`.
6
- 2. Install the locked development environment with `uv sync --frozen`.
7
- 3. Update `PLANS.md` for substantial work.
8
- 4. Add a Towncrier fragment for user-visible changes.
9
- 5. Run the checks in [docs/development.md](docs/development.md).
10
- 6. Open a pull request; protected `main` accepts changes only through green pull requests.
11
-
12
- Every non-release merge publishes a `0.2.0.devN` development build to TestPyPI. Stable releases use a repository-owned `release/vX.Y.Z` branch and an exact `Release vX.Y.Z` pull-request title; merging that reviewed PR authorizes the automated TestPyPI, PyPI, and GitHub Release chain.
13
-
14
- Do not include real credentials, provider payloads, internal hosts, or private repository details. Security reports should follow [SECURITY.md](SECURITY.md), not public issues.
@@ -1,13 +0,0 @@
1
- # Security Policy
2
-
3
- ## Supported versions
4
-
5
- Before 1.0, only the newest published release receives security fixes.
6
-
7
- ## Reporting a vulnerability
8
-
9
- Use [GitHub private vulnerability reporting](https://github.com/xeonvs/open-code-review-toolkit/security/advisories/new) for this repository. Do not open a public issue or attach live credentials, proprietary source, or production provider responses. Include a minimal synthetic reproducer, impact, affected version, and suggested mitigation when available.
10
-
11
- Maintainers will acknowledge a report within five business days, privately validate severity and affected versions, and coordinate disclosure only after a fix or documented mitigation is available. Critical fixes are released as soon as practical; reporters receive status updates at least every ten business days while investigation remains open.
12
-
13
- The runtime trust model and deployment guidance are documented in [docs/security.md](docs/security.md).
@@ -1,3 +0,0 @@
1
- # Changelog fragments
2
-
3
- Every user-visible 0.x change needs one fragment named `<issue>.<type>.md`, where type is `feature`, `bugfix`, `security`, or `doc`. Use a numeric GitHub issue or pull-request identifier so generated links remain valid.
@@ -1,44 +0,0 @@
1
- # Agent Execution Pitfalls
2
-
3
- ## Closing a public-contract change after only the feature merge
4
-
5
- **Failure mode:** A feature changes a public command or integration contract, its pull request merges, and a TestPyPI `.devN` build succeeds. The plan is then marked completed even though stable PyPI users still receive the old behavior.
6
-
7
- **Why it happens:** Implementation, preview publication, and stable delivery are treated as separate mental tasks even when the user asked for one outcome. SCM-derived versions also make the source tree appear ready for the next version without proving that a stable tag or package exists.
8
-
9
- **Required prevention:**
10
-
11
- 1. Classify the work at plan start and write the target stable version into `PLANS.md`.
12
- 2. Treat feature merge and TestPyPI `.devN` verification as intermediate receipts.
13
- 3. When release is required, prepare the version/changelog release PR immediately after the development build is verified.
14
- 4. Keep the objective active until the release workflow publishes and independent readback confirms PyPI, TestPyPI, the signed tag, immutable GitHub Release, hashes, attestations, and supported-Python installs.
15
- 5. If publication is intentionally deferred, record who deferred it, why, and the exact command or PR needed to resume.
16
-
17
- **Closure question:** "Can a user installing from production PyPI obtain the promised behavior now?" If not, the stable-release objective is not complete.
18
-
19
- This note records recurring execution mistake patterns discovered during real work. Record generalized lessons, not one-off complaints.
20
-
21
- ## Planning And Context Discipline
22
-
23
- - Do not leave durable scope, source boundaries, or resume state only in chat; keep the active plan current.
24
- - Do not treat a local working specification as publishable documentation; translate its public requirements and keep private audit criteria outside tracked files.
25
- - Do not silently reduce extraction scope because the source is large; split it into coherent, validated subsystem slices.
26
- - Do not commit completed work while the active plan still says `planned` or `in_progress`.
27
-
28
- ## Source And Privacy Boundaries
29
-
30
- - Inventory tracked source explicitly and avoid broad copy commands that could include ignored or untracked files.
31
- - Do not turn one-time private marker criteria into a tracked denylist or test fixture.
32
- - Do not use real provider payloads, hosts, repositories, or credentials in public fixtures.
33
-
34
- ## Tooling And Validation Hygiene
35
-
36
- - Prefer the narrowest reproducer before broad reruns.
37
- - Verify both UTF-8 byte limits and Python character limits when changing note formatting.
38
- - Treat tests, lint, typing, artifact checks, install smoke, privacy scans, and source-integrity checks as distinct gates.
39
- - Pin third-party Actions by full commit SHA and keep readable version comments beside the pin.
40
-
41
- ## Learning Loop
42
-
43
- - Promote a repeated stable lesson into `docs/engineering/project_principles.md`.
44
- - Record actionable future work in `docs/codex/TASKS_BACKLOG.md` only when it has an activation trigger and next safe action.