Skip to content

refactor!: retire policy.json fallback - #405

Merged
dean0x merged 8 commits into
mainfrom
refactor/394-retire-policy-json-fallback
Sep 29, 2026
Merged

dean0x merged 8 commits into
mainfrom
refactor/394-retire-policy-json-fallback

Conversation

@dean0x

@dean0x dean0x commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

PR7 of epic #387 (plan .devflow/docs/design/per-repo-config-layer-master.2026-09-27.md, "PR7" section). .devflow/policy.json is no longer parsed: at every source (default branch, tracking, HEAD, worktree) where project.json has no evidence key, an existing policy.json resolves required with invalid-file, whatever it says; neither file present is unchanged. parsePolicyBytes, POLICY_GRAMMAR_RE, serializePolicy and their seam keys are deleted, and a guard over src/ keeps them out. Presence reuses each source's existing call, so resolver argv is unchanged and the output line format is unchanged.

Changes

  • Delete parsePolicyBytes, POLICY_GRAMMAR_RE, serializePolicy and their seam keys from src/assets/scripts/resolve-evidence-policy.cjs / src/core/evidence-policy.ts; add a source guard over src/ to keep them out.
  • Where project.json has no evidence key and policy.json is present at that source, resolve required with WARN=invalid-file — the file's contents are never parsed.
  • compliance --status hint no longer implies the file is read; it prints the project.json equivalents instead.
  • /implement's stop remedies now tell teams to commit .devflow/project.json {"version":1,"evidence":"standard"} rather than editing policy.json.
  • .gitignore carve-out and uninstall keep-list for policy.json are unchanged.
  • D-POLICY-JSON-RETIRED added; D-POLICY-SOURCE-PRECEDENCE rewritten.
  • CHANGELOG documents the migration {"version":1,"evidencePolicy":"standard"} → .devflow/project.json {"version":1,"evidence":"standard"}, alongside feat: committed .devflow/project.json + resolver --settings, narrow-only switches, publication ceiling #392 (project.json), since the release was held and both ship together.
  • Dogfood: this repo moves to .devflow/project.json {"version":1,"evidence":"required"}, deletes policy.json, and commits the v6 !.devflow/project.json gitignore line.

Breaking Changes

refactor!: any repo still relying on .devflow/policy.json content (without a project.json evidence key) now resolves required regardless of what the file said, instead of honoring standard. On this branch the resolver prints required SOURCE=invalid WARN=invalid-file,pr-changes-policy because main still has only policy.json; after merge it resolves required SOURCE=file with no warning.

Acceptance Criteria

  • AC-39: a policy.json saying standard with no project.json evidence key resolves required.
  • AC-40: a migrated project.json resolves exactly as it did in PR5.

Pins Changed

File Row Change
resolver.test.ts parsePolicyBytes (7 valid, 24 invalid, absent) + serializePolicy (2) describes removed
resolver.test.ts fold matrix (40), named rows (14), SOURCES/WARNINGS exhaustiveness, pr-changes-policy (8), per-call bounds, fail-closed, exit arms fixtures moved to project.json evidence; every expected line unchanged
resolver.test.ts tracking/HEAD blob timeout rows + offline no-tracking-ref target moved to trackingProjectBlob/headProjectBlob
resolver.test.ts argv "reachable + present" log unchanged; line required SOURCE=file → required SOURCE=invalid WARN=invalid-file
resolver.test.ts argv "probe fails ⇒ ls-remote…" log unchanged; line standard SOURCE=worktree WARN=remote-unavailable → required SOURCE=invalid WARN=remote-unavailable,invalid-file
resolver.test.ts argv "contents 403…", injection hostile remote/worktree, invalid-bytes rows, bounds rows, real-git rows re-encoded on project.json (64 KiB remote row stays on policy.json as presence); __proto__ row → duplicated evidence key
resolver.test.ts source-guard markers D-POLICY-STRICT-SCHEMA → D-POLICY-JSON-RETIRED
project-json.test.ts "…falls back to the SAME source's policy.json" SOURCE=file → SOURCE=invalid WARN=invalid-file
project-json.test.ts 404/403 row; byte-rules row worktree fixture → project.json; parsePolicyBytes assertion removed, renamed
cli-seam.test.ts surface-key count ≥9; unusable-detail; migration hint unit + e2e rows; --status online/offline exact 8-key list; /complianceDefault/; new 5-line hint; project.json fixtures (online log 3 calls → 2)
ticket-gate.test.ts / implement-flow.test.ts remedy rows assert project.json literal
scripted-shim.ts scenarioCalls policy.json defaults to absent/404; argv tables unchanged

Testing

Coverage moves onto project.json fixtures across the resolver, project-json, cli-seam, ticket-gate, implement-flow and scripted-shim suites per the pins table above. CI is the verification method for both ACs; no manual steps.

Related Issues

Closes #394

Test Plan

  • TP-45 (AC-39) A policy.json that used to resolve standard and one with invalid bytes both resolve required without parsing — method:ci [files: tests/evidence-policy/resolver.test.ts]
  • TP-46 (AC-40) project.json resolution rows unchanged and no production caller of the legacy policy parser remains — method:ci [files: tests/evidence-policy/*.test.ts, src/assets/scripts/resolve-evidence-policy.cjs]

dean0x and others added 8 commits September 30, 2026 00:17
.devflow/policy.json is no longer parsed. At any source (default
branch, tracking copy, HEAD, worktree) whose project.json has no
`evidence`, an existing policy.json — whatever its bytes — makes that
source invalid, so the repository resolves `required` with the
`invalid-file` warning until its value moves to project.json
`evidence`. Presence is probed with the call each source already
makes (contents GET, cat-file blob, worktree lstat), so the argv
sequence is unchanged; the worktree file is never opened.

- delete parsePolicyBytes, POLICY_GRAMMAR_RE and serializePolicy and
  the serializePolicy seam key (D-POLICY-JSON-RETIRED; rewrites
  D-POLICY-SOURCE-PRECEDENCE to the end state)
- compliance --status migration hint states the rule and prints the
  project.json line per value from serializeProjectSuggestion
- /implement's standard-policy remedy names project.json `evidence`
- tests: TP-45 (AC-39) and TP-46 (AC-40, incl. a src/ guard with a red
  probe); fold fixtures restated in project.json

BREAKING CHANGE: a repository with only .devflow/policy.json resolves
`required` (WARN=invalid-file). Migrate
{"version":1,"evidencePolicy":"standard"} to
{"version":1,"evidence":"standard"} in .devflow/project.json.

Refs #394
State the end state of the policy.json retirement: `evidence` in
.devflow/project.json is the only evidence-policy authority, and a
committed .devflow/policy.json is never parsed — where project.json has
no `evidence` its presence alone holds the repository at `required`.
CLAUDE.md (Runtime data, Evidence policy), README, cli-reference,
release-process, file-organization, the compliance-feature and
installer-shadowing knowledge bases, and CHANGELOG [Unreleased]: a
BREAKING entry with the exact migration, and the #392 entries rewritten
so the one release reads consistently.

Refs #394
Dogfood the policy.json retirement: commit .devflow/project.json with
{"version":1,"evidence":"required"} and remove .devflow/policy.json.
The root .gitignore gains the v6 carve-out line !.devflow/project.json,
byte-identical to what ensure-root-gitignore appends, so the file is
tracked without `git add -f`.

Refs #394
The byte-budget note cited .devflow/policy.json as this repository's
required evidence policy; it now lives in .devflow/project.json.

Refs #394
Pin the working-tree policy.json presence probe's edge arms (dangling
symlink and directory present, ENOTDIR absent, EACCES present), and
correct stale comments: resolve() makes at most three gh calls, the
tracked-paths list names the retired policy file, the blob buffer note
names project.json.

Co-Authored-By: Claude <noreply@anthropic.com>
Matches the PR's vocabulary for the retired .devflow/policy.json
(RETIRED_POLICY_FILE, retiredPolicyHint). The field is CLI-only and
never reaches the settings line.
The resolver's MAX_POLICY_BYTES was an alias of the shared parser's
bound for the project.json read; use projectConfig.MAX_CONFIG_BYTES
directly, as resolve-settings.cjs does, and drop the alias export.
project-json.test.ts pins the 4096 bound; the resolver's oversize
tests keep the behaviour covered.
post-install DEVFLOW_POLICY_LINE JSDoc names D-POLICY-JSON-RETIRED
(emitted gitignore bytes unchanged); release-process lists a committed
retired policy.json, where project.json has no evidence key, among the
sources that make the policy required.
@dean0x

dean0x commented Sep 29, 2026

Copy link
Copy Markdown
Owner Author

Test Plan Evidence — 9fc088f

Verified 2/2: VERIFIED-CI 2, ATTESTED-LOCAL 0, UNVERIFIED 0, STALE 0, FAILED 0, INDETERMINATE 0

  • TP-45 VERIFIED-CI sha:9fc088f46c7e3d5b17aa2a6f5f77f96118513f8b out:PASS run:36634955886/1 exit:0 h:ababce7dfe81
  • TP-46 VERIFIED-CI sha:9fc088f46c7e3d5b17aa2a6f5f77f96118513f8b out:PASS run:36634955886/1 exit:0 h:f0f696dad345

@dean0x
dean0x merged commit 12d1b52 into main Sep 29, 2026
3 checks passed
@dean0x
dean0x deleted the refactor/394-retire-policy-json-fallback branch September 29, 2026 21:58
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.

refactor!: retire policy.json fallback

1 participant