diff --git a/docs/backlog-priority.md b/docs/backlog-priority.md index 1b7eb795..16ecda58 100644 --- a/docs/backlog-priority.md +++ b/docs/backlog-priority.md @@ -59,7 +59,7 @@ was met on 2026-06-22; completing that intake does not authorize new work. | Tag | Meaning | | --- | --- | -| `friction-driven` | Owner friction log evidence or closed hypothesis promoted after validation | +| `friction-driven` | Owner friction log evidence or a hypothesis promoted (status `promoted`) to owner evidence after validation | | `governance-gap` | Security/audit/approval correctness; not UX speculation | | `legacy-competitive` | Pre–2026-06-13 competitive positioning or survey machinery | | `owner-override` | Explicit owner direction record or dated override rationale | diff --git a/docs/generated/docs-aging-dashboard.md b/docs/generated/docs-aging-dashboard.md index 3471be19..e2756433 100644 --- a/docs/generated/docs-aging-dashboard.md +++ b/docs/generated/docs-aging-dashboard.md @@ -4,7 +4,7 @@ **Stale threshold:** 90 days since `Last reviewed` **Current-truth docs scanned:** 17 -**Needs attention (working tier only):** 4 +**Needs attention (working tier only):** 6 **Archive-tier docs (exempt from staleness):** 0 Regenerate: `python3 scripts/report_docs_aging.py` @@ -30,6 +30,18 @@ Regenerate: `python3 scripts/report_docs_aging.py` | --- | --- | --- | --- | --- | | `README.md` | stale_by_mtime | 2026-09-15 | 2026-09-24 | Missing owner banner; File modified after last reviewed date | +### roadmap + +| Document | Status | Last reviewed | File mtime | Notes | +| --- | --- | --- | --- | --- | +| `docs/roadmap-status.md` | stale_by_mtime | 2026-10-09 | 2026-10-10 | Missing review trigger banner; File modified after last reviewed date | + +### strategy + +| Document | Status | Last reviewed | File mtime | Notes | +| --- | --- | --- | --- | --- | +| `docs/backlog-priority.md` | stale_by_mtime | 2026-10-09 | 2026-10-10 | Missing owner banner; File modified after last reviewed date | + ## Review Triggers (Current-Truth Docs) | Document | Owner | Tier | Review trigger | diff --git a/docs/generated/docs-inventory.md b/docs/generated/docs-inventory.md index 1b25c29f..a30cb37f 100644 --- a/docs/generated/docs-inventory.md +++ b/docs/generated/docs-inventory.md @@ -197,7 +197,7 @@ Do not edit this file manually — regenerate instead. | `architecture/seven-control-loops-teaagent-integration-map-2026-06-05.md` | archive | 9282 | `6d85e832f8eb` | | `archive/INDEX.md` | working | 8774 | `1efa1af575f5` | | `audit-events.md` | working | 2376 | `b83066076fba` | -| `backlog-priority.md` | working | 41728 | `3d032d17ccfd` | +| `backlog-priority.md` | working | 41761 | `675c2f952524` | | `cli.md` | working | 42072 | `a02a140f784d` | | `cloud-deployment.md` | working | 9439 | `b27aada996c9` | | `context-bus-and-federated-sync.md` | working | 1564 | `2dde6eed4bf8` | @@ -245,8 +245,8 @@ Do not edit this file manually — regenerate instead. | `error-reference.md` | working | 4897 | `067448aba6f1` | | `gateway-oauth-tenants.md` | working | 1818 | `0ff9c7df4652` | | `generated/command-snippet-inventory.md` | working | 8128 | `bf21da5dfe66` | -| `generated/docs-aging-dashboard.md` | working | 4492 | `dcd84c84257d` | -| `generated/release-docs-evidence.md` | working | 3943 | `6b807acddeb4` | +| `generated/docs-aging-dashboard.md` | working | 4984 | `413820fd6deb` | +| `generated/release-docs-evidence.md` | working | 3978 | `8d7a0c8de8ab` | | `governance-compliance.md` | constitution | 1645 | `d7665f2f7864` | | `governance/code-review-checklist.md` | working | 4379 | `f580da208c75` | | `governance/command-snippet-registry.md` | working | 3023 | `2e6c2b337773` | @@ -561,7 +561,7 @@ Do not edit this file manually — regenerate instead. | `reviews/project-state-critical-questioning-2026-06-04.md` | archive | 7340 | `78b9b54c3a9c` | | `reviews/roadmap-intent-socratic-2026-09-09.md` | archive | 11911 | `57b215adbe19` | | `reviews/roadmap-rethink-2026-09-16.md` | archive | 28570 | `96e151b6ba06` | -| `reviews/roadmap-review-and-improvement-plan-2026-10-09.md` | archive | 40607 | `1bbcdaf3735a` | +| `reviews/roadmap-review-and-improvement-plan-2026-10-09.md` | archive | 40787 | `5dbfb8c06252` | | `reviews/roadmaps-socratic-panel-2026-09-15-r10.md` | archive | 3861 | `e11b0872d78d` | | `reviews/roadmaps-socratic-panel-2026-09-15-r11.md` | archive | 1823 | `0a831c7afadf` | | `reviews/roadmaps-socratic-panel-2026-09-15-r12.md` | archive | 2308 | `790625d98890` | @@ -582,7 +582,7 @@ Do not edit this file manually — regenerate instead. | `reviews/sec09-sec15-multisig-hardening-risk.md` | working | 11076 | `ae895567bb02` | | `reviews/security-risk-assessment-2026-06-02.md` | archive | 24112 | `4c9e2e00d001` | | `reviews/seven-control-loops-critical-questioning-2026-06-05.md` | archive | 7531 | `ae1e34b8369d` | -| `roadmap-status.md` | constitution | 109091 | `14e3ef82f651` | +| `roadmap-status.md` | constitution | 109542 | `58d4a8f0c1a1` | | `run-evidence-and-audit-guide.md` | working | 2494 | `5ef9c11997d7` | | `security-whitepaper.md` | working | 9690 | `87fc3c31c4a3` | | `security/approval-abuse-cases-2026-06-02.md` | archive | 1281 | `4c43296d1c66` | @@ -671,7 +671,7 @@ Do not edit this file manually — regenerate instead. | `work-log/m4-budget-stays-inline-2026-06-13.md` | archive | 5727 | `0e7a6ee74954` | | `work-log/m4-dogfood-2026-09-15.md` | archive | 4695 | `65911944ecc7` | | `work-log/m5-hooks-observability-only-2026-06-13.md` | archive | 5000 | `8a87eaee4d15` | -| `work-log/operator-friction-log.md` | working | 16847 | `2d581bc22167` | +| `work-log/operator-friction-log.md` | working | 17670 | `2afb4f1612d6` | | `work-log/p0-p1-governance-implementation-ledger-2026-06-11.md` | archive | 5212 | `0b72cd69de32` | | `work-log/parallel-phase-0-implementation-report-2026-06-04.md` | archive | 13181 | `098186167459` | | `work-log/phase-0-governance-closure-report-2026-06-04.md` | archive | 4309 | `2af44267aaf4` | diff --git a/docs/generated/release-docs-evidence.json b/docs/generated/release-docs-evidence.json index b9a6cde6..852b798c 100644 --- a/docs/generated/release-docs-evidence.json +++ b/docs/generated/release-docs-evidence.json @@ -1,10 +1,10 @@ { "ok": true, - "created_at": "2026-10-09T19:20:56+00:00", + "created_at": "2026-10-10T03:41:22+00:00", "repo_root": "/home/user/teaAgent", "git": { "branch": "claude/gracious-wright-p34vu8", - "commit": "b0cb4ef520341ddd7a3cb6befc159a8f962dd6a4", + "commit": "683fb04a246e7b41d2569f00bfbb1e05153f6b95", "dirty": true }, "commands": [ @@ -23,11 +23,13 @@ ], "docs_freshness": { "scanned": 17, - "needs_attention": 4, + "needs_attention": 6, "by_owner": { "project": 1, "daily-driver": 2, - "cli": 1 + "cli": 1, + "roadmap": 1, + "strategy": 1 }, "stale_threshold_days": 90 }, diff --git a/docs/generated/release-docs-evidence.md b/docs/generated/release-docs-evidence.md index 930ac3ed..350f51a3 100644 --- a/docs/generated/release-docs-evidence.md +++ b/docs/generated/release-docs-evidence.md @@ -1,7 +1,7 @@ # Release Documentation Evidence Bundle (Generated) -**Generated:** 2026-10-09T19:20:56+00:00 -**Git commit:** `b0cb4ef520341ddd7a3cb6befc159a8f962dd6a4` on `claude/gracious-wright-p34vu8` +**Generated:** 2026-10-10T03:41:22+00:00 +**Git commit:** `683fb04a246e7b41d2569f00bfbb1e05153f6b95` on `claude/gracious-wright-p34vu8` **Working tree dirty:** yes Regenerate: `python3 scripts/build_release_docs_evidence_bundle.py` @@ -21,11 +21,13 @@ Regenerate: `python3 scripts/build_release_docs_evidence_bundle.py` ## Documentation Freshness - Current-truth docs scanned: **17** -- Needs attention: **4** (>90 days) +- Needs attention: **6** (>90 days) - Stale by owner surface: - `cli`: 1 - `daily-driver`: 2 - `project`: 1 + - `roadmap`: 1 + - `strategy`: 1 ## Roadmap Excerpt diff --git a/docs/reviews/roadmap-review-and-improvement-plan-2026-10-09.md b/docs/reviews/roadmap-review-and-improvement-plan-2026-10-09.md index 2bbd0c39..323eb677 100644 --- a/docs/reviews/roadmap-review-and-improvement-plan-2026-10-09.md +++ b/docs/reviews/roadmap-review-and-improvement-plan-2026-10-09.md @@ -267,7 +267,7 @@ git push origin v0.1.1 Per owner decision 2026-10-09 ("tag only, no PyPI"), `release.yml` builds and attaches artifacts on the tag and its `publish` job runs only on `workflow_dispatch`. Because the tag points at `609657eb`, the dependency bumps in #78–#85 are post-0.1.1 and are listed under `Unreleased` in `CHANGELOG.md`. -**Left for the owner, not fixable from here.** (1) The tag above. (2) The destructive test described in §7.2 left an 8.4 GB zero-filled regular file at `/dev/sda` inside the recording container; deleting it was refused by the session's permission classifier, so it is reported rather than removed (it does not exist on CI or on the owner's machine). (3) Three older friction-log hypotheses that were promoted to F2/F3/F7 remain `open` pending owner confirmation; only the four the owner named were closed. (4) Nothing else: after #76 no production code changed, only dependency floors, the lockfile, and the one test. +**Left for the owner, not fixable from here.** (1) The tag above. (2) The destructive test described in §7.2 left an 8.4 GB zero-filled regular file at `/dev/sda` inside the recording container; deleting it was refused by the session's permission classifier, so it is reported rather than removed (it does not exist on CI or on the owner's machine). (3) Three older friction-log hypotheses that were promoted to F2/F3/F7 remained `open` pending owner confirmation; only the four the owner named were closed. *Resolved 2026-10-10:* the owner confirmed them and chose to add a `promoted` status to the log's schema, so they no longer count as open (see the friction log's intake status). (4) Nothing else: after #76 no production code changed, only dependency floors, the lockfile, and the one test. ## 8. Verification of this record diff --git a/docs/roadmap-status.md b/docs/roadmap-status.md index 6bc56801..5f926bf6 100644 --- a/docs/roadmap-status.md +++ b/docs/roadmap-status.md @@ -91,6 +91,8 @@ **Landing 2026-10-09 (record [reviews/roadmap-review-and-improvement-plan-2026-10-09.md §7.4](reviews/roadmap-review-and-improvement-plan-2026-10-09.md)):** PRs #76–#85 merged to `main`, each on a green CI run (`main` green on every run since 862); the eight Dependabot PRs were superseded by trailer-carrying, `uv`-regenerated bumps and closed; one fixed-sleep timing test (`test_heartbeat_writes_liveness_file`) was made deadline-based after a single red run. `v0.1.1` is annotated locally at `609657eb` but the tag push and the refs API are refused by the recording environment's GitHub proxy (HTTP 403), so the tag is **pending owner action**; `release.yml` publishes to PyPI only on `workflow_dispatch`. No horizon, milestone, goal, or gate status moved. +**Friction-log schema 2026-10-10 (owner decision in session):** the three hypotheses promoted to F2/F3/F7 on 2026-06-22 move from `open` to a new terminal `promoted` status (validator: hypothesis-only, must cite the owner evidence entry and its closure evidence; G1 evidence counts `promoted` separately from `open`). Open hypotheses: 0. The 2026-09-15 correction above is superseded on this point. No horizon, milestone, goal, or gate status moved. + **Update 2026-09-17 (advisor note internalized):** the 2026-08-31 external-advisor hypothesis was cited from three committed docs by a path under `.omx/artifacts/` that is excluded from git (`.git/info/exclude`) — evidence readable on one machine only. Internalized verbatim, with the prompt snapshot and an agent-prepared disposition table (extension taken bound to the booked session; revert not triggered; EFX live proof, ADR-0031 sign-off, DR-006 owner falsifier note, B1 owner session and the Horizon C fork all still open), at `reviews/external-advisor-roadmap-hypothesis-2026-08-31.md`; `backlog-priority.md`, this file's H4 row and `plans/current-roadmap-execution-plan-2026-08-26.md` §6.3 now cite it. No horizon or milestone status moved. diff --git a/docs/work-log/operator-friction-log.md b/docs/work-log/operator-friction-log.md index da01a2de..9b2e4cc9 100644 --- a/docs/work-log/operator-friction-log.md +++ b/docs/work-log/operator-friction-log.md @@ -20,12 +20,17 @@ UX work tied to real harness use instead of competitor feature parity. mechanisms verified in the entries); `rejected` is the log's state for a hypothesis the owner did not confirm as real friction, and none of the four became owner evidence. -- Open hypothesis entries still `Status: open` after 2026-10-09: 3 (`agent run JSON - errors without hints`, competitor-derived `Governance vocabulary on daily path`, - competitor-derived `Receipts need plain-language first line`). Their closure - evidence cites owner evidence F7/F2/F3; the owner did not list them for closure - on 2026-10-09, so they stay open until the owner confirms. -- Owner-validated/promoted hypotheses retained for provenance: 3 (F2/F3/F7). +- Open hypothesis entries: 0. +- Promoted hypotheses retained for provenance: 3 (`agent run JSON errors without + hints` → F7, `Governance vocabulary on daily path` → F2, `Receipts need + plain-language first line` → F3). The owner confirmed all three in the + 2026-06-22 capture batch; until 2026-10-10 the log had no status for "confirmed + and promoted" (only open/closed/rejected, and a hypothesis may not close), so + they sat at `open` with an explanatory note. Owner decision 2026-10-10: add + `promoted` as the terminal state for confirmed hypotheses; the three carry it + now. Their closure mechanisms still hold on the current build + (`tests/test_friction_ux_fixes.py::test_soften_operator_copy`, + `::test_wants_human_cli_tty_default`, `tests/test_cli_run_error_formatting.py`). - Governance-process hypothesis resolved by DR-006: 1. - Direction implication: none of the retained hypotheses is open roadmap demand; new UX work still requires a cited owner-evidence entry or governance gap. @@ -38,6 +43,8 @@ UX work tied to real harness use instead of competitor feature parity. a B1 session (date to be booked), and recorded the DR-006 falsifier review. No new friction entry was written; absence of friction remains dormancy evidence, not ergonomics evidence. +- Owner decision (2026-10-10): the three hypotheses promoted to F2/F3/F7 move + from `open` to the new `promoted` status; no new friction was reported. Use this log when: @@ -58,6 +65,10 @@ Use this log when: is allowed only for open or rejected entries. - A promoted entry must cite the ticket or acceptance-gap artifact in `Promoted to`; `n/a` is allowed only before promotion or for rejected entries. +- `promoted` is the terminal state of a hypothesis the owner confirmed as real + friction: it points at the owner evidence entry it became (`Promoted to`) and + carries that entry's closure evidence. Evidence entries close; they are never + `promoted`. A hypothesis the owner did not confirm is `rejected`. - Closing promoted work requires both links: the downstream artifact in `Promoted to` and the commit/test/doc in `Closure evidence`. - Do not use this log for public positioning, competitor ranking, or feature @@ -76,7 +87,7 @@ fields only when the entry is promoted to work. - **Type:** evidence | hypothesis - **Source:** owner real use | [hypothesis: source, date] - **One-line capture:** I tried __; expected __; got __. -- **Status:** open | closed | rejected +- **Status:** open | closed | rejected | promoted ``` ## Socratic Intake Prompts @@ -105,7 +116,7 @@ fields only when the entry is promoted to work. - **Expected:** What should have happened. - **Actual:** What happened instead. - **Harness impact:** approval | audit | rollback | cost | receipt | state | validation | ergonomics -- **Status:** open | closed | rejected +- **Status:** open | closed | rejected | promoted - **Closure evidence:** commit/test/doc link; `n/a` only while open or rejected - **Promoted to:** ticket/acceptance-gap link; `n/a` only before promotion or when rejected ``` @@ -217,7 +228,7 @@ fields only when the entry is promoted to work. - **Type:** hypothesis - **Source:** [hypothesis: retrospective 04-ux-usability.md, 2026-06-22] - **One-line capture:** `agent run` task/plan/background failures may bypass `format_error_block` hints. -- **Status:** open +- **Status:** promoted - **Closure evidence:** Owner evidence F7 (2026-06-22) - **Promoted to:** [Owner evidence: agent run errors without hints](#2026-06-22---agent-run-errors-without-hints-f7) ### 2026-06-22 - Run id passed as task [hypothesis] @@ -237,7 +248,7 @@ fields only when the entry is promoted to work. - **Type:** hypothesis - **Source:** [hypothesis: harness-first direction §5.2 + intent debate, 2026-06-22] - **One-line capture:** Daily ask/approve/undo path may still surface tenant/envelope/cockpit nouns instead of plain-language receipts. -- **Status:** open +- **Status:** promoted - **Closure evidence:** Owner evidence F2 (2026-06-22) - **Promoted to:** [Owner evidence: Governance vocabulary on daily path (F2)](#2026-06-22---governance-vocabulary-on-daily-path-f2) @@ -246,7 +257,7 @@ fields only when the entry is promoted to work. - **Type:** hypothesis - **Source:** [hypothesis: harness-first direction §5.2, 2026-06-22] - **One-line capture:** Run receipts may bury the actionable answer behind JSON unless `--json` is explicit. -- **Status:** open +- **Status:** promoted - **Closure evidence:** Owner evidence F3 (2026-06-22) - **Promoted to:** [Owner evidence: JSON before plain-language receipt (F3)](#2026-06-22---json-before-plain-language-receipt-f3) ### 2026-06-22 - Config source confusion ("why is it read-only") diff --git a/knowledge/teaagent-current/concepts/roadmap-status.md b/knowledge/teaagent-current/concepts/roadmap-status.md index 34dc5c89..0c46621e 100644 --- a/knowledge/teaagent-current/concepts/roadmap-status.md +++ b/knowledge/teaagent-current/concepts/roadmap-status.md @@ -6,7 +6,7 @@ resource: "urn:teaagent:doc:docs/roadmap-status.md" tags: ["teaagent", "roadmap", "status", "constitution"] teaagent: source_path: "docs/roadmap-status.md" - source_sha256: "14e3ef82f65199dd805865a7b92fb133b08e9c9043e023016f4ceb7659594d9b" + source_sha256: "58d4a8f0c1a1fa4f4a6af3c40df9964ca894c4537718dfa0336047264bce5a3b" docs_tier: "constitution" authority: "canonical" lifecycle: "current" diff --git a/knowledge/teaagent-current/concepts/work-log-operator-friction-log.md b/knowledge/teaagent-current/concepts/work-log-operator-friction-log.md index e34dc830..bf4f9458 100644 --- a/knowledge/teaagent-current/concepts/work-log-operator-friction-log.md +++ b/knowledge/teaagent-current/concepts/work-log-operator-friction-log.md @@ -6,7 +6,7 @@ resource: "urn:teaagent:doc:docs/work-log/operator-friction-log.md" tags: ["teaagent", "friction", "intake", "evidence"] teaagent: source_path: "docs/work-log/operator-friction-log.md" - source_sha256: "2d581bc22167213f1668640abcf76dd3275bfbd8c26fafedf5fa28c88d09e76b" + source_sha256: "2afb4f1612d6eb0c2a2b4699af7b076efcb9460f328672b97bf4764719c68266" docs_tier: "working" authority: "canonical" lifecycle: "current" diff --git a/scripts/prepare_g1_evidence.py b/scripts/prepare_g1_evidence.py index 16404ddf..9380953d 100755 --- a/scripts/prepare_g1_evidence.py +++ b/scripts/prepare_g1_evidence.py @@ -197,7 +197,12 @@ def _in_window(moment: Optional[datetime]) -> bool: else: unknown += 1 - friction_counts: dict[str, int] = {'open': 0, 'closed': 0, 'rejected': 0} + friction_counts: dict[str, int] = { + 'open': 0, + 'closed': 0, + 'rejected': 0, + 'promoted': 0, + } for entry in friction_entries: entry_dt = _parse_bound(entry.get('date')) if not _in_window(entry_dt): diff --git a/scripts/validate_docs_consistency.py b/scripts/validate_docs_consistency.py index a61065d5..2907c86b 100644 --- a/scripts/validate_docs_consistency.py +++ b/scripts/validate_docs_consistency.py @@ -1190,10 +1190,11 @@ def validate_operator_friction_log(friction_log_text: str) -> list[str]: ) status = _normalize_friction_field(fields.get('Status', '')) - if status not in {'open', 'closed', 'rejected'}: + if status not in {'open', 'closed', 'rejected', 'promoted'}: errors.append( f'operator-friction-log.md entry {title!r} at line {line_no} ' - "has invalid Status; expected 'open', 'closed', or 'rejected'." + "has invalid Status; expected 'open', 'closed', 'rejected', " + "or 'promoted'." ) continue @@ -1204,6 +1205,30 @@ def validate_operator_friction_log(friction_log_text: str) -> list[str]: 'after owner validation.' ) + if status == 'promoted': + # A hypothesis the owner confirmed as real friction: it lives on as + # an owner evidence entry, so it must point at that entry and at the + # closure evidence the entry carries. Evidence entries are never + # "promoted"; they close. + if entry_type != 'hypothesis': + errors.append( + f'operator-friction-log.md entry {title!r} at line {line_no} ' + 'may not use Status: promoted; only hypothesis entries are ' + 'promoted (evidence entries close).' + ) + if _is_empty_friction_link(fields.get('Promoted to')): + errors.append( + f'operator-friction-log.md promoted entry {title!r} at line ' + f'{line_no} must cite the owner evidence entry it became in ' + 'Promoted to.' + ) + if _is_empty_friction_link(fields.get('Closure evidence')): + errors.append( + f'operator-friction-log.md promoted entry {title!r} at line ' + f'{line_no} must cite non-n/a Closure evidence.' + ) + continue + if status != 'closed': continue diff --git a/tests/test_docs_consistency.py b/tests/test_docs_consistency.py index 3598b95a..f4e8dcb7 100644 --- a/tests/test_docs_consistency.py +++ b/tests/test_docs_consistency.py @@ -260,6 +260,52 @@ def test_validate_operator_friction_log_allows_open_unpromoted_entry() -> None: assert errors == [] +def test_validate_operator_friction_log_allows_promoted_hypothesis() -> None: + log = ( + '# Operator Friction Log\n\n' + '## Competitor-Derived Hypotheses\n\n' + '### 2026-06-14 - Competitor clue\n\n' + '- **Type:** hypothesis\n' + '- **Source:** [hypothesis: example, 2026-06-14]\n' + '- **Status:** promoted\n' + '- **Closure evidence:** Owner evidence F9 (2026-06-14)\n' + '- **Promoted to:** [Owner evidence: Clue (F9)](#2026-06-14---clue-f9)\n' + ) + errors = _VALIDATE_MODULE.validate_operator_friction_log(log) + assert errors == [] + + +def test_validate_operator_friction_log_promoted_requires_both_links() -> None: + log = ( + '# Operator Friction Log\n\n' + '## Competitor-Derived Hypotheses\n\n' + '### 2026-06-14 - Competitor clue\n\n' + '- **Type:** hypothesis\n' + '- **Source:** [hypothesis: example, 2026-06-14]\n' + '- **Status:** promoted\n' + '- **Closure evidence:** n/a\n' + '- **Promoted to:** n/a\n' + ) + errors = _VALIDATE_MODULE.validate_operator_friction_log(log) + assert any('must cite the owner evidence entry' in err for err in errors) + assert any('must cite non-n/a Closure evidence' in err for err in errors) + + +def test_validate_operator_friction_log_rejects_promoted_evidence_entry() -> None: + log = ( + '# Operator Friction Log\n\n' + '## Owner Evidence Entries\n\n' + '### 2026-06-14 - Real friction\n\n' + '- **Type:** evidence\n' + '- **Source:** owner real use\n' + '- **Status:** promoted\n' + '- **Closure evidence:** tests/test_example.py\n' + '- **Promoted to:** docs/work-log/example-ticket.md\n' + ) + errors = _VALIDATE_MODULE.validate_operator_friction_log(log) + assert any('only hypothesis entries are promoted' in err for err in errors) + + def test_validate_operator_friction_log_detects_malformed_heading() -> None: log = ( '# Operator Friction Log\n\n' diff --git a/tests/test_prepare_g1_evidence.py b/tests/test_prepare_g1_evidence.py index 67f4fb8e..d0e95e41 100644 --- a/tests/test_prepare_g1_evidence.py +++ b/tests/test_prepare_g1_evidence.py @@ -90,6 +90,24 @@ def test_organic_run_without_open_friction_is_frictionless(tmp_path): assert report.doc_lookup_instrumented is False +def test_promoted_hypothesis_is_not_open_friction(tmp_path): + # A hypothesis the owner confirmed and promoted to an evidence entry is + # accounted for by that entry; it must not keep the verdict at + # friction_observed on its own. + log = _friction_log( + tmp_path, + '### 2026-09-10 - Promoted clue\n- **Status:** promoted\n' + '### 2026-09-11 - Its evidence entry\n- **Status:** closed\n', + ) + entries = load_friction_entries(log) + report = build_g1_evidence_report( + [_run('Refactor the auth module', origin='owner')], entries + ) + assert report.friction_entries_in_window['promoted'] == 1 + assert report.friction_entries_in_window['open'] == 0 + assert report.verdict == 'frictionless' + + def test_window_bounds_exclude_out_of_range_runs(): runs = [ _run('Organic task A', created_at='2026-09-01T00:00:00+00:00', origin='owner'),