GitHub issues review: proposal + fixes for #176, #181 (scan), #178 aliases, #173 ownership guard - #182
GitHub issues review: proposal + fixes for #176, #181 (scan), #178 aliases, #173 ownership guard#182ohjonathan wants to merge 7 commits into
Conversation
A depends_on entry such as 'pyproject.toml' (no separator, no .md suffix) bypassed filesystem resolution entirely in _resolve_depends_on_path and was reported as a hard broken_link even when the file existed at the workspace root and was listed in validation.allowed_external_dependency_paths, while './pyproject.toml' resolved as an external file dependency. Bare tokens now get a stricter probe than explicit path spellings: - active document IDs keep deterministic precedence (unchanged); - only existing regular files count — a bare token matching a directory stays a broken link; - the existing symlink-containment and loaded-path/inode collision logic applies unchanged; - when the workspace-root and declaring-doc candidates name two different files, resolution fails closed instead of picking by ordering accident; - the allowlist matches the resolved workspace-relative path, so bare and ./ spellings classify identically. Explicit-path behavior, including its characterized handling of directories, is byte-for-byte unchanged. All consumers (activate, doctor, map, link-check, hooks, MCP) share this resolver via build_graph. Acceptance matrix from the v5 remediation plan §11.2 covered in tests/core/test_graph.py::TestBareRootFileDependencies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
The default skip pattern 'archive/*' was matched with a right-anchored, segment-local Path.match plus a full-path fnmatch, so it only excluded files DIRECTLY under an archive/ directory. The nested layout that 'ontos consolidate' itself writes by default (docs/archive/logs/...) was scanned straight back into the live graph — the opposite of the archive contract — and every nested file under node_modules/ (e.g. package READMEs one level down) leaked into scans the same way. scan_documents now additionally matches each skip pattern against every path-segment suffix with fnmatch (where '*' crosses '/'), so directory patterns exclude the whole subtree. Basename patterns such as '_template.md' stay exact-match only, and a file literally named archive.md is still scanned. Behavior note: repositories with nested archive layouts (or markdown under node_modules/) will correctly see lower document counts; library scope now excludes .ontos-internal/archive/ like every other scope. Found while reviewing GitHub issue #181 — this slice delivers the issue's core 'archive out of the live graph' premise, which was not actually true for the layout consolidate produces. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
…mapping (#178) Mature repositories carry project vocabulary ('runbook', 'provider_limited_fallback_complete', the ubiquitous agent-emitted 'in-progress') that the hard-coded repair tables cannot map, leaving maintain --fix-frontmatter-enums reporting invalid values with zero repairable — and one unresolved value blocks the entire apply. - New [frontmatter.aliases.type] / [frontmatter.aliases.status] tables in .ontos.toml declare explicit project mappings. Validation is fail-closed at config load: targets must be canonical enum values ('unknown' is never a valid target, so chains/cycles are impossible by construction), alias keys must not themselves be canonical, keys are lowercased with case-normalized duplicates rejected. - Built-in 'in-progress' -> 'in_progress' repair (v5 remediation plan §11.4): the canonical status existed but its most common spelling had no mapping. - Config aliases win over built-ins; every repair edit now reports its mapping source ('built-in' | 'config') in dry-run/apply JSON. - Threaded through both consumers of build_enum_repair_plan: maintain --fix-frontmatter-enums and doctor --frontmatter. - Manual documents the new tables and the forward-compatibility caveat: older installs hard-reject unknown config sections, so repositories adopting [frontmatter] should pin [ontos].required_version. Existing guarantees (plan-first deterministic JSON, original_type/ original_status preservation, formatting/BOM/CRLF safety, clean-git guard, refusing partial applies) are reused unchanged. Deferred per the issue review: custom types extending canonical types and custom statuses with lifecycle classes — a cross-cutting refactor of the str-Enum normalization boundary (~98 call sites) that original_* retention does not require. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
…ess (#173) AGENTS.md freshness was a pure mtime comparison in both doctor and maintain. That signal is reproducibly wrong across clones, worktrees, and touch-only changes: two checkouts of the same tree report opposite freshness depending on checkout write order. Worse, maintain's sync task acted on the false signal with a forced regeneration — a plain 'ontos maintain' in a fresh clone replaced a fully hand-authored AGENTS.md with generated content (only a .bak survived). - classify_instruction_ownership() distinguishes ontos_owned (generation header present), managed_block (USER CUSTOM markers only), and user_managed (neither). Unreadable files fail closed to user_managed. - maintain's sync task, its staleness condition, and map --sync-agents all refuse to touch a file that is not ontos_owned, with an explicit 'run ontos agents --force to adopt' message. Explicit adoption via 'ontos agents --force' is unchanged. - Staleness for Ontos-owned files is now decided by rendering the expected content and comparing through the existing timestamp-normalizing semantic comparator — content, not mtime. Identical trees now agree on freshness at any absolute path or device. - The touch_on_unchanged hack (touching AGENTS.md to game the mtime signal) is retired with the signal it gamed; a semantic no-op leaves bytes AND metadata untouched. - doctor reports ownership instead of false staleness for non-Ontos files. Mtime-pinned tests rewritten to pin the content contract, including a clone-equivalence regression test mirroring the reproduced failure. Remainder of #173 (portable graph/config digests, activation receipts) is designed in docs/specs/project-ontos-github-issues-review-2026-07-proposal.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
…ted artifacts - docs/specs/project-ontos-github-issues-review-2026-07-proposal.md: verdicts and dispositions for all ten open issues (#149, #158, #165, #173-#178, #181) — every verdict adversarially verified against v5.0.2 code with end-to-end reproductions of the bug claims — plus designs for the deferred slices (#175 map --check/merge-check, #177 wikilink namespaces, #174 finding IDs and findings diff, #173 digest core, #181 archive manifest/verify, #165 audit registry) and the cross-cutting contract constraints binding future work. - CHANGELOG.md: Unreleased entries for the four implemented slices with behavior notes. - Session log per the Ontos session-end ritual; Ontos_Context_Map.md and AGENTS.md regenerated via ontos map --sync-agents (activation is clean: 234 docs, 0 errors, 0 warnings). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
Review outcome: request changesI reviewed the full issue set, the merged v5 remediation plan, the implementation at 0acd0bf, CI, the added tests, and adversarial cases beyond the committed fixtures. The direction is valuable and the PR fixes real bugs, but I do not think PR #182 should merge as-is. Several safety, portability, and compatibility claims fail under adversarial testing. Blocking findings
The new skip matcher walks suffixes of the absolute filesystem path and applies fnmatch. A checkout beneath an ancestor directory named archive, node_modules, pycache, or another configured skip name can therefore scan zero documents. Reproduced:
It also silently makes custom patterns recursive: reviews/.md excludes reviews/team/nested.md, and reviews//draft.md can exclude deeper paths than requested. Code: Project-Ontos/ontos/io/files.py Lines 169 to 187 in 0acd0bf Match only workspace- or scan-root-relative paths. Define segment-local * semantics and explicit recursive syntax such as /**.
Any line beginning “Generated by Ontos v” anywhere in a file is accepted as proof of whole-file Ontos ownership. I reproduced a hand-authored AGENTS.md containing that text as an example being classified ontos_owned and selected for implicit replacement. A backup helps recovery, but does not meet the “never overwrite user-owned instructions” contract. Code: Project-Ontos/ontos/core/instruction_artifacts.py Lines 305 to 343 in 0acd0bf Require an exact, fixed-position, versioned ownership sentinel plus structural validation. USER CUSTOM markers should not imply Ontos ownership; real managed blocks need separate ONTOS MANAGED START/END markers.
Generated instructions embed the current branch, while semantic comparison normalizes only timestamps. At identical HEAD:
The freshness API also reads the passed repository root but regenerates expected content from the process CWD, allowing cross-repository false staleness. Code: Project-Ontos/ontos/core/instruction_artifacts.py Lines 175 to 183 in 0acd0bf Comparator: Project-Ontos/ontos/core/instruction_artifacts.py Lines 349 to 370 in 0acd0bf Branch, HEAD, dirty state, root basename, and timestamps should not participate in portable committed artifact bytes. Keep checkout binding in activation output or a separate receipt.
A configured alias with a quoted # value can corrupt the preserved original value. For example, status: "qa#blocked" was repaired successfully but produced original_status: '"qa' rather than qa#blocked, because the lexical extractor treats # inside the quoted YAML scalar as a comment. Code: Project-Ontos/ontos/core/frontmatter_repair.py Lines 237 to 257 in 0acd0bf Default config serialization also always emits an empty [frontmatter.aliases] table. Ontos v5.0.2 rejects that section even when aliases are unused. Adding required_version >=5.1 does not help because the old client rejects the unknown section before checking the version. Config serialization: Project-Ontos/ontos/core/config.py Lines 122 to 156 in 0acd0bf There is also a contract conflict: configured aliases can override built-ins—for example in-progress to complete—while the issue acceptance language calls for conflicts to fail closed. For a patch release, ship only the built-in alias. Move workspace-configured vocabulary to v5.1 after designing a compatible extension mechanism.
The core bare-root dependency bug is fixed, but ambiguity is collapsed into the same null result as “missing.” The caller then says a dependency does not exist and recommends creating it even when two candidates exist. A loaded-document match can also return before inspecting the second candidate. Code: Project-Ontos/ontos/core/graph.py Lines 169 to 204 in 0acd0bf Return a typed resolution result such as document, external_file, missing, ambiguous, escape, or unsupported. Ambiguity diagnostics should list relative candidates and require an explicit path.
MCP cache freshness still treats (mtime_ns, size) as authoritative. I rewrote a file with same-length content and restored its original mtime. MCP retained revision 1 and served the old content. Code: Project-Ontos/ontos/mcp/cache.py Lines 194 to 218 in 0acd0bf Adding digests to generated artifacts is insufficient while another surface continues using mutable filesystem metadata as its source of truth. Claim verification
The passing suite is useful evidence, but it does not cover the checkout-location, different-branch worktree, quoted-# scalar, older-client parser-order, or same-size/same-mtime cache cases above. Corrections to the issue proposal
The merged remediation plan explicitly limits the v5.0.3 slice more narrowly: Recommended architectureThe deeper opportunity is to stop treating map generation, doctor, MCP, merge checking, portfolio discovery, and agent instructions as separate filesystem readers. Create one immutable WorkspaceAnalysis snapshot containing:
Map, doctor, MCP, audit, portfolio, and merge-check should become pure consumers of that snapshot. For multi-repository and multi-device use, portable catalogs should be content-addressed and support references such as ontos://workspace-id/document-id. Absolute paths, checkout basenames, branches, mtimes, and dirty state belong in local binding metadata—not semantic identity. Recommended dispositionSplit this into three PRs:
The PR contains worthwhile work, particularly the core #176 fix and intended nested archive fix, but the blockers above should be addressed before merge. |
All five code findings from the review reproduced and are fixed with
regression tests pinning each reproduction:
1 (P1, scan): the segment-suffix skip matcher walked ABSOLUTE path
suffixes, so a checkout under an ancestor directory named archive/
node_modules/__pycache__ scanned zero documents, and fnmatch's
separator-crossing '*' silently made custom patterns recursive
(reviews/*.md excluded reviews/team/nested.md). Rewritten: patterns
match contiguous runs of workspace-relative (or scan-root-relative)
segments, '*' is segment-local, '/**' is explicit subtree syntax, and
ancestors outside the repository never participate.
2 (P1, ownership): 'Generated by Ontos v' anywhere in a file conferred
ownership, so a hand-authored file quoting the phrase was eligible for
implicit replacement. Ownership now requires the byte-exact versioned
header line in its structural template position (file head, preceded
only by known template head lines); anything else fails closed to
user_managed with the explicit --force adoption path.
3 (P1, staleness): the current branch is checkout-local state and is now
normalized out of the semantic comparison, so two worktrees at the same
HEAD agree on freshness; generate_agents_content and
is_agents_semantically_stale take an explicit repo_root so freshness is
never computed against the process CWD's repository.
4 (P2, aliases): original_* extraction is quote-aware ('#' inside a
quoted scalar is content; unquoted '#' opens a comment only after
whitespace; malformed quoting falls back to the parsed value);
config_to_dict omits an empty [frontmatter] section so defaults stay
readable by <=5.0.2; a configured alias redefining a built-in mapping
to a different target is a fail-closed config conflict; the manual's
compatibility wording corrected (old clients reject the unknown section
before required_version is evaluated).
5 (P2, #176 ambiguity): bare-token resolution returns a typed result,
both candidate bases are evaluated before deciding (a loaded-doc match
cannot shadow a different file at the other base), and ambiguity emits
an explicit diagnostic naming the workspace-relative candidates
(reason: ambiguous_bare_candidates) instead of a false 'does not
exist'.
Finding 6 (MCP mtime-fingerprint cache freshness) is acknowledged and
tracked as the #173 remainder — it must land batched with the pinned
freshness_mode contract and MCP schema/parity updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
Records the adversarial-review dispositions in the proposal spec (fixed findings 1-5, tracked finding 6, and accepted corrections to the #174/ #175/#177/#181/#158 designs including the finding_key/finding_id split, merge_safe=false on source conflicts, the unresolved-wikilink downgrade hazard, manifest-vs-catalog scope, and the #158 custody-rule amendment), corrects the frozen-paths claim in place, and updates the Unreleased changelog entries to match the hardened behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
|
Thanks for the adversarial review — all six findings verified as real. I reproduced each one before fixing. Status as of Fixed in
Acknowledged, not in this PR:
Proposal corrections — all accepted and recorded in the spec's new Review Addendum (§5): the Left to you: the three-PR split and release scoping (this branch keeps each concern in separate commits so it can be split or cherry-picked either way — e.g. a strict-§11.5 v5.0.3 could take Generated by Claude Code |
Summary
Reviews all ten open issues (#149, #158, #165, #173–#178, #181) against the v5.0.2 codebase — every verdict adversarially verified, every bug claim reproduced end-to-end — and lands the four highest-value remediation slices plus a consolidated disposition/design document (
docs/specs/project-ontos-github-issues-review-2026-07-proposal.md).Changes
ontos/core/graph.py) — bare workspace-root files (pyproject.toml,Makefile,LICENSE) resolve as dependencies without a./prefix through a strict fail-closed probe: regular files only, doc-ID precedence preserved, symlink containment unchanged, ambiguous workspace-root vs declaring-doc candidates fail closed. Regression tests mirror the v5 remediation plan §11.2 acceptance matrix.ontos/io/files.py) — skip patterns like the defaultarchive/*now exclude whole subtrees; previously the nesteddocs/archive/logs/…layout thatontos consolidateitself writes was scanned back into the live graph (as was nested markdown undernode_modules/). Behavior note: repos with nested archives will correctly see lower doc counts.ontos/core/config.py,ontos/core/frontmatter_repair.py) — new[frontmatter.aliases.type]/[frontmatter.aliases.status]tables with fail-closed validation (targets must be canonical, neverunknown; keys must not be canonical; no chains possible by construction), built-inin-progress → in_progressrepair (plan §11.4), per-editsourcereporting, threaded through bothmaintain --fix-frontmatter-enumsanddoctor --frontmatter. Manual documents therequired_versionpinning caveat.ontos/core/instruction_artifacts.py,maintain/doctor/map) — instruction-file ownership classification (ontos_owned/managed_block/user_managed); implicit syncs never overwrite a file Ontos did not generate (previously a plainontos maintainin a fresh clone destroyed a hand-authored AGENTS.md); staleness is now decided by semantic content comparison instead of mtimes, so clones/worktrees/touch-only changes agree; thetouch_on_unchangedmtime hack is retired.map --check/merge-check, [Feature] Semantic wikilink namespaces and path-scoped body-reference policies #177 wikilink namespaces, [Feature] Stable finding IDs, Git diffing, and no-new-debt validation baselines #174 finding IDs/findings diff, [Feature] Content-addressed activation receipts and portable AGENTS/map freshness #173 digest core, [FEATURE] External archive backends for integrity-bound cold storage #181 archive manifest/verify, Machine-readable audit registry and external parity gate #165 audit registry), CHANGELOG entries, session log, regeneratedOntos_Context_Map.mdandAGENTS.md.Related Issues
Fixes #176. Advances #181, #178, #173 (slice checkboxes; issues stay open per their remaining scope). Design dispositions recorded for #174, #175, #177, #165; board-maintenance plan for #158/#149 in proposal §3.9.
Note: per plan §11.8, #176 closes only after a released artifact passes the reproduction — auto-close on merge is earlier than that gate; reopen or defer closure if you want to hold to it strictly.
Checklist
ontos activate: usable, 234 docs, 0 errors, 0 warnings;ontos doctor: 10 passed, 0 failed)Testing
Full suite green at every commit. New/rewritten coverage: §11.2 acceptance matrix for bare deps (
tests/core/test_graph.py::TestBareRootFileDependencies), nested-archive skip semantics (tests/io/test_scan_skip_patterns.py), alias config validation + repair round-trips incl. CLI end-to-end (tests/core/test_config_phase3.py,tests/core/test_frontmatter_repair.py,tests/commands/test_agentic_activation_resilience.py), ownership + clone-equivalence regression (tests/core/test_instruction_ownership.py). End-to-end manual verification: issue #176's exact scenario flips frombroken_link/exit 1 to allowlistedexternal_file_dependency/exit 0 acrossactivate,doctor, andlink-check; a hand-authored AGENTS.md survivesontos maintainandontos map --sync-agentsbyte-identical.🤖 Generated with Claude Code
https://claude.ai/code/session_019poEo7Q6nsQkayuZ7v5dP5
Generated by Claude Code